Browse Source

Support properties of arbitrary type

Suren A. Chilingaryan 8 years ago
parent
commit
c8628b2a71
22 changed files with 871 additions and 168 deletions
  1. 2 2
      pcilib/CMakeLists.txt
  2. 21 14
      pcilib/bank.c
  3. 52 2
      pcilib/bank.h
  4. 1 0
      pcilib/error.h
  5. 4 4
      pcilib/pci.c
  6. 0 10
      pcilib/pci.h
  7. 45 20
      pcilib/pcilib.h
  8. 207 0
      pcilib/property.c
  9. 30 0
      pcilib/property.h
  10. 56 11
      pcilib/register.c
  11. 33 1
      pcilib/register.h
  12. 28 13
      pcilib/unit.c
  13. 36 3
      pcilib/unit.h
  14. 1 0
      pcilib/value.c
  15. 64 36
      pcilib/view.c
  16. 27 9
      pcilib/view.h
  17. 5 2
      pcilib/xml.c
  18. 172 38
      pcitool/cli.c
  19. 2 2
      views/CMakeLists.txt
  20. 1 1
      views/enum.c
  21. 65 0
      views/register.c
  22. 19 0
      views/register.h

+ 2 - 2
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)
 

+ 21 - 14
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;
 }
 

+ 52 - 2
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);

+ 1 - 0
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,

+ 4 - 4
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);

+ 0 - 10
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 */
     

+ 45 - 20
pcilib/pcilib.h

@@ -40,6 +40,12 @@ typedef enum {
     PCILIB_BIG_ENDIAN
 } 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 */
@@ -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);

+ 207 - 0
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);
+}

+ 30 - 0
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?
+

+ 56 - 11
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) {

+ 33 - 1
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
 }

+ 28 - 13
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;
 }
 

+ 36 - 3
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);
 
 

+ 1 - 0
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;
 }

+ 64 - 36
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;
 

+ 27 - 9
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);

+ 5 - 2
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;

+ 172 - 38
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:

+ 2 - 2
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)

+ 1 - 1
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) {

+ 65 - 0
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 };

+ 19 - 0
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 */