diff options
-rw-r--r-- | pcilib/CMakeLists.txt | 4 | ||||
-rw-r--r-- | pcilib/bank.c | 35 | ||||
-rw-r--r-- | pcilib/bank.h | 54 | ||||
-rw-r--r-- | pcilib/error.h | 1 | ||||
-rw-r--r-- | pcilib/pci.c | 8 | ||||
-rw-r--r-- | pcilib/pci.h | 10 | ||||
-rw-r--r-- | pcilib/pcilib.h | 65 | ||||
-rw-r--r-- | pcilib/property.c | 207 | ||||
-rw-r--r-- | pcilib/property.h | 30 | ||||
-rw-r--r-- | pcilib/register.c | 67 | ||||
-rw-r--r-- | pcilib/register.h | 34 | ||||
-rw-r--r-- | pcilib/unit.c | 41 | ||||
-rw-r--r-- | pcilib/unit.h | 39 | ||||
-rw-r--r-- | pcilib/value.c | 1 | ||||
-rw-r--r-- | pcilib/view.c | 100 | ||||
-rw-r--r-- | pcilib/view.h | 36 | ||||
-rw-r--r-- | pcilib/xml.c | 7 | ||||
-rw-r--r-- | pcitool/cli.c | 210 | ||||
-rw-r--r-- | views/CMakeLists.txt | 4 | ||||
-rw-r--r-- | views/enum.c | 2 | ||||
-rw-r--r-- | views/register.c | 65 | ||||
-rw-r--r-- | views/register.h | 19 |
22 files changed, 871 insertions, 168 deletions
diff --git a/pcilib/CMakeLists.txt b/pcilib/CMakeLists.txt index 3fc789e..5d84ddc 100644 --- a/pcilib/CMakeLists.txt +++ b/pcilib/CMakeLists.txt @@ -8,8 +8,8 @@ include_directories( ${UTHASH_INCLUDE_DIRS} ) -set(HEADERS pcilib.h pci.h export.h value.h bar.h fifo.h model.h bank.h register.h view.h unit.h xml.h py.h kmem.h irq.h locking.h lock.h dma.h event.h plugin.h tools.h error.h debug.h env.h version.h config.h) -add_library(pcilib SHARED pci.c export.c value.c bar.c fifo.c model.c bank.c register.c view.c unit.c xml.c py.c kmem.c irq.c locking.c lock.c dma.c event.c plugin.c tools.c error.c debug.c env.c ) +set(HEADERS pcilib.h pci.h export.h value.h bar.h fifo.h model.h bank.h register.h view.h property.h unit.h xml.h py.h kmem.h irq.h locking.h lock.h dma.h event.h plugin.h tools.h error.h debug.h env.h version.h config.h) +add_library(pcilib SHARED pci.c export.c value.c bar.c fifo.c model.c bank.c register.c view.c unit.c property.c xml.c py.c kmem.c irq.c locking.c lock.c dma.c event.c plugin.c tools.c error.c debug.c env.c ) target_link_libraries(pcilib dma protocols views ${CMAKE_THREAD_LIBS_INIT} ${UFODECODE_LIBRARIES} ${CMAKE_DL_LIBS} ${EXTRA_SYSTEM_LIBS} ${LIBXML2_LIBRARIES} ${PYTHON_LIBRARIES}) add_dependencies(pcilib dma protocols views) diff --git a/pcilib/bank.c b/pcilib/bank.c index 24ddf1f..ec38c82 100644 --- a/pcilib/bank.c +++ b/pcilib/bank.c @@ -19,6 +19,7 @@ int pcilib_init_register_banks(pcilib_t *ctx) { int err; + size_t start = ctx->num_banks_init; err = pcilib_map_register_space(ctx); if (err) return err; @@ -33,6 +34,7 @@ int pcilib_init_register_banks(pcilib_t *ctx) { const char *name = ctx->banks[ctx->num_banks_init].name; if (!name) name = "unnamed"; pcilib_error("Invalid register protocol address (%u) is specified for bank %i (%s)", ctx->banks[ctx->num_banks_init].protocol, ctx->banks[ctx->num_banks_init].addr, name); + pcilib_free_register_banks(ctx, start); return PCILIB_ERROR_INVALID_BANK; } @@ -46,8 +48,10 @@ int pcilib_init_register_banks(pcilib_t *ctx) { } else bank_ctx = (pcilib_register_bank_context_t*)malloc(sizeof(pcilib_register_bank_context_t)); - if (!bank_ctx) + if (!bank_ctx) { + pcilib_free_register_banks(ctx, start); return PCILIB_ERROR_FAILED; + } bank_ctx->bank = ctx->banks + ctx->num_banks_init; bank_ctx->api = bapi; @@ -58,10 +62,10 @@ int pcilib_init_register_banks(pcilib_t *ctx) { return 0; } -void pcilib_free_register_banks(pcilib_t *ctx) { +void pcilib_free_register_banks(pcilib_t *ctx, pcilib_register_bank_t start) { size_t i; - for (i = 0; i < ctx->num_banks_init; i++) { + for (i = start; i < ctx->num_banks_init; i++) { const pcilib_register_protocol_api_description_t *bapi = ctx->bank_ctx[i]->api; if (ctx->bank_ctx[i]) { @@ -74,14 +78,16 @@ void pcilib_free_register_banks(pcilib_t *ctx) { } } - ctx->num_banks_init = 0; + ctx->num_banks_init = start; } int pcilib_add_register_banks(pcilib_t *ctx, pcilib_model_modification_flags_t flags, size_t n, const pcilib_register_bank_description_t *banks, pcilib_register_bank_t *ids) { + int err; size_t i; pcilib_register_bank_t bank; size_t dyn_banks = ctx->dyn_banks; size_t num_banks = ctx->num_banks; + size_t cur_banks = num_banks; if (!n) { for (n = 0; banks[n].access; n++); @@ -90,11 +96,6 @@ int pcilib_add_register_banks(pcilib_t *ctx, pcilib_model_modification_flags_t f if ((ctx->num_banks + n + 1) > PCILIB_MAX_REGISTER_BANKS) return PCILIB_ERROR_TOOBIG; -/* - memcpy(ctx->banks + ctx->num_banks, banks, n * sizeof(pcilib_register_bank_description_t)); - ctx->num_banks += n; -*/ - for (i = 0; i < n; i++) { // Try to find if the bank is already existing... bank = pcilib_find_register_bank_by_name(ctx, banks[i].name); @@ -111,6 +112,7 @@ int pcilib_add_register_banks(pcilib_t *ctx, pcilib_model_modification_flags_t f pcilib_error("The bank %s is already existing and override flag is not set", banks[i].name); else pcilib_error("The bank with address 0x%lx is already existing and override flag is not set", banks[i].addr); + memset(ctx->banks + ctx->num_banks, 0, sizeof(pcilib_register_bank_description_t)); return PCILIB_ERROR_EXIST; } @@ -122,17 +124,22 @@ int pcilib_add_register_banks(pcilib_t *ctx, pcilib_model_modification_flags_t f dyn_banks++; } } - + ctx->num_banks = num_banks; - ctx->dyn_banks = dyn_banks; // If banks are already initialized, we need to re-run the initialization code - // DS: Locking is currently missing if (ctx->reg_bar_mapped) { ctx->reg_bar_mapped = 0; - return pcilib_init_register_banks(ctx); + err = pcilib_init_register_banks(ctx); + if (err) { + ctx->num_banks = cur_banks; + memset(ctx->banks + ctx->num_banks, 0, sizeof(pcilib_register_bank_description_t)); + return err; + } } - + + ctx->dyn_banks = dyn_banks; + return 0; } diff --git a/pcilib/bank.h b/pcilib/bank.h index 602fa67..39dd79c 100644 --- a/pcilib/bank.h +++ b/pcilib/bank.h @@ -96,14 +96,64 @@ struct pcilib_register_bank_context_s { extern "C" { #endif - // we don't copy strings, they should be statically allocated +/** + * Initalizes context of register banks. This is an internal function and will + * be called automatically when new register banks are added. On error no new + * banks are initalized + * @param[in,out] ctx - pcilib context + * @return - error or 0 on success + */ int pcilib_init_register_banks(pcilib_t *ctx); -void pcilib_free_register_banks(pcilib_t *ctx); +/** + * Destroys contexts of register banks. This is an internal function and will + * be called during clean-up. + * @param[in,out] ctx - pcilib context + * @param[in] start - specifies first bank to clean (used to clean only part of the banks to keep the defined state if pcilib_init_register_banks has failed) + */ +void pcilib_free_register_banks(pcilib_t *ctx, pcilib_register_bank_t start); + + +/** + * Use this function to add new register banks into the model or override configuration + * of the existing banks. The function will copy the context of banks structure, but name, + * description, and other strings in the structure are considered to have static duration + * and will not be copied. On error no new banks are initalized. + * @param[in,out] ctx - pcilib context + * @param[in] flags - instructs if existing banks should be reported as error (default), overriden or ignored + * @param[in] n - number of banks to initialize. It is OK to pass 0 if banks variable is NULL terminated (last member of banks array have all members set to 0) + * @param[in] banks - bank descriptions + * @param[out] ids - if specified will contain the ids of the newly registered and overriden banks + * @return - error or 0 on success + */ int pcilib_add_register_banks(pcilib_t *ctx, pcilib_model_modification_flags_t flags, size_t n, const pcilib_register_bank_description_t *banks, pcilib_register_bank_t *ids); + +/** + * Use this function to add new register protocols into the model. It is error to re-register + * already registered protocols. The function will copy the context of banks structure, but name, + * description, and other strings in the structure are considered to have static duration + * and will not be copied. On error no new protocols are initalized. + * @param[in,out] ctx - pcilib context + * @param[in] flags - not used + * @param[in] n - number of protocols to initialize. It is OK to pass 0 if protocols variable is NULL terminated (last member of protocols array have all members set to 0) + * @param[in] protocols - protocol descriptions + * @param[out] ids - if specified will contain the ids of the newly registered protocols + * @return - error or 0 on success + */ int pcilib_add_register_protocols(pcilib_t *ctx, pcilib_model_modification_flags_t flags, size_t n, const pcilib_register_protocol_description_t *protocols, pcilib_register_protocol_t *ids); + +/** + * Use this function to add new register ranges into the model. It is error register + * overlapping registered ranges. On error no new ranges are initalized. + * @param[in,out] ctx - pcilib context + * @param[in] flags - not used + * @param[in] n - number of protocols to initialize. It is OK to pass 0 if protocols variable is NULL terminated. + * @param[in] ranges - range descriptions + * @return - error or 0 on success + */ int pcilib_add_register_ranges(pcilib_t *ctx, pcilib_model_modification_flags_t flags, size_t n, const pcilib_register_range_t *ranges); + pcilib_register_bank_t pcilib_find_register_bank_by_addr(pcilib_t *ctx, pcilib_register_bank_addr_t bank); pcilib_register_bank_t pcilib_find_register_bank_by_name(pcilib_t *ctx, const char *bankname); pcilib_register_bank_t pcilib_find_register_bank(pcilib_t *ctx, const char *bank); diff --git a/pcilib/error.h b/pcilib/error.h index 4ac0967..a9f4c0b 100644 --- a/pcilib/error.h +++ b/pcilib/error.h @@ -26,6 +26,7 @@ enum { PCILIB_ERROR_OUTOFRANGE = ERANGE, PCILIB_ERROR_NOTAVAILABLE = ENAVAIL, PCILIB_ERROR_NOTINITIALIZED = EBADFD, + PCILIB_ERROR_NOTPERMITED = EPERM, PCILIB_ERROR_TOOBIG = EFBIG, PCILIB_ERROR_OVERWRITTEN = ESTALE, PCILIB_ERROR_BUSY = EBUSY, diff --git a/pcilib/pci.c b/pcilib/pci.c index 4a0e79c..ae892a2 100644 --- a/pcilib/pci.c +++ b/pcilib/pci.c @@ -383,7 +383,7 @@ void pcilib_close(pcilib_t *ctx) { pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, ctx->dma_wlock[dma]); } - pcilib_free_register_banks(ctx); + pcilib_free_register_banks(ctx, 0); if (ctx->event_plugin) pcilib_plugin_close(ctx->event_plugin); @@ -414,16 +414,16 @@ void pcilib_close(pcilib_t *ctx) { if (ctx->units) { - pcilib_clean_units(ctx); + pcilib_clean_units(ctx, 0); free(ctx->units); } if (ctx->views) { - pcilib_clean_views(ctx); + pcilib_clean_views(ctx, 0); free(ctx->views); } - pcilib_clean_registers(ctx); + pcilib_clean_registers(ctx, 0); if (ctx->register_ctx) free(ctx->register_ctx); diff --git a/pcilib/pci.h b/pcilib/pci.h index caefe44..8f05ddf 100644 --- a/pcilib/pci.h +++ b/pcilib/pci.h @@ -40,16 +40,6 @@ typedef struct { } pcilib_pcie_link_info_t; -typedef struct { - const char *name; /**< Register name */ - pcilib_register_t reg; /**< Register index */ - pcilib_register_bank_t bank; /**< Reference to bank containing the register */ - pcilib_register_value_t min, max; /**< Minimum & maximum allowed values */ - pcilib_xml_node_t *xml; /**< Additional XML properties */ - pcilib_view_reference_t *views; /**< For non-static list of views, this vairables holds a copy of a NULL-terminated list from model (if present, memory should be de-allocated) */ - UT_hash_handle hh; -} pcilib_register_context_t; - struct pcilib_s { int handle; /**< file handle of device */ diff --git a/pcilib/pcilib.h b/pcilib/pcilib.h index 5c1ca70..e4fdf6d 100644 --- a/pcilib/pcilib.h +++ b/pcilib/pcilib.h @@ -41,6 +41,12 @@ typedef enum { } pcilib_endianess_t; typedef enum { + PCILIB_ACCESS_R = 1, /**< getting property is allowed */ + PCILIB_ACCESS_W = 2, /**< setting property is allowed */ + PCILIB_ACCESS_RW = 3 +} pcilib_access_mode_t; + +typedef enum { PCILIB_TYPE_INVALID = 0, /**< uninitialized */ PCILIB_TYPE_DEFAULT = 0, /**< default type */ PCILIB_TYPE_STRING = 1, /**< char* */ @@ -74,14 +80,14 @@ typedef enum { } pcilib_dma_flags_t; typedef enum { - PCILIB_STREAMING_STOP = 0, /**< stop streaming */ - PCILIB_STREAMING_CONTINUE = 1, /**< wait the default DMA timeout for a new data */ - PCILIB_STREAMING_WAIT = 2, /**< wait the specified timeout for a new data */ - PCILIB_STREAMING_CHECK = 3, /**< do not wait for the data, bail out imideatly if no data ready */ - PCILIB_STREAMING_FAIL = 4, /**< fail if data is not available on timeout */ - PCILIB_STREAMING_REQ_FRAGMENT = 5, /**< only fragment of a packet is read, wait for next fragment and fail if no data during DMA timeout */ - PCILIB_STREAMING_REQ_PACKET = 6, /**< wait for next packet and fail if no data during the specified timeout */ - PCILIB_STREAMING_TIMEOUT_MASK = 3 /**< mask specifying all timeout modes */ + PCILIB_STREAMING_STOP = 0, /**< stop streaming */ + PCILIB_STREAMING_CONTINUE = 1, /**< wait the default DMA timeout for a new data */ + PCILIB_STREAMING_WAIT = 2, /**< wait the specified timeout for a new data */ + PCILIB_STREAMING_CHECK = 3, /**< do not wait for the data, bail out imideatly if no data ready */ + PCILIB_STREAMING_FAIL = 4, /**< fail if data is not available on timeout */ + PCILIB_STREAMING_REQ_FRAGMENT = 5, /**< only fragment of a packet is read, wait for next fragment and fail if no data during DMA timeout */ + PCILIB_STREAMING_REQ_PACKET = 6, /**< wait for next packet and fail if no data during the specified timeout */ + PCILIB_STREAMING_TIMEOUT_MASK = 3 /**< mask specifying all timeout modes */ } pcilib_streaming_action_t; typedef enum { @@ -104,23 +110,38 @@ typedef struct { pcilib_event_info_flags_t flags; /**< flags */ } pcilib_event_info_t; +typedef enum { + PCILIB_LIST_FLAGS_DEFAULT = 0, + PCILIB_LIST_FLAG_CHILDS = 1 /**< Request all sub-elements or indicated that sub-elements are available */ +} pcilib_list_flags_t; + typedef struct { - pcilib_value_type_t type; - const char *unit; - const char *format; + pcilib_value_type_t type; /**< Current data type */ + const char *unit; /**< Units (if known) */ + const char *format; /**< requested printf format (may enforce using output in hex form) */ union { - long ival; - double fval; - const char *sval; + long ival; /**< The value if type = PCILIB_TYPE_LONG */ + double fval; /**< The value if type = PCILIB_TYPE_DOUBLE */ + const char *sval; /**< The value if type = PCILIB_TYPE_STRING, the pointer may point to static location or reference actual string in str or data */ }; // This is a private part - size_t size; - void *data; - char str[16]; + size_t size; /**< Size of the data */ + void *data; /**< Arbitrary data, for instance actual string referenced by the sval */ + char str[16]; /**< Used for shorter strings converted from integer/float types */ } pcilib_value_t; +typedef struct { + const char *name; /**< Name of the property view */ + const char *path; /**< Full path to the property */ + const char *description; /**< Short description */ + pcilib_value_type_t type; /**< The default data type or PCILIB_TYPE_INVALID if directory */ + pcilib_access_mode_t mode; /**< Specifies if the view is read/write-only */ + pcilib_list_flags_t flags; /**< Indicates if have sub-folders, etc. */ + const char *unit; /**< Returned unit (if any) */ +} pcilib_property_info_t; + #define PCILIB_BAR_DETECT ((pcilib_bar_t)-1) #define PCILIB_BAR_INVALID ((pcilib_bar_t)-1) @@ -218,6 +239,8 @@ int pcilib_read_register_by_id(pcilib_t *ctx, pcilib_register_t reg, pcilib_regi int pcilib_write_register_by_id(pcilib_t *ctx, pcilib_register_t reg, pcilib_register_value_t value); int pcilib_read_register(pcilib_t *ctx, const char *bank, const char *regname, pcilib_register_value_t *value); int pcilib_write_register(pcilib_t *ctx, const char *bank, const char *regname, pcilib_register_value_t value); +int pcilib_read_register_view(pcilib_t *ctx, const char *bank, const char *regname, const char *unit, pcilib_value_t *value); +int pcilib_write_register_view(pcilib_t *ctx, const char *bank, const char *regname, const char *unit, const pcilib_value_t *value); void pcilib_clean_value(pcilib_t *ctx, pcilib_value_t *val); int pcilib_copy_value(pcilib_t *ctx, pcilib_value_t *dst, const pcilib_value_t *src); @@ -228,12 +251,14 @@ int pcilib_set_value_from_static_string(pcilib_t *ctx, pcilib_value_t *value, co double pcilib_get_value_as_float(pcilib_t *ctx, const pcilib_value_t *val, int *err); long pcilib_get_value_as_int(pcilib_t *ctx, const pcilib_value_t *val, int *err); pcilib_register_value_t pcilib_get_value_as_register_value(pcilib_t *ctx, const pcilib_value_t *val, int *err); - int pcilib_convert_value_unit(pcilib_t *ctx, pcilib_value_t *val, const char *unit_name); int pcilib_convert_value_type(pcilib_t *ctx, pcilib_value_t *val, pcilib_value_type_t type); -int pcilib_read_register_view(pcilib_t *ctx, const char *bank, const char *regname, const char *unit, pcilib_value_t *value); -int pcilib_write_register_view(pcilib_t *ctx, const char *bank, const char *regname, const char *unit, const pcilib_value_t *value); +pcilib_property_info_t *pcilib_get_property_list(pcilib_t *ctx, const char *branch, pcilib_list_flags_t flags); +void pcilib_free_property_info(pcilib_t *ctx, pcilib_property_info_t *info); +int pcilib_get_property(pcilib_t *ctx, const char *prop, pcilib_value_t *val); +int pcilib_set_property(pcilib_t *ctx, const char *prop, const pcilib_value_t *val); + int pcilib_reset(pcilib_t *ctx); int pcilib_trigger(pcilib_t *ctx, pcilib_event_t event, size_t trigger_size, void *trigger_data); diff --git a/pcilib/property.c b/pcilib/property.c new file mode 100644 index 0000000..23c92d1 --- /dev/null +++ b/pcilib/property.c @@ -0,0 +1,207 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> + +#include <views/register.h> + +#include "pci.h" +#include "bank.h" +#include "view.h" +#include "register.h" +#include "property.h" + +#include "tools.h" +#include "error.h" + +int pcilib_add_register_properties(pcilib_t *ctx, size_t n, const pcilib_register_bank_t *banks, const pcilib_register_description_t *registers) { + int err; + + pcilib_register_t i; + pcilib_view_t cur_view = ctx->num_views; + pcilib_view_context_t *view_ctx; + + if (!n) + return PCILIB_ERROR_INVALID_ARGUMENT; + + + for (i = 0; i < n; i++) { + pcilib_access_mode_t mode = 0; + + pcilib_register_view_description_t v; + pcilib_register_bank_description_t *b = &ctx->banks[banks[i]]; + + char *view_name = malloc(strlen(registers[i].name) + strlen(b->name) + 13); + if (!view_name) { + pcilib_clean_views(ctx, cur_view); + return PCILIB_ERROR_MEMORY; + } + + sprintf(view_name, "/registers/%s/%s", b->name, registers[i].name); + + if ((registers[i].views)&&(registers[i].views[0].view)) { + pcilib_view_t view = pcilib_find_view_by_name(ctx, registers[i].views[0].view); + if (view == PCILIB_VIEW_INVALID) return PCILIB_ERROR_NOTFOUND; + + memcpy(&v, ctx->views[view], sizeof(pcilib_view_description_t)); + v.view = registers[i].views[0].name; + + if (ctx->views[view]->api->read_from_reg) mode |= PCILIB_ACCESS_R; + if (ctx->views[view]->api->write_to_reg) mode |= PCILIB_ACCESS_W; + mode &= ctx->views[view]->mode; + } else { + v.base.type = PCILIB_TYPE_LONG; + mode = PCILIB_ACCESS_RW; + } + + v.base.api = &pcilib_register_view_api; + v.base.name = view_name; + v.base.description = registers[i].description; + v.base.mode = registers[i].mode&mode; + v.reg = registers[i].name; + v.bank = b->name; + + err = pcilib_add_views(ctx, 1, (pcilib_view_description_t*)&v); + if (err) { + free(view_name); + pcilib_clean_views(ctx, cur_view); + return err; + } + + view_ctx = pcilib_find_view_context_by_name(ctx, v.base.name); + view_ctx->flags |= PCILIB_VIEW_FLAG_PROPERTY; + } + + return 0; +} + +pcilib_property_info_t *pcilib_get_property_list(pcilib_t *ctx, const char *branch, pcilib_list_flags_t flags) { + int err = 0; + + size_t pos = 0; + size_t name_offset = 0; + pcilib_view_context_t *view_ctx, *view_tmp; + pcilib_property_info_t *info = (pcilib_property_info_t*)malloc((ctx->num_views + 1) * sizeof(pcilib_property_info_t)); + + struct dir_hash_s { + char *name; + UT_hash_handle hh; + } *dir_hash = NULL, *dir, *dir_tmp; + + if (branch) { + name_offset = strlen(branch); + if (branch[name_offset - 1] != '/') name_offset++; + } + + // Find all folders + HASH_ITER(hh, ctx->view_hash, view_ctx, view_tmp) { + const pcilib_view_description_t *v = ctx->views[view_ctx->view]; + const char *subname = v->name + name_offset; + const char *suffix; + + if (!(view_ctx->flags&PCILIB_VIEW_FLAG_PROPERTY)) continue; + if ((branch)&&(strncasecmp(branch, v->name, strlen(branch)))) continue; + + suffix = strchr(subname, '/'); + if (suffix) { + char *name = strndup(v->name, suffix - v->name); + if (!name) { + err = PCILIB_ERROR_MEMORY; + break; + } + + HASH_FIND_STR(dir_hash, name + name_offset, dir); + if (dir) { + free(name); + continue; + } + + + dir = (struct dir_hash_s*)malloc(sizeof(struct dir_hash_s)); + if (!dir) { + err = PCILIB_ERROR_MEMORY; + break; + } + + dir->name = name; + + HASH_ADD_KEYPTR(hh, dir_hash, dir->name + name_offset, strlen(dir->name + name_offset), dir); + } + } + + HASH_ITER(hh, ctx->view_hash, view_ctx, view_tmp) { + const pcilib_view_description_t *v = ctx->views[view_ctx->view]; + const char *subname = v->name + name_offset; + + if (!(view_ctx->flags&PCILIB_VIEW_FLAG_PROPERTY)) continue; + if ((branch)&&(strncasecmp(branch, v->name, strlen(branch)))) continue; + + if (!strchr(subname, '/')) { + pcilib_view_context_t *found_view; + + char *path = strdup(v->name); + if (!path) { + err = PCILIB_ERROR_MEMORY; + break; + } + char *name = strrchr(v->name, '/'); + if (name) name++; + else name = path; + + HASH_FIND_STR(dir_hash, name, found_view); + + info[pos++] = (pcilib_property_info_t) { + .name = name, + .path = path, + .description = v->description, + .type = v->type, + .mode = v->mode, + .unit = v->unit, + .flags = (found_view?PCILIB_LIST_FLAG_CHILDS:0) + }; + + if (found_view) HASH_DEL(dir_hash, found_view); + } + } + + HASH_ITER(hh, dir_hash, dir, dir_tmp) { + char *name = strrchr(dir->name, '/'); + if (name) name++; + else name = dir->name; + + info[pos++] = (pcilib_property_info_t) { + .name = name, + .path = dir->name, + .type = PCILIB_TYPE_INVALID, + .flags = PCILIB_LIST_FLAG_CHILDS + }; + } + + HASH_CLEAR(hh, dir_hash); + + memset(&info[pos], 0, sizeof(pcilib_property_info_t)); + + if (err) { + pcilib_free_property_info(ctx, info); + return NULL; + } + + return info; +} + +void pcilib_free_property_info(pcilib_t *ctx, pcilib_property_info_t *info) { + int i; + + for (i = 0; info[i].path; i++) + free((char*)info[i].path); + free(info); +} + +int pcilib_get_property(pcilib_t *ctx, const char *prop, pcilib_value_t *val) { + return pcilib_read_register_view(ctx, NULL, NULL, prop, val); +} + +int pcilib_set_property(pcilib_t *ctx, const char *prop, const pcilib_value_t *val) { + return pcilib_write_register_view(ctx, NULL, NULL, prop, val); +} diff --git a/pcilib/property.h b/pcilib/property.h new file mode 100644 index 0000000..bec11c8 --- /dev/null +++ b/pcilib/property.h @@ -0,0 +1,30 @@ +#ifndef _PCILIB_PROPERTY_H +#define _PCILIB_PROPERTY_H + +#ifdef __cplusplus +extern "C" { +#endif +/** + * This is internal function used to add property view for all model registers. It is automatically + * called from pcilib_add_registers and should not be called by the users. On error no new views are + * initalized. + * @param[in,out] ctx - pcilib context + * @param[in] n - number of views to initialize. + * @param[in] banks - array containing a bank id for each of the considered registers + * @param[in] desc - register descriptions + * @return - error or 0 on success + */ +int pcilib_add_register_properties(pcilib_t *ctx, size_t n, const pcilib_register_bank_t *banks, const pcilib_register_description_t *desc); + +#ifdef __cplusplus +} +#endif + +#endif /* _PCILIB_PROPERTY_H */ + + + + + +// free'd by user. Do we need it? + diff --git a/pcilib/register.c b/pcilib/register.c index 3a60800..30505ae 100644 --- a/pcilib/register.c +++ b/pcilib/register.c @@ -11,22 +11,28 @@ #include <arpa/inet.h> #include <errno.h> #include <assert.h> +#include <alloca.h> #include "pci.h" #include "bank.h" #include "tools.h" #include "error.h" - +#include "property.h" int pcilib_add_registers(pcilib_t *ctx, pcilib_model_modification_flags_t flags, size_t n, const pcilib_register_description_t *registers, pcilib_register_t *ids) { // DS: Overrride existing registers // Registers identified by addr + offset + size + type or name - + int err; + size_t size; pcilib_register_t i; + pcilib_register_description_t *regs; pcilib_register_context_t *reg_ctx; - size_t size; + + pcilib_register_bank_t bank = PCILIB_REGISTER_BANK_INVALID; + pcilib_register_bank_addr_t bank_addr = (pcilib_register_bank_addr_t)-1; + pcilib_register_bank_t *banks; if (!n) { for (n = 0; registers[n].bits; n++); @@ -59,13 +65,43 @@ int pcilib_add_registers(pcilib_t *ctx, pcilib_model_modification_flags_t flags, ctx->alloc_reg = size; } + banks = (pcilib_register_bank_t*)alloca(n * sizeof(pcilib_register_bank_t)); + if (!banks) return PCILIB_ERROR_MEMORY; + + for (i = 0; i < n; i++) { + if (registers[i].bank != bank_addr) { + bank_addr = registers[i].bank; + bank = pcilib_find_register_bank_by_addr(ctx, bank_addr); + if (bank == PCILIB_REGISTER_BANK_INVALID) { + pcilib_error("Invalid bank address (0x%lx) is specified for register %s", bank_addr, registers[i].name); + return PCILIB_ERROR_INVALID_BANK; + } + } + +/* + // No hash so far, will iterate. + pcilib_register_t reg = pcilib_find_register(ctx, ctx->banks[bank].name, registers[i].name); + if (reg != PCILIB_REGISTER_INVALID) { + pcilib_error("Register %s is already defined in the model", registers[i].name); + return PCILIB_ERROR_EXIST; + } +*/ + + banks[i] = bank; + } + + err = pcilib_add_register_properties(ctx, n, banks, registers); + if (err) return err; + for (i = 0; i < n; i++) { pcilib_register_context_t *cur = &ctx->register_ctx[ctx->num_reg + i]; + cur->reg = ctx->num_reg + i; cur->name = registers[i].name; + cur->bank = banks[i]; HASH_ADD_KEYPTR(hh, ctx->reg_hash, cur->name, strlen(cur->name), cur); } - + memcpy(ctx->registers + ctx->num_reg, registers, n * sizeof(pcilib_register_description_t)); memset(ctx->registers + ctx->num_reg + n, 0, sizeof(pcilib_register_description_t)); @@ -78,26 +114,35 @@ int pcilib_add_registers(pcilib_t *ctx, pcilib_model_modification_flags_t flags, ctx->num_reg += n; - return 0; } -void pcilib_clean_registers(pcilib_t *ctx) { +void pcilib_clean_registers(pcilib_t *ctx, pcilib_register_t start) { pcilib_register_t reg; + pcilib_register_context_t *reg_ctx, *tmp; + + if (start) { + HASH_ITER(hh, ctx->reg_hash, reg_ctx, tmp) { + if (reg_ctx->reg >= start) { + HASH_DEL(ctx->reg_hash, reg_ctx); + } + } + } else { + HASH_CLEAR(hh, ctx->reg_hash); + } - HASH_CLEAR(hh, ctx->reg_hash); - for (reg = 0; reg < ctx->num_reg; reg++) { + for (reg = start; reg < ctx->num_reg; reg++) { if (ctx->register_ctx[reg].views) free(ctx->register_ctx[reg].views); } if (ctx->registers) - memset(ctx->registers, 0, sizeof(pcilib_register_description_t)); + memset(&ctx->registers[start], 0, sizeof(pcilib_register_description_t)); if (ctx->register_ctx) - memset(ctx->register_ctx, 0, ctx->alloc_reg * sizeof(pcilib_register_context_t)); + memset(&ctx->register_ctx[start], 0, (ctx->alloc_reg - start) * sizeof(pcilib_register_context_t)); - ctx->num_reg = 0; + ctx->num_reg = start; } static int pcilib_read_register_space_internal(pcilib_t *ctx, pcilib_register_bank_t bank, pcilib_register_addr_t addr, size_t n, pcilib_register_size_t offset, pcilib_register_size_t bits, pcilib_register_value_t *buf) { diff --git a/pcilib/register.h b/pcilib/register.h index 95f52cc..e2e8508 100644 --- a/pcilib/register.h +++ b/pcilib/register.h @@ -1,6 +1,8 @@ #ifndef _PCILIB_REGISTER_H #define _PCILIB_REGISTER_H +#include <uthash.h> + #include <pcilib.h> #include <pcilib/bank.h> @@ -49,13 +51,43 @@ typedef struct { pcilib_view_reference_t *views; /**< List of supported views for this register */ } pcilib_register_description_t; +typedef struct { + const char *name; /**< Register name */ + pcilib_register_t reg; /**< Register index */ + pcilib_register_bank_t bank; /**< Reference to bank containing the register */ + pcilib_register_value_t min, max; /**< Minimum & maximum allowed values */ + pcilib_xml_node_t *xml; /**< Additional XML properties */ + pcilib_view_reference_t *views; /**< For non-static list of views, this vairables holds a copy of a NULL-terminated list from model (if present, memory should be de-allocated) */ + UT_hash_handle hh; +} pcilib_register_context_t; #ifdef __cplusplus extern "C" { #endif +/** + * Use this function to add new registers into the model. Currently, it is considered a error + * to re-add already defined register. If it will turn out to be useful to redefine some registers + * from the model, it may change in the future. However, we should think how to treat bit-registers + * in this case. The function will copy the context of registers structure, but name, + * description, and other strings in the structure are considered to have static duration + * and will not be copied. On error no new registers are initalized. + * @param[in,out] ctx - pcilib context + * @param[in] flags - not used now, but in future may instruct if existing registers should be reported as error (default), overriden or ignored + * @param[in] n - number of registers to initialize. It is OK to pass 0 if registers array is NULL terminated (last member of the array have all members set to 0) + * @param[in] registers - register descriptions + * @param[out] ids - if specified will contain the ids of the newly registered and overriden registers + * @return - error or 0 on success + */ int pcilib_add_registers(pcilib_t *ctx, pcilib_model_modification_flags_t flags, size_t n, const pcilib_register_description_t *registers, pcilib_register_t *ids); -void pcilib_clean_registers(pcilib_t *ctx); + +/** + * Destroys data associated with registers. This is an internal function and will + * be called during clean-up. + * @param[in,out] ctx - pcilib context + * @param[in] start - specifies first register to clean (used to clean only part of the registers to keep the defined state if pcilib_add_registers has failed) + */ +void pcilib_clean_registers(pcilib_t *ctx, pcilib_register_t start); #ifdef __cplusplus } diff --git a/pcilib/unit.c b/pcilib/unit.c index 0295120..99ece99 100644 --- a/pcilib/unit.c +++ b/pcilib/unit.c @@ -35,11 +35,19 @@ int pcilib_add_units(pcilib_t *ctx, size_t n, const pcilib_unit_description_t *d // ToDo: Check if exists... for (i = 0; i < n; i++) { + pcilib_unit_t unit = pcilib_find_unit_by_name(ctx, desc[i].name); + if (unit != PCILIB_UNIT_INVALID) { + pcilib_clean_units(ctx, ctx->num_units); + pcilib_error("Unit %s is already defined in the model", desc[i].name); + return PCILIB_ERROR_EXIST; + } + pcilib_unit_context_t *unit_ctx = (pcilib_unit_context_t*)malloc(sizeof(pcilib_unit_context_t)); if (!unit_ctx) { - err = PCILIB_ERROR_MEMORY; - break; + pcilib_clean_units(ctx, ctx->num_units); + return PCILIB_ERROR_MEMORY; } + memset(unit_ctx, 0, sizeof(pcilib_unit_context_t)); unit_ctx->unit = ctx->num_units + i; unit_ctx->name = desc[i].name; @@ -48,24 +56,26 @@ int pcilib_add_units(pcilib_t *ctx, size_t n, const pcilib_unit_description_t *d memcpy(ctx->units + ctx->num_units + i, &desc[i], sizeof(pcilib_unit_description_t)); } - memset(ctx->units + ctx->num_units + i, 0, sizeof(pcilib_unit_description_t)); - ctx->num_units += i; + ctx->num_units += n; + memset(ctx->units + ctx->num_units, 0, sizeof(pcilib_unit_description_t)); return err; } -void pcilib_clean_units(pcilib_t *ctx) { +void pcilib_clean_units(pcilib_t *ctx, pcilib_unit_t start) { pcilib_unit_context_t *s, *tmp; if (ctx->unit_hash) { HASH_ITER(hh, ctx->unit_hash, s, tmp) { - HASH_DEL(ctx->unit_hash, s); - free(s); + if (s->unit >= start) { + HASH_DEL(ctx->unit_hash, s); + free(s); + } } } - memset(ctx->units, 0, sizeof(pcilib_unit_description_t)); - ctx->num_units = 0; + memset(&ctx->units[start], 0, sizeof(pcilib_unit_description_t)); + ctx->num_units = start; } pcilib_unit_t pcilib_find_unit_by_name(pcilib_t *ctx, const char *name) { @@ -105,13 +115,18 @@ pcilib_unit_transform_t *pcilib_find_transform_by_unit_names(pcilib_t *ctx, cons return NULL; } -int pcilib_transform_unit(pcilib_t *ctx, pcilib_unit_transform_t *trans, pcilib_value_t *value) { +int pcilib_transform_unit(pcilib_t *ctx, const pcilib_unit_transform_t *trans, pcilib_value_t *value) { int err; - err = pcilib_py_eval_string(ctx, trans->transform, value); - if (err) return err; + if (trans->transform) { + err = pcilib_py_eval_string(ctx, trans->transform, value); + if (err) return err; + + value->unit = trans->unit; + } else if (trans->unit) { + value->unit = trans->unit; + } - value->unit = trans->unit; return 0; } diff --git a/pcilib/unit.h b/pcilib/unit.h index 3e49174..2351f26 100644 --- a/pcilib/unit.h +++ b/pcilib/unit.h @@ -35,14 +35,47 @@ struct pcilib_unit_context_s { extern "C" { #endif +/** + * Use this function to add new unit definitions into the model. It is error to re-register + * already registered unit. The function will copy the context of unit description, but name, + * transform, and other strings in the structure are considered to have static duration + * and will not be copied. On error no new units are initalized. + * @param[in,out] ctx - pcilib context + * @param[in] n - number of units to initialize. It is OK to pass 0 if protocols variable is NULL terminated (last member of protocols array have all members set to 0) + * @param[in] desc - unit descriptions + * @return - error or 0 on success + */ int pcilib_add_units(pcilib_t *ctx, size_t n, const pcilib_unit_description_t *desc); -void pcilib_clean_units(pcilib_t *ctx); + +/** + * Destroys data associated with units. This is an internal function and will + * be called during clean-up. + * @param[in,out] ctx - pcilib context + * @param[in] start - specifies first unit to clean (used to clean only part of the units to keep the defined state if pcilib_add_units has failed) + */ +void pcilib_clean_units(pcilib_t *ctx, pcilib_unit_t start); pcilib_unit_t pcilib_find_unit_by_name(pcilib_t *ctx, const char *unit); pcilib_unit_transform_t *pcilib_find_transform_by_unit_names(pcilib_t *ctx, const char *from, const char *to); - // value is modified -int pcilib_transform_unit(pcilib_t *ctx, pcilib_unit_transform_t *trans, pcilib_value_t *value); +/** + * Converts value to the requested units. It is error to convert values with unspecified units. + * This is internal function, use pcilib_value_convert_value_unit instead. + * @param[in,out] ctx - pcilib context + * @param[in] trans - the requested unit transform + * @param[in,out] value - the value to be converted (changed on success) + * @return - error or 0 on success + */ +int pcilib_transform_unit(pcilib_t *ctx, const pcilib_unit_transform_t *trans, pcilib_value_t *value); + +/** + * Converts value to the requested units. It is error to convert values with unspecified units. + * This is internal function, use pcilib_value_convert_value_unit instead. + * @param[in,out] ctx - pcilib context + * @param[in] name - specifies the requested unit of the value + * @param[in,out] value - the value to be converted (changed on success) + * @return - error or 0 on success + */ int pcilib_transform_unit_by_name(pcilib_t *ctx, const char *to, pcilib_value_t *value); diff --git a/pcilib/value.c b/pcilib/value.c index 42e7993..4264759 100644 --- a/pcilib/value.c +++ b/pcilib/value.c @@ -267,5 +267,6 @@ int pcilib_convert_value_type(pcilib_t *ctx, pcilib_value_t *val, pcilib_value_t return PCILIB_ERROR_NOTSUPPORTED; } + val->type = type; return 0; } diff --git a/pcilib/view.c b/pcilib/view.c index f00e483..8df5fc4 100644 --- a/pcilib/view.c +++ b/pcilib/view.c @@ -11,7 +11,6 @@ #include "value.h" int pcilib_add_views(pcilib_t *ctx, size_t n, const pcilib_view_description_t *desc) { - int err = 0; size_t i; void *ptr; @@ -44,55 +43,68 @@ int pcilib_add_views(pcilib_t *ctx, size_t n, const pcilib_view_description_t *d ptr = (void*)desc; for (i = 0; i < n; i++) { const pcilib_view_description_t *v = (const pcilib_view_description_t*)ptr; + pcilib_view_description_t *cur; pcilib_view_context_t *view_ctx; - ctx->views[ctx->num_views + i] = (pcilib_view_description_t*)malloc(v->api->description_size); - if (!ctx->views[ctx->num_views + i]) { - err = PCILIB_ERROR_MEMORY; - break; + pcilib_view_t view = pcilib_find_view_by_name(ctx, v->name); + if (view != PCILIB_VIEW_INVALID) { + pcilib_clean_views(ctx, ctx->num_views); + pcilib_error("View %s is already defined in the model", v->name); + return PCILIB_ERROR_EXIST; + } + + cur = (pcilib_view_description_t*)malloc(v->api->description_size); + if (!cur) { + pcilib_clean_views(ctx, ctx->num_views); + return PCILIB_ERROR_MEMORY; } if (v->api->init) view_ctx = v->api->init(ctx); else { view_ctx = (pcilib_view_context_t*)malloc(sizeof(pcilib_view_context_t)); - memset(view_ctx, 0, sizeof(pcilib_view_context_t)); + if (view_ctx) memset(view_ctx, 0, sizeof(pcilib_view_context_t)); } - view_ctx->view = ctx->num_views + i; - view_ctx->name = v->name; - if (!view_ctx) { - free(ctx->views[ctx->num_views + i]); - err = PCILIB_ERROR_MEMORY; - break; + free(cur); + pcilib_clean_views(ctx, ctx->num_views); + return PCILIB_ERROR_FAILED; } + memcpy(cur, v, v->api->description_size); + view_ctx->view = ctx->num_views + i; + view_ctx->name = v->name; + HASH_ADD_KEYPTR(hh, ctx->view_hash, view_ctx->name, strlen(view_ctx->name), view_ctx); - memcpy(ctx->views[ctx->num_views + i], v, v->api->description_size); + ctx->views[ctx->num_views + i] = cur; + ptr += v->api->description_size; } - ctx->views[ctx->num_views + i] = NULL; - ctx->num_views += i; - return err; + ctx->views[ctx->num_views + n] = NULL; + ctx->num_views += n; + + return 0; } -void pcilib_clean_views(pcilib_t *ctx) { +void pcilib_clean_views(pcilib_t *ctx, pcilib_view_t start) { pcilib_view_t i; pcilib_view_context_t *view_ctx, *tmp; - if (ctx->unit_hash) { + if (ctx->view_hash) { HASH_ITER(hh, ctx->view_hash, view_ctx, tmp) { const pcilib_view_description_t *v = ctx->views[view_ctx->view]; - HASH_DEL(ctx->view_hash, view_ctx); - if (v->api->free) v->api->free(ctx, view_ctx); - else free(view_ctx); + if (view_ctx->view >= start) { + HASH_DEL(ctx->view_hash, view_ctx); + if (v->api->free) v->api->free(ctx, view_ctx); + else free(view_ctx); + } } } - for (i = 0; ctx->views[i]; i++) { + for (i = start; ctx->views[i]; i++) { if (ctx->views[i]->api->free_description) { ctx->views[i]->api->free_description(ctx, ctx->views[i]); } else { @@ -100,8 +112,8 @@ void pcilib_clean_views(pcilib_t *ctx) { } } - ctx->views[0] = NULL; - ctx->num_views = 0; + ctx->views[start] = NULL; + ctx->num_views = start; } pcilib_view_context_t *pcilib_find_view_context_by_name(pcilib_t *ctx, const char *name) { @@ -117,8 +129,6 @@ pcilib_view_t pcilib_find_view_by_name(pcilib_t *ctx, const char *name) { return PCILIB_VIEW_INVALID; } - - pcilib_view_context_t *pcilib_find_register_view_context_by_name(pcilib_t *ctx, pcilib_register_t reg, const char *name) { pcilib_view_t i; pcilib_register_context_t *regctx = &ctx->register_ctx[reg]; @@ -191,7 +201,7 @@ typedef struct { pcilib_unit_transform_t *trans; } pcilib_view_configuration_t; -static int pcilib_detect_view_configuration(pcilib_t *ctx, const char *bank, const char *regname, const char *view_cname, int write_direction, pcilib_view_configuration_t *cfg) { +static int pcilib_detect_view_configuration(pcilib_t *ctx, const char *bank, const char *regname, const char *view_cname, const char *unit_cname, int write_direction, pcilib_view_configuration_t *cfg) { int err = 0; pcilib_view_t view; pcilib_view_context_t *view_ctx; @@ -199,17 +209,21 @@ static int pcilib_detect_view_configuration(pcilib_t *ctx, const char *bank, con pcilib_register_t reg = PCILIB_REGISTER_INVALID; char *view_name = alloca(strlen(view_cname) + 1); - char *unit_name; + const char *unit_name; strcpy(view_name, view_cname); - unit_name = strchr(view_name, ':'); - if (unit_name) { - *unit_name = 0; - unit_name++; + if (unit_cname) unit_name = unit_cname; + else { + unit_name = strchr(view_name, ':'); + if (unit_name) { + *(char*)unit_name = 0; + unit_name++; + } } + if (regname) { reg = pcilib_find_register(ctx, bank, regname); if (reg == PCILIB_REGISTER_INVALID) { @@ -244,7 +258,7 @@ static int pcilib_detect_view_configuration(pcilib_t *ctx, const char *bank, con } // No transform is required - if (!trans->transform) trans = NULL; + if ((trans)&&(!trans->transform)) trans = NULL; cfg->reg = reg; cfg->view = view_ctx; @@ -261,7 +275,7 @@ int pcilib_read_register_view(pcilib_t *ctx, const char *bank, const char *regna pcilib_view_configuration_t cfg; pcilib_register_value_t regvalue = 0; - err = pcilib_detect_view_configuration(ctx, bank, regname, view, 0, &cfg); + err = pcilib_detect_view_configuration(ctx, bank, regname, view, NULL, 0, &cfg); if (err) return err; v = ctx->views[cfg.view->view]; @@ -271,6 +285,11 @@ int pcilib_read_register_view(pcilib_t *ctx, const char *bank, const char *regna return PCILIB_ERROR_NOTSUPPORTED; } + if ((v->mode & PCILIB_REGISTER_R) == 0) { + pcilib_error("The view (%s) does not allow reading from the register", view); + return PCILIB_ERROR_NOTPERMITED; + } + if (regname) { err = pcilib_read_register_by_id(ctx, cfg.reg, ®value); if (err) { @@ -290,6 +309,10 @@ int pcilib_read_register_view(pcilib_t *ctx, const char *bank, const char *regna return err; } + if (v->unit) { + val->unit = v->unit; + } + if (cfg.trans) { err = pcilib_transform_unit(ctx, cfg.trans, val); if (err) return err; @@ -307,16 +330,21 @@ int pcilib_write_register_view(pcilib_t *ctx, const char *bank, const char *regn pcilib_view_configuration_t cfg; pcilib_register_value_t regvalue = 0; - err = pcilib_detect_view_configuration(ctx, bank, regname, view, 1, &cfg); + err = pcilib_detect_view_configuration(ctx, bank, regname, view, valarg->unit, 1, &cfg); if (err) return err; v = ctx->views[cfg.view->view]; if (!v->api->write_to_reg) { - pcilib_error("The view (%s) does not support reading from the register", view); + pcilib_error("The view (%s) does not support writting to the register", view); return PCILIB_ERROR_NOTSUPPORTED; } + if ((v->mode & PCILIB_REGISTER_W) == 0) { + pcilib_error("The view (%s) does not allow writting to the register", view); + return PCILIB_ERROR_NOTPERMITED; + } + err = pcilib_copy_value(ctx, &val, valarg); if (err) return err; diff --git a/pcilib/view.h b/pcilib/view.h index 9d3d32d..6287942 100644 --- a/pcilib/view.h +++ b/pcilib/view.h @@ -16,19 +16,19 @@ typedef enum { } pcilib_view_flags_t; typedef struct { - pcilib_version_t version; - size_t description_size; - pcilib_view_context_t *(*init)(pcilib_t *ctx); - void (*free)(pcilib_t *ctx, pcilib_view_context_t *view); - void (*free_description)(pcilib_t *ctx, pcilib_view_description_t *view); - int (*read_from_reg)(pcilib_t *ctx, pcilib_view_context_t *view, pcilib_register_value_t regval, pcilib_value_t *val); - int (*write_to_reg)(pcilib_t *ctx, pcilib_view_context_t *view, pcilib_register_value_t *regval, const pcilib_value_t *val); + pcilib_version_t version; /**< Version */ + size_t description_size; /**< The actual size of the description */ + pcilib_view_context_t *(*init)(pcilib_t *ctx); /**< Optional function which should allocated context used by read/write functions */ + void (*free)(pcilib_t *ctx, pcilib_view_context_t *view); /**< Optional function which should clean context */ + void (*free_description)(pcilib_t *ctx, pcilib_view_description_t *view); /**< Optional function which shoud clean required parts of the extended description if non-static memory was used to initialize it */ + int (*read_from_reg)(pcilib_t *ctx, pcilib_view_context_t *view, pcilib_register_value_t regval, pcilib_value_t *val); /**< Function which computes view value based on the passed the register value (view-based properties should not use register value) */ + int (*write_to_reg)(pcilib_t *ctx, pcilib_view_context_t *view, pcilib_register_value_t *regval, const pcilib_value_t *val); /**< Function which computes register value based on the passed value (view-based properties are not required to set the register value) */ } pcilib_view_api_description_t; struct pcilib_view_description_s { const pcilib_view_api_description_t *api; pcilib_value_type_t type; /**< The default data type returned by operation, PCILIB_VIEW_TYPE_STRING is supported by all operations */ - pcilib_view_flags_t flags; /**< Flags specifying type of the view */ + pcilib_access_mode_t mode; /**< Specifies if the view is read/write-only */ const char *unit; /**< Returned unit (if any) */ const char *name; /**< Name of the view */ const char *description; /**< Short description */ @@ -37,6 +37,7 @@ struct pcilib_view_description_s { struct pcilib_view_context_s { const char *name; pcilib_view_t view; + pcilib_view_flags_t flags; /**< Flags specifying type of the view */ UT_hash_handle hh; }; @@ -44,8 +45,25 @@ struct pcilib_view_context_s { extern "C" { #endif +/** + * Use this function to add new view definitions into the model. It is error to re-register + * already registered view. The function will copy the context of unit description, but name, + * transform, and other strings in the structure are considered to have static duration + * and will not be copied. On error no new views are initalized. + * @param[in,out] ctx - pcilib context + * @param[in] n - number of views to initialize. It is OK to pass 0 if protocols variable is NULL terminated (last member of protocols array have all members set to 0) + * @param[in] desc - view descriptions + * @return - error or 0 on success + */ int pcilib_add_views(pcilib_t *ctx, size_t n, const pcilib_view_description_t *desc); -void pcilib_clean_views(pcilib_t *ctx); + +/** + * Destroys data associated with views. This is an internal function and will + * be called during clean-up. + * @param[in,out] ctx - pcilib context + * @param[in] start - specifies first view to clean (used to clean only part of the views to keep the defined state if pcilib_add_views has failed) + */ +void pcilib_clean_views(pcilib_t *ctx, pcilib_view_t start); pcilib_view_context_t *pcilib_find_view_context_by_name(pcilib_t *ctx, const char *view); pcilib_view_context_t *pcilib_find_register_view_context_by_name(pcilib_t *ctx, pcilib_register_t reg, const char *name); diff --git a/pcilib/xml.c b/pcilib/xml.c index 25ffbfe..4df0d2d 100644 --- a/pcilib/xml.c +++ b/pcilib/xml.c @@ -479,8 +479,6 @@ static int pcilib_xml_parse_view(pcilib_t *ctx, xmlXPathContextPtr xpath, xmlDoc xmlAttrPtr cur; const char *value, *name; - desc->type = PCILIB_TYPE_STRING; - for (cur = node->properties; cur != NULL; cur = cur->next) { if (!cur->children) continue; if (!xmlNodeIsText(cur->children)) continue; @@ -517,6 +515,7 @@ static int pcilib_xml_create_transform_view(pcilib_t *ctx, xmlXPathContextPtr xp pcilib_transform_view_description_t desc = {0}; desc.base.api = &pcilib_transform_view_api; + desc.base.type = PCILIB_TYPE_DOUBLE; err = pcilib_xml_parse_view(ctx, xpath, doc, node, (pcilib_view_description_t*)&desc); if (err) return err; @@ -531,8 +530,10 @@ static int pcilib_xml_create_transform_view(pcilib_t *ctx, xmlXPathContextPtr xp if (!strcasecmp(name, "read_from_register")) { desc.read_from_reg = value; + if ((value)&&(*value)) desc.base.mode |= PCILIB_ACCESS_R; } else if (!strcasecmp(name, "write_to_register")) { desc.write_to_reg = value; + if ((value)&&(*value)) desc.base.mode |= PCILIB_ACCESS_W; } } @@ -610,8 +611,10 @@ static int pcilib_xml_create_enum_view(pcilib_t *ctx, xmlXPathContextPtr xpath, pcilib_enum_view_description_t desc = {0}; + desc.base.type = PCILIB_TYPE_STRING; desc.base.unit = pcilib_xml_enum_view_unit; desc.base.api = &pcilib_enum_view_xml_api; + desc.base.mode = PCILIB_ACCESS_RW; err = pcilib_xml_parse_view(ctx, xpath, doc, node, (pcilib_view_description_t*)&desc); if (err) return err; diff --git a/pcitool/cli.c b/pcitool/cli.c index 867b65e..2956306 100644 --- a/pcitool/cli.c +++ b/pcitool/cli.c @@ -77,8 +77,10 @@ typedef enum { MODE_BENCHMARK, MODE_READ, MODE_READ_REGISTER, + MODE_READ_PROPERTY, MODE_WRITE, MODE_WRITE_REGISTER, + MODE_WRITE_PROPERTY, MODE_RESET, MODE_GRAB, MODE_START_DMA, @@ -193,7 +195,7 @@ static struct option long_options[] = { {"timeout", required_argument, 0, OPT_TIMEOUT }, {"iterations", required_argument, 0, OPT_ITERATIONS }, {"info", optional_argument, 0, OPT_INFO }, - {"list", no_argument, 0, OPT_LIST }, + {"list", optional_argument, 0, OPT_LIST }, {"reset", no_argument, 0, OPT_RESET }, {"benchmark", optional_argument, 0, OPT_BENCHMARK }, {"read", optional_argument, 0, OPT_READ }, @@ -258,7 +260,7 @@ void Usage(int argc, char *argv[], const char *format, ...) { " %s <mode> [options] [hex data]\n" " Modes:\n" " -i [target] - Device or Register (target) Info\n" -" -l[l] - List (detailed) Data Banks & Registers\n" +" -l[l] [bank|/branch] - List (detailed) Data Banks & Registers\n" " -r <addr|dmaX|reg[/unit]> - Read Data/Register\n" " -w <addr|dmaX|reg[/unit]> - Write Data/Register\n" " --benchmark <barX|dmaX> - Performance Evaluation\n" @@ -406,6 +408,79 @@ int RegisterCompare(const void *aptr, const void *bptr, void *registers) { return 0; } +void ListProperties(pcilib_t *handle, const char *branch, int details) { + int i; + pcilib_property_info_t *props; + + props = pcilib_get_property_list(handle, branch, 0); + if (!props) Error("Error getting properties"); + + if (props[0].path) { + printf("Properties: \n"); + + for (i = 0; props[i].path; i++) { + const char *mode; + const char *type; + + switch (props[i].type) { + case PCILIB_TYPE_LONG: + type = "int "; + break; + case PCILIB_TYPE_DOUBLE: + type = "float "; + break; + case PCILIB_TYPE_STRING: + type = "string "; + break; + case PCILIB_TYPE_INVALID: + type = NULL; + break; + default: + type = "unknown"; + } + + switch (props[i].mode) { + case PCILIB_ACCESS_RW: + mode = "RW"; + break; + case PCILIB_ACCESS_R: + mode = "R "; + break; + case PCILIB_ACCESS_W: + mode = "W "; + break; + default: + mode = " "; + } + + if (type) + printf(" (%s %s) ", type, mode); + else + printf(" %12s", ""); + + if (props[i].flags&PCILIB_LIST_FLAG_CHILDS) + printf(" + "); + else + printf(" "); + + if (details > 0) { + printf("%s", props[i].name); + if ((props[i].description)&&(props[i].description[0])) { + printf(": %s", props[i].description); + } + } else { + printf("%s", props[i].path); + } + printf("\n"); + } + + printf("\n"); + + pcilib_free_property_info(handle, props); + } + +} + void List(pcilib_t *handle, const pcilib_model_description_t *model_info, const char *bank, int details) { int i, j, k; const pcilib_register_bank_description_t *banks; @@ -415,7 +490,7 @@ void List(pcilib_t *handle, const pcilib_model_description_t *model_info, const const pcilib_board_info_t *board_info = pcilib_get_board_info(handle); const pcilib_dma_description_t *dma_info = pcilib_get_dma_description(handle); - + for (i = 0; i < PCILIB_MAX_BARS; i++) { if (board_info->bar_length[i] > 0) { printf(" BAR %d - ", i); @@ -470,7 +545,7 @@ void List(pcilib_t *handle, const pcilib_model_description_t *model_info, const if ((bank)&&(bank != (char*)-1)) banks = NULL; else banks = model_info->banks; - + if (banks) { printf("Banks: \n"); for (i = 0; banks[i].access; i++) { @@ -543,6 +618,8 @@ void List(pcilib_t *handle, const pcilib_model_description_t *model_info, const printf("\n"); } + ListProperties(handle, "/", details); + if (bank == (char*)-1) events = NULL; else { events = model_info->events; @@ -1126,7 +1203,7 @@ int ReadData(pcilib_t *handle, ACCESS_MODE mode, FLAGS flags, pcilib_dma_engine_ -int ReadRegister(pcilib_t *handle, const pcilib_model_description_t *model_info, const char *bank, const char *reg, const char *unit) { +int ReadRegister(pcilib_t *handle, const pcilib_model_description_t *model_info, const char *bank, const char *reg, const char *view, const char *unit) { int i; int err; const char *format; @@ -1139,19 +1216,37 @@ int ReadRegister(pcilib_t *handle, const pcilib_model_description_t *model_info, // Adding DMA registers pcilib_get_dma_description(handle); - if (reg) { - pcilib_register_t regid = pcilib_find_register(handle, bank, reg); - - if (unit) { + if (reg||view) { + if (view) { pcilib_value_t val = {0}; - err = pcilib_read_register_view(handle, bank, reg, unit, &val); - if (err) Error("Error reading view %s of register %s", unit, reg); + if (reg) { + err = pcilib_read_register_view(handle, bank, reg, view, &val); + if (err) Error("Error reading view %s of register %s", view, reg); + } else { + err = pcilib_get_property(handle, view, &val); + if (err) Error("Error reading property %s", view); + } + if (unit) { + err = pcilib_convert_value_unit(handle, &val, unit); + if (err) { + if (reg) Error("Error converting view %s of register %s to unit %s", view, reg, unit); + else Error("Error converting property %s to unit %s", view, unit); + } + } + err = pcilib_convert_value_type(handle, &val, PCILIB_TYPE_STRING); - if (err) Error("Error converting view %s of register %s to string", unit, reg); + if (err) { + if (reg) Error("Error converting view %s of register %s to string", view); + else Error("Error converting property %s to string", view); + } - printf("%s = %s\n", reg, val.sval); + printf("%s = %s", (reg?reg:view), val.sval); + if ((val.unit)&&(strcasecmp(val.unit, "name"))) + printf(" %s", val.unit); + printf("\n"); } else { + pcilib_register_t regid = pcilib_find_register(handle, bank, reg); bank_id = pcilib_find_register_bank_by_addr(handle, model_info->registers[regid].bank); format = model_info->banks[bank_id].format; if (!format) format = "%lu"; @@ -1362,14 +1457,12 @@ int WriteRegisterRange(pcilib_t *handle, const pcilib_model_description_t *model } -int WriteRegister(pcilib_t *handle, const pcilib_model_description_t *model_info, const char *bank, const char *reg, const char *unit, char **data) { +int WriteRegister(pcilib_t *handle, const pcilib_model_description_t *model_info, const char *bank, const char *reg, const char *view, const char *unit, char **data) { int err = 0; pcilib_value_t val = {0}; pcilib_register_value_t value, verify; - pcilib_register_t regid = pcilib_find_register(handle, bank, reg); - if (regid == PCILIB_REGISTER_INVALID) Error("Can't find register (%s) from bank (%s)", reg, bank?bank:"autodetected"); /* pcilib_register_bank_t bank_id; @@ -1384,11 +1477,23 @@ int WriteRegister(pcilib_t *handle, const pcilib_model_description_t *model_info err = pcilib_set_value_from_static_string(handle, &val, *data); if (err) Error("Error (%i) setting value", err); - if (unit) { - err = pcilib_write_register_view(handle, bank, reg, unit, &val); - if (err) Error("Error writting view %s of register %s", unit, reg); - printf("%s is written\n ", reg); + if (view) { + if (unit) + val.unit = unit; + + if (reg) { + err = pcilib_write_register_view(handle, bank, reg, view, &val); + if (err) Error("Error writting view %s of register %s", view, reg); + printf("%s is written\n ", reg); + } else { + err = pcilib_set_property(handle, view, &val); + if (err) Error("Error setting property %s", view); + printf("%s is written\n ", view); + } } else { + pcilib_register_t regid = pcilib_find_register(handle, bank, reg); + if (regid == PCILIB_REGISTER_INVALID) Error("Can't find register (%s) from bank (%s)", reg, bank?bank:"autodetected"); + value = pcilib_get_value_as_register_value(handle, &val, &err); if (err) Error("Error (%i) parsing data value (%s)", *data); @@ -2758,6 +2863,7 @@ int main(int argc, char **argv) { pcilib_bar_t bar = PCILIB_BAR_DETECT; const char *addr = NULL; const char *reg = NULL; + const char *view = NULL; const char *unit = NULL; const char *bank = NULL; char **data = NULL; @@ -2767,6 +2873,7 @@ int main(int argc, char **argv) { const char *use = NULL; const char *lock = NULL; const char *info_target = NULL; + const char *list_target = NULL; size_t block = (size_t)-1; pcilib_irq_type_t irq_type = PCILIB_IRQ_TYPE_ALL; pcilib_irq_hw_source_t irq_source = PCILIB_IRQ_SOURCE_DEFAULT; @@ -2813,6 +2920,9 @@ int main(int argc, char **argv) { if (mode == MODE_LIST) details++; else if (mode != MODE_INVALID) Usage(argc, argv, "Multiple operations are not supported"); + if (optarg) list_target = optarg; + else if ((optind < argc)&&(argv[optind][0] != '-')) list_target = argv[optind++]; + mode = MODE_LIST; break; case OPT_RESET: @@ -3302,17 +3412,25 @@ int main(int argc, char **argv) { num_offset = dma_channel + 3; itmp -= 3; } - + if (bank) { if (strncmp(num_offset, bank, itmp)) Usage(argc, argv, "Conflicting DMA channels are specified in mode parameter (%s) and bank parameter (%s)", dma_channel, bank); } - + if (!isnumber_n(num_offset, itmp)) Usage(argc, argv, "Invalid DMA channel (%s) is specified", dma_channel); dma = atoi(num_offset); } break; + case MODE_LIST: + if (bank&&list_target) { + if (strcmp(list_target, bank)) + Usage(argc, argv, "Conflicting banks are specified in list parameter (%s) and bank parameter (%s)", list_target, bank); + } else if (bank) { + list_target = bank; + } + break; default: if (argc > optind) Usage(argc, argv, "Invalid non-option parameters are supplied"); } @@ -3359,24 +3477,35 @@ int main(int argc, char **argv) { } } } else { - unit = strchr(addr, '/'); - if (!unit) unit = strchr(addr, ':'); - if (unit) { - char *reg_name; - size_t reg_size = strlen(addr) - strlen(unit); - reg_name = alloca(reg_size + 1); - memcpy(reg_name, addr, reg_size); - reg_name[reg_size] = 0; - reg = reg_name; - unit++; + view = strchr(addr, '/'); + unit = strchr((view?view:addr), ':'); + + if (view||unit) { + size_t reg_size = strlen(addr) - strlen(view?view:unit); + if (reg_size) reg = strndupa(addr, reg_size); + else reg = NULL; + + if ((reg)&&(view)) view++; + if (unit) unit++; + + if (view&&unit) { + view = strndupa(view, strlen(view) - strlen(unit) - 1); + } else if ((reg)&&(unit)) { + view = unit; + unit = NULL; + } } else { reg = addr; } - if (pcilib_find_register(handle, bank, reg) == PCILIB_REGISTER_INVALID) { - Usage(argc, argv, "Invalid address (%s) is specified", addr); + if (reg) { + if (pcilib_find_register(handle, bank, reg) == PCILIB_REGISTER_INVALID) { + Usage(argc, argv, "Invalid address (%s) is specified", addr); + } else { + ++mode; + } } else { - ++mode; + mode += 2; } } } @@ -3458,7 +3587,10 @@ int main(int argc, char **argv) { Info(handle, model_info, info_target); break; case MODE_LIST: - List(handle, model_info, bank, details); + if ((list_target)&&(*list_target == '/')) + ListProperties(handle, list_target, details); + else + List(handle, model_info, list_target, details); break; case MODE_BENCHMARK: Benchmark(handle, amode, dma, bar, start, size_set?size:0, access, iterations); @@ -3475,14 +3607,16 @@ int main(int argc, char **argv) { } break; case MODE_READ_REGISTER: - if ((reg)||(!addr)) ReadRegister(handle, model_info, bank, reg, unit); + case MODE_READ_PROPERTY: + if ((reg)||(view)||(!addr)) ReadRegister(handle, model_info, bank, reg, view, unit); else ReadRegisterRange(handle, model_info, bank, start, addr_shift, size, ofile); break; case MODE_WRITE: WriteData(handle, amode, dma, bar, start, size, access, endianess, data, verify); break; case MODE_WRITE_REGISTER: - if (reg) WriteRegister(handle, model_info, bank, reg, unit, data); + case MODE_WRITE_PROPERTY: + if (reg||view) WriteRegister(handle, model_info, bank, reg, view, unit, data); else WriteRegisterRange(handle, model_info, bank, start, addr_shift, size, data); break; case MODE_RESET: diff --git a/views/CMakeLists.txt b/views/CMakeLists.txt index 646a982..0e0c20b 100644 --- a/views/CMakeLists.txt +++ b/views/CMakeLists.txt @@ -8,6 +8,6 @@ include_directories( ${UTHASH_INCLUDE_DIRS} ) -set(HEADERS ${HEADERS} enum.h transform.h) +set(HEADERS ${HEADERS} enum.h transform.h register.h) -add_library(views STATIC enum.c transform.c) +add_library(views STATIC enum.c transform.c register.c) diff --git a/views/enum.c b/views/enum.c index d712c71..e049c43 100644 --- a/views/enum.c +++ b/views/enum.c @@ -34,7 +34,7 @@ static int pcilib_enum_view_read(pcilib_t *ctx, pcilib_view_context_t *view_ctx, return pcilib_set_value_from_static_string(ctx, val, v->names[i].name); } - return pcilib_set_value_from_int(ctx, val, regval); + return pcilib_set_value_from_register_value(ctx, val, regval); } static int pcilib_enum_view_write(pcilib_t *ctx, pcilib_view_context_t *view_ctx, pcilib_register_value_t *regval, const pcilib_value_t *val) { diff --git a/views/register.c b/views/register.c new file mode 100644 index 0000000..8256a4a --- /dev/null +++ b/views/register.c @@ -0,0 +1,65 @@ +#define _PCILIB_VIEW_ENUM_C + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> + +#include <uthash.h> + +#include "error.h" +#include "version.h" +#include "model.h" +#include "enum.h" +#include "view.h" +#include "value.h" +#include "register.h" + + +static void pcilib_register_view_free(pcilib_t *ctx, pcilib_view_description_t *view) { + if (view->name) + free((void*)view->name); + free(view); +} + +static int pcilib_register_view_read(pcilib_t *ctx, pcilib_view_context_t *view_ctx, pcilib_register_value_t dummy, pcilib_value_t *val) { + int err; + + const pcilib_model_description_t *model_info = pcilib_get_model_description(ctx); + pcilib_register_view_description_t *v = (pcilib_register_view_description_t*)(model_info->views[view_ctx->view]); + + if (v->view) { + return pcilib_read_register_view(ctx, v->bank, v->reg, v->view, val); + } else { + pcilib_register_value_t regval; + + err = pcilib_read_register(ctx, v->bank, v->reg, ®val); + if (err) return err; + + return pcilib_set_value_from_register_value(ctx, val, regval); + } +} + +static int pcilib_register_view_write(pcilib_t *ctx, pcilib_view_context_t *view_ctx, pcilib_register_value_t *dummy, const pcilib_value_t *val) { + int err; + + const pcilib_model_description_t *model_info = pcilib_get_model_description(ctx); + pcilib_register_view_description_t *v = (pcilib_register_view_description_t*)(model_info->views[view_ctx->view]); + + if (v->view) { + return pcilib_write_register_view(ctx, v->bank, v->reg, v->view, val); + } else { + pcilib_register_value_t regval; + + regval = pcilib_get_value_as_register_value(ctx, val, &err); + if (err) return err; + + return pcilib_write_register(ctx, v->bank, v->reg, regval); + } + + return err; +} + +const pcilib_view_api_description_t pcilib_register_view_api = + { PCILIB_VERSION, sizeof(pcilib_register_view_description_t), NULL, NULL, pcilib_register_view_free, pcilib_register_view_read, pcilib_register_view_write }; diff --git a/views/register.h b/views/register.h new file mode 100644 index 0000000..e527846 --- /dev/null +++ b/views/register.h @@ -0,0 +1,19 @@ +#ifndef _PCILIB_VIEW_REGISTER_H +#define _PCILIB_VIEW_REGISTER_H + +#include <pcilib.h> +#include <pcilib/view.h> + +typedef struct { + pcilib_view_description_t base; + const char *view; + const char *reg; + const char *bank; +} pcilib_register_view_description_t; + + +#ifndef _PCILIB_VIEW_REGISTER_C +extern const pcilib_view_api_description_t pcilib_register_view_api; +#endif /* _PCILIB_VIEW_REGISTER_C */ + +#endif /* _PCILIB_VIEW_REGISTER_H */ |