Blame doc/src/fpga_api/plug_guide/readme.md

Packit 534379
# Plugin Developer's Guide #
Packit 534379
Packit 534379
```eval_rst
Packit 534379
.. toctree::
Packit 534379
```
Packit 534379
Packit 534379
## Overview ##
Packit 534379
Packit 534379
Beginning with OPAE C library version 1.2.0, OPAE implements a plugin-centric
Packit 534379
model. This guide serves as a reference to define the makeup of an OPAE C API
Packit 534379
plugin and to describe a sequence of steps that one may follow when constructing
Packit 534379
an OPAE C API plugin.
Packit 534379
Packit 534379
## Plugin Required Functions ##
Packit 534379
Packit 534379
An OPAE C API plugin is a runtime-loadable shared object library, also known as
Packit 534379
a module. On Linux systems, the *dl* family of APIs from libdl are used to
Packit 534379
interact with shared objects. Refer to "man dlopen" and "man dlsym" for examples
Packit 534379
of using the libdl API.
Packit 534379
Packit 534379
An OPAE C API plugin implements one required function. This function is required
Packit 534379
to have C linkage, so that its name is not mangled.
Packit 534379
Packit 534379
```c
Packit 534379
    int opae_plugin_configure(opae_api_adapter_table *table, const char *config);
Packit 534379
```
Packit 534379
Packit 534379
During initialization, the OPAE plugin manager component loads each plugin,
Packit 534379
searching for its `opae_plugin_configure` function. If none is found, then
Packit 534379
the plugin manager rejects that plugin. When it is found, `opae_plugin_configure`
Packit 534379
is called passing a pointer to a freshly-created `opae_api_adapter_table` and
Packit 534379
a buffer consisting of configuration data for the plugin.
Packit 534379
Packit 534379
The job of the `opae_plugin_configure` function is to populate the given adapter
Packit 534379
table with each of the plugin's API entry points and to consume and comprehend
Packit 534379
the given configuration data in preparation for initialization.
Packit 534379
Packit 534379
## OPAE API Adapter Table ##
Packit 534379
Packit 534379
The adapter table is a data structure that contains function pointer entry points
Packit 534379
for each of the OPAE APIs implemented by a plugin. In this way, it adapts the
Packit 534379
plugin-specific behavior to the more general case of a flat C API. Note that
Packit 534379
OPAE applications are only required to link with opae-c. In other words, the
Packit 534379
name of the plugin library should not appear on the linker command line. In this
Packit 534379
way, plugins are truly decoupled from the OPAE C API, and they are required to
Packit 534379
adapt to the strict API specification by populating the adapter table only. No
Packit 534379
other linkage is required nor recommended.
Packit 534379
Packit 534379
`adapter.h` contains the definition of the `opae_api_adapter_table`. An abbreviated
Packit 534379
version is depicted below, along with supporting type `opae_plugin`:
Packit 534379
Packit 534379
```c
Packit 534379
    typedef struct _opae_plugin {
Packit 534379
        char *path;
Packit 534379
        void *dl_handle;
Packit 534379
    } opae_plugin;
Packit 534379
Packit 534379
    typedef struct _opae_api_adapter_table {
Packit 534379
Packit 534379
        struct _opae_api_adapater_table *next;
Packit 534379
        opae_plugin plugin;
Packit 534379
Packit 534379
        fpga_result (*fpgaOpen)(fpga_token token, fpga_handle *handle,
Packit 534379
                                int flags);
Packit 534379
Packit 534379
        fpga_result (*fpgaClose)(fpga_handle handle);
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        fpga_result (*fpgaEnumerate)(const fpga_properties *filters,
Packit 534379
                                     uint32_t num_filters, fpga_token *tokens,
Packit 534379
                                     uint32_t max_tokens,
Packit 534379
                                     uint32_t *num_matches);
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        // configuration functions
Packit 534379
        int (*initialize)(void);
Packit 534379
        int (*finalize)(void);
Packit 534379
Packit 534379
        // first-level query
Packit 534379
        bool (*supports_device)(const char *device_type);
Packit 534379
        bool (*supports_host)(const char *hostname);
Packit 534379
Packit 534379
    } opae_api_adapter_table;
Packit 534379
```
Packit 534379
Packit 534379
Some points worth noting are that the adapter tables are organized in memory by
Packit 534379
adding them to a linked list data structure. This is the use of the `next`
Packit 534379
structure member. (The list management is handled by the plugin manager.)
Packit 534379
The `plugin` structure member contains the handle to the shared object instance,
Packit 534379
as created by `dlopen`. This handle is used in the plugin's `opae_plugin_configure`
Packit 534379
to load plugin entry points. A plugin need only implement the portion of the
Packit 534379
OPAE C API that a target application needs. Any API entry points that are not
Packit 534379
supported should be left as NULL pointers (the default) in the adapter table.
Packit 534379
When an OPAE API that has no associated entry point in the adapter table is
Packit 534379
called, the result for objects associated with that plugin will be
Packit 534379
`FPGA_NOT_SUPPORTED`.
Packit 534379
Packit 534379
The following code illustrates a portion of the `opae_plugin_configure` for
Packit 534379
a theoretical OPAE C API plugin libfoo.so:
Packit 534379
Packit 534379
```c
Packit 534379
    /* foo_plugin.c */
Packit 534379
Packit 534379
    int opae_plugin_configure(opae_api_adapter_table *table, const char *config)
Packit 534379
    {
Packit 534379
        adapter->fpgaOpen = dlsym(adapter->plugin.dl_handle, "foo_fpgaOpen");
Packit 534379
        adapter->fpgaClose =
Packit 534379
                dlsym(adapter->plugin.dl_handle, "foo_fpgaClose");
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        adapter->fpgaEnumerate =
Packit 534379
                dlsym(adapter->plugin.dl_handle, "foo_fpgaEnumerate");
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        return 0;
Packit 534379
    }
Packit 534379
```
Packit 534379
Packit 534379
Notice that the implementations of the API entry points for plugin libfoo.so
Packit 534379
are prefixed with `foo_`. This is the recommended practice to avoid name
Packit 534379
collisions and to enhance the debugability of the application. Upon successful
Packit 534379
configuration, `opae_plugin_configure` returns 0 to indicate success. A
Packit 534379
non-zero return value indicates failure and causes the plugin manager to
Packit 534379
reject the plugin from futher consideration.
Packit 534379
Packit 534379
## Plugin Optional Functions ##
Packit 534379
Packit 534379
Once the plugin manager loads and configures each plugin, it uses the adapter
Packit 534379
table to call back into the plugin so that it can be made ready for runtime.
Packit 534379
This is the job of the `opae_plugin_initialize` entry point, whose signature
Packit 534379
is defined as:
Packit 534379
Packit 534379
```c
Packit 534379
    int opae_plugin_initialize(void);
Packit 534379
```
Packit 534379
Packit 534379
The function takes no parameters, as the configuration data was already given
Packit 534379
to the plugin by `opae_plugin_configure`. `opae_plugin_initialize` returns 0
Packit 534379
if no errors were encountered during initialization. A non-zero return code
Packit 534379
indicates that plugin initialization failed. A plugin makes its
Packit 534379
`opae_plugin_initialize` available to the plugin manager by populating the
Packit 534379
adapter table's `initialize` entry point as shown:
Packit 534379
Packit 534379
```c
Packit 534379
    /* foo_plugin.c */
Packit 534379
Packit 534379
    int foo_plugin_initialize(void)
Packit 534379
    {
Packit 534379
        ...
Packit 534379
Packit 534379
        return 0; /* success */
Packit 534379
    }
Packit 534379
Packit 534379
    int opae_plugin_configure(opae_api_adapter_table *table, const char *config)
Packit 534379
    {
Packit 534379
        ... 
Packit 534379
Packit 534379
        adapter->initialize =
Packit 534379
                dlsym(adapter->plugin.dl_handle, "foo_plugin_initialize");
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        return 0;
Packit 534379
    }
Packit 534379
```
Packit 534379
Packit 534379
If a plugin does not implement an `opae_plugin_initialize` entry point, then
Packit 534379
the `initialize` member of the adapter table should be left uninitialized.
Packit 534379
During plugin initialization, if a plugin has no `opae_plugin_initialize`
Packit 534379
entry in its adapter table, the plugin initialization step will be skipped,
Packit 534379
and the plugin will be considered to have initialized successfully.
Packit 534379
Packit 534379
Once plugin initialization is complete for all loaded plugins, the system
Packit 534379
is considered to be running and fully functional.
Packit 534379
Packit 534379
During teardown, the plugin manager uses the adapter table to call into each
Packit 534379
plugin's `opae_plugin_finalize` entry point, whose signature is defined as:
Packit 534379
Packit 534379
```c
Packit 534379
    int opae_plugin_finalize(void);
Packit 534379
```
Packit 534379
Packit 534379
`opae_plugin_finalize` returns 0 if no errors were encountered during teardown.
Packit 534379
A non-zero return code indicates that plugin teardown failed. A plugin makes
Packit 534379
its `opae_plugin_finalize` available to the plugin manager by populating the
Packit 534379
adapter table's `finalize` entry point as shown:
Packit 534379
Packit 534379
```c
Packit 534379
    /* foo_plugin.c */
Packit 534379
Packit 534379
    int foo_plugin_finalize(void)
Packit 534379
    {
Packit 534379
        ...
Packit 534379
Packit 534379
        return 0; /* success */
Packit 534379
    }
Packit 534379
Packit 534379
    int opae_plugin_configure(opae_api_adapter_table *table, const char *config)
Packit 534379
    {
Packit 534379
        ... 
Packit 534379
Packit 534379
        adapter->finalize =
Packit 534379
                dlsym(adapter->plugin.dl_handle, "foo_plugin_finalize");
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        return 0;
Packit 534379
    }
Packit 534379
```
Packit 534379
Packit 534379
If a plugin does not implement an `opae_plugin_finalize` entry point, then
Packit 534379
the `finalize` member of the adapter table should be left uninitialized.
Packit 534379
During plugin cleanup, if a plugin has no `opae_plugin_finalize` entry
Packit 534379
point in its adapter table, the plugin finalize step will be skipped, and
Packit 534379
the plugin will be considered to have finalized successfully.
Packit 534379
Packit 534379
In addition to `initialize` and `finalize`, an OPAE C API plugin has two
Packit 534379
further optional entry points that relate to device enumeration. During
Packit 534379
enumeration, when a plugin is being considered for a type of device, the
Packit 534379
plugin may provide input on that decision by exporting an
Packit 534379
`opae_plugin_supports_device` entry point in the adapter table:
Packit 534379
Packit 534379
```c
Packit 534379
    bool opae_plugin_supports_device(const char *device_type);
Packit 534379
```
Packit 534379
Packit 534379
`opae_plugin_supports_device` returns true if the given device type is
Packit 534379
supported and false if it is not. A false return value from
Packit 534379
`opae_plugin_supports_device` causes device enumeration to skip the
Packit 534379
plugin.
Packit 534379
Packit 534379
Populating the `opae_plugin_supports_device` is done as:
Packit 534379
Packit 534379
```c
Packit 534379
    /* foo_plugin.c */
Packit 534379
Packit 534379
    bool foo_plugin_supports_device(const char *device_type)
Packit 534379
    {
Packit 534379
        if (/* device_type is supported */)
Packit 534379
            return true;
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        return false;
Packit 534379
    }
Packit 534379
Packit 534379
    int opae_plugin_configure(opae_api_adapter_table *table, const char *config)
Packit 534379
    {
Packit 534379
        ... 
Packit 534379
Packit 534379
        adapter->supports_device =
Packit 534379
                dlsym(adapter->plugin.dl_handle, "foo_plugin_supports_device");
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        return 0;
Packit 534379
    }
Packit 534379
```
Packit 534379
Packit 534379
```eval_rst
Packit 534379
.. note::
Packit 534379
    The `opae_plugin_supports_device` mechanism serves as a placeholder only.
Packit 534379
    It is not implemented in the current version of the OPAE C API.
Packit 534379
```
Packit 534379
Packit 534379
Similarly to determining whether a plugin supports a type of device, a plugin
Packit 534379
may also answer questions about network host support by populating an
Packit 534379
`opae_plugin_supports_host` entry point in the adapter table:
Packit 534379
Packit 534379
```c
Packit 534379
    bool opae_plugin_supports_host(const char *hostname);
Packit 534379
```
Packit 534379
Packit 534379
`opae_plugin_supports_host` returns true if the given hostname is supported
Packit 534379
and false if it is not. A false return value from `opae_plugin_supports_host`
Packit 534379
causes device enumeration to skip the plugin.
Packit 534379
Packit 534379
Populating the `opae_plugin_supports_host` is done as:
Packit 534379
Packit 534379
```c
Packit 534379
    /* foo_plugin.c */
Packit 534379
Packit 534379
    bool foo_plugin_supports_host(const char *hostname)
Packit 534379
    {
Packit 534379
        if (/* hostname is supported */)
Packit 534379
            return true;
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        return false;
Packit 534379
    }
Packit 534379
Packit 534379
    int opae_plugin_configure(opae_api_adapter_table *table, const char *config)
Packit 534379
    {
Packit 534379
        ... 
Packit 534379
Packit 534379
        adapter->supports_host =
Packit 534379
                dlsym(adapter->plugin.dl_handle, "foo_plugin_supports_host");
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        return 0;
Packit 534379
    }
Packit 534379
```
Packit 534379
Packit 534379
```eval_rst
Packit 534379
.. note::
Packit 534379
    The `opae_plugin_supports_host` mechanism serves as a placeholder only.
Packit 534379
    It is not implemented in the current version of the OPAE C API.
Packit 534379
```
Packit 534379
Packit 534379
## Plugin Construction ##
Packit 534379
Packit 534379
The steps required to implement an OPAE C API plugin, libfoo.so, are:
Packit 534379
Packit 534379
* Create foo\_plugin.c: implements `opae_plugin_configure`,
Packit 534379
`opae_plugin_initialize`, `opae_plugin_finalize`, `opae_plugin_supports_device`,
Packit 534379
and `opae_plugin_supports_host` as described in the previous sections.
Packit 534379
* Create foo\_plugin.h: implements function prototypes for each of the
Packit 534379
plugin-specific OPAE C APIs.
Packit 534379
Packit 534379
```c
Packit 534379
    /* foo_plugin.h */
Packit 534379
Packit 534379
    fpga_result foo_fpgaOpen(fpga_token token, fpga_handle *handle,
Packit 534379
                             int flags);
Packit 534379
Packit 534379
    fpga_result foo_fpgaClose(fpga_handle handle);
Packit 534379
Packit 534379
    ...
Packit 534379
Packit 534379
    fpga_result foo_fpgaEnumerate(const fpga_properties *filters,
Packit 534379
                                  uint32_t num_filters, fpga_token *tokens,
Packit 534379
                                  uint32_t max_tokens,
Packit 534379
                                  uint32_t *num_matches);
Packit 534379
    ...
Packit 534379
```
Packit 534379
Packit 534379
* Create foo\_types.h: implements plugin-specific types for opaque data
Packit 534379
structures.
Packit 534379
Packit 534379
```c
Packit 534379
    /* foo_types.h */
Packit 534379
Packit 534379
    struct _foo_token {
Packit 534379
        ...
Packit 534379
    };
Packit 534379
Packit 534379
    struct _foo_handle {
Packit 534379
        ...
Packit 534379
    };
Packit 534379
Packit 534379
    struct _foo_event_handle {
Packit 534379
        ...
Packit 534379
    };
Packit 534379
Packit 534379
    struct _foo_object {
Packit 534379
        ...
Packit 534379
    };
Packit 534379
```
Packit 534379
Packit 534379
* Create foo\_enum.c: implements `foo_fpgaEnumerate`,
Packit 534379
`foo_fpgaCloneToken`, and `foo_fpgaDestroyToken`.
Packit 534379
* Create foo\_open.c: implements `foo_fpgaOpen`.
Packit 534379
* Create foo\_close.c: implements `foo_fpgaClose`.
Packit 534379
* Create foo\_props.c: implements `foo_fpgaGetProperties`,
Packit 534379
`foo_fpgaGetPropertiesFromHandle`, `foo_fpgaUpdateProperties`
Packit 534379
* Create foo\_mmio.c: implements `foo_fpgaMapMMIO`, `foo_fpgaUnmapMMIO`
Packit 534379
`foo_fpgaWriteMMIO64`, `foo_fpgaReadMMIO64`, `foo_fpgaWriteMMIO32`,
Packit 534379
`foo_fpgaReadMMIO32`.
Packit 534379
* Create foo\_buff.c: implements `foo_fpgaPrepareBuffer`,
Packit 534379
`foo_fpgaReleaseBuffer`, `foo_fpgaGetIOAddress`.
Packit 534379
* Create foo\_error.c: implements `foo_fpgaReadError`, `foo_fpgaClearError`,
Packit 534379
`foo_fpgaClearAllErrors`, `foo_fpgaGetErrorInfo`.
Packit 534379
* Create foo\_event.c: implements `foo_fpgaCreateEventHandle`,
Packit 534379
`foo_fpgaDestroyEventHandle`, `foo_fpgaGetOSObjectFromEventHandle`,
Packit 534379
`foo_fpgaRegisterEvent`, `foo_fpgaUnregisterEvent`.
Packit 534379
* Create foo\_reconf.c: implements `foo_fpgaReconfigureSlot`.
Packit 534379
* Create foo\_obj.c: implements `foo_fpgaTokenGetObject`,
Packit 534379
`foo_fpgaHandleGetObject`, `foo_fpgaObjectGetObject`,
Packit 534379
`foo_fpgaDestroyObject`, `foo_fpgaObjectGetSize`, `foo_fpgaObjectRead`,
Packit 534379
`foo_fpgaObjectRead64`, `foo_fpgaObjectWrite64`.
Packit 534379
* Create foo\_clk.c: implements `foo_fpgaSetUserClock`,
Packit 534379
`foo_fpgaGetUserClock`.