summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuren A. Chilingaryan <csa@suren.me>2015-10-18 03:47:47 +0200
committerSuren A. Chilingaryan <csa@suren.me>2015-10-18 03:47:47 +0200
commitc8628b2a715a7cfaaccbd7e403cd1c6c76b918cd (patch)
tree53971a137e5d0e32ad7219f1d2fd01559c0a6ff3
parent2e9457b666a303fab83aa17e33624f39de9a1dd7 (diff)
downloadpcitool-c8628b2a715a7cfaaccbd7e403cd1c6c76b918cd.tar.gz
pcitool-c8628b2a715a7cfaaccbd7e403cd1c6c76b918cd.tar.bz2
pcitool-c8628b2a715a7cfaaccbd7e403cd1c6c76b918cd.tar.xz
pcitool-c8628b2a715a7cfaaccbd7e403cd1c6c76b918cd.zip
Support properties of arbitrary type
-rw-r--r--pcilib/CMakeLists.txt4
-rw-r--r--pcilib/bank.c35
-rw-r--r--pcilib/bank.h54
-rw-r--r--pcilib/error.h1
-rw-r--r--pcilib/pci.c8
-rw-r--r--pcilib/pci.h10
-rw-r--r--pcilib/pcilib.h65
-rw-r--r--pcilib/property.c207
-rw-r--r--pcilib/property.h30
-rw-r--r--pcilib/register.c67
-rw-r--r--pcilib/register.h34
-rw-r--r--pcilib/unit.c41
-rw-r--r--pcilib/unit.h39
-rw-r--r--pcilib/value.c1
-rw-r--r--pcilib/view.c100
-rw-r--r--pcilib/view.h36
-rw-r--r--pcilib/xml.c7
-rw-r--r--pcitool/cli.c210
-rw-r--r--views/CMakeLists.txt4
-rw-r--r--views/enum.c2
-rw-r--r--views/register.c65
-rw-r--r--views/register.h19
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, &regvalue);
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, &regval);
+ 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 */