From fa54d4c2ca8ffcece7a9c6e9c784e2150cbed78b Mon Sep 17 00:00:00 2001
From: "Suren A. Chilingaryan" <csa@suren.me>
Date: Sun, 18 Oct 2015 07:36:47 +0200
Subject: Support reading/writting register views by id

---
 docs/ToDo         |  7 +++--
 pcilib/bank.c     | 19 +++++++++++++-
 pcilib/bank.h     |  4 ++-
 pcilib/pcilib.h   |  6 +++++
 pcilib/property.c | 14 ++++++++++
 pcilib/register.c | 29 +++++++++++++++++++++
 pcilib/view.c     | 73 ++++++++++++++++++++++++++++++++++++++++-----------
 pcilib/view.h     | 14 ++++++++++
 pcilib/xml.c      | 28 +++++++++++++++++---
 pcilib/xml.h      |  3 +++
 pcitool/cli.c     | 78 +++++++++++++++++++++++++++++++++++++++++++++----------
 11 files changed, 236 insertions(+), 39 deletions(-)

diff --git a/docs/ToDo b/docs/ToDo
index 5575c00..2b7b741 100644
--- a/docs/ToDo
+++ b/docs/ToDo
@@ -1,16 +1,15 @@
 High Priority (we would need it for IPE Camera)
 =============
  1. Join multiple XML files and on error use simplified XSD scheme on all files to find the file causing error
- 2. Universal tree-style api to access the independent views, frontend all registers as well (pci -l /register; pci -r /register/reg1; pci -r /sensor/width;) Unit is path of the view /view[:unit] or just /unit for register vies
- 3. Information on bank and the view values in the pci -i <reg>, show listing of enum values (shall we have a type associated with the view: enum, range, ...?)
- 4. Integrate hash tables for views, units, and registers
+ 2. Information on bank and the view values in the pci -i <reg>, show listing of enum values (shall we have a type associated with the view: enum, range, ...?)
  
 Normal Priority (it would make just few things a bit easier)
 ===============
  1. Implement pcilib_configure_autotrigger
  2. Provide OR and AND operations on registers in cli
  3. Support writting a data from a binary file in cli
- 
+ 4. Support Python-scripts in a views, we need to provide python function to read registers/properties...
+
 Low Priority (only as generalization for other projects)
 ============
  1. Shall we allow overriding of registers?
diff --git a/pcilib/bank.c b/pcilib/bank.c
index ec38c82..efaa810 100644
--- a/pcilib/bank.c
+++ b/pcilib/bank.c
@@ -242,7 +242,6 @@ pcilib_register_t pcilib_find_register(pcilib_t *ctx, const char *bank, const ch
     return PCILIB_REGISTER_INVALID;
 };
 
-
 pcilib_register_protocol_t pcilib_find_register_protocol_by_addr(pcilib_t *ctx, pcilib_register_protocol_addr_t protocol) {
     pcilib_register_protocol_t i;
 
@@ -272,3 +271,21 @@ pcilib_register_protocol_t pcilib_find_register_protocol(pcilib_t *ctx, const ch
 
     return pcilib_find_register_protocol_by_name(ctx, protocol);
 }
+
+int pcilib_get_register_bank_attr_by_id(pcilib_t *ctx, pcilib_register_bank_t bank, const char *attr, pcilib_value_t *val) {
+    assert(bank < ctx->num_banks);
+
+    return pcilib_get_xml_attr(ctx, ctx->bank_ctx[bank]->xml, attr, val);
+}
+
+int pcilib_get_register_bank_attr(pcilib_t *ctx, const char *bankname, const char *attr, pcilib_value_t *val) {
+    pcilib_register_bank_t bank;
+
+    bank = pcilib_find_register_bank_by_name(ctx, bankname);
+    if (bank == PCILIB_REGISTER_BANK_INVALID) {
+        pcilib_error("Bank (%s) is not found", bankname);
+        return PCILIB_ERROR_NOTFOUND;
+    }
+
+    return pcilib_get_register_bank_attr_by_id(ctx, bank, attr, val);
+}
diff --git a/pcilib/bank.h b/pcilib/bank.h
index 39dd79c..ba149b9 100644
--- a/pcilib/bank.h
+++ b/pcilib/bank.h
@@ -153,7 +153,6 @@ int pcilib_add_register_protocols(pcilib_t *ctx, pcilib_model_modification_flags
  */
 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);
@@ -162,6 +161,9 @@ pcilib_register_protocol_t pcilib_find_register_protocol_by_addr(pcilib_t *ctx,
 pcilib_register_protocol_t pcilib_find_register_protocol_by_name(pcilib_t *ctx, const char *name);
 pcilib_register_protocol_t pcilib_find_register_protocol(pcilib_t *ctx, const char *name);
 
+
+int pcilib_get_register_bank_attr_by_id(pcilib_t *ctx, pcilib_register_bank_t bank, const char *attr, pcilib_value_t *val);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/pcilib/pcilib.h b/pcilib/pcilib.h
index e4fdf6d..b2837a9 100644
--- a/pcilib/pcilib.h
+++ b/pcilib/pcilib.h
@@ -241,6 +241,8 @@ int pcilib_read_register(pcilib_t *ctx, const char *bank, const char *regname, p
 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);
+int pcilib_read_register_view_by_id(pcilib_t *ctx, pcilib_register_t reg, const char *view, pcilib_value_t *val);
+int pcilib_write_register_view_by_id(pcilib_t *ctx, pcilib_register_t reg, const char *view, const pcilib_value_t *valarg);
 
 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);
@@ -259,6 +261,10 @@ 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_get_property_attr(pcilib_t *ctx, const char *prop, const char *attr, pcilib_value_t *val);
+int pcilib_get_register_attr_by_id(pcilib_t *ctx, pcilib_register_t reg, const char *attr, pcilib_value_t *val);
+int pcilib_get_register_attr(pcilib_t *ctx, const char *bank, const char *regname, const char *attr, pcilib_value_t *val);
+int pcilib_get_register_bank_attr(pcilib_t *ctx, const char *bankname, const char *attr, 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
index 1db6dd3..276360a 100644
--- a/pcilib/property.c
+++ b/pcilib/property.c
@@ -206,3 +206,17 @@ 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) {
     return pcilib_write_register_view(ctx, NULL, NULL, prop, val);
 }
+
+int pcilib_get_property_attr(pcilib_t *ctx, const char *prop, const char *attr, pcilib_value_t *val) {
+    pcilib_view_context_t *view_ctx;
+
+    view_ctx = pcilib_find_view_context_by_name(ctx, prop);
+    if (!view_ctx) {
+        pcilib_error("The specified property (%s) is not found", prop);
+        return PCILIB_ERROR_NOTFOUND;
+    }
+
+    if (!view_ctx->xml) return NULL;
+
+    return pcilib_get_xml_attr(ctx, view_ctx->xml, attr, val);
+}
diff --git a/pcilib/register.c b/pcilib/register.c
index 30505ae..2752d47 100644
--- a/pcilib/register.c
+++ b/pcilib/register.c
@@ -385,3 +385,32 @@ int pcilib_write_register(pcilib_t *ctx, const char *bank, const char *regname,
 
     return pcilib_write_register_by_id(ctx, reg, value);
 }
+
+
+int pcilib_get_register_attr_by_id(pcilib_t *ctx, pcilib_register_t reg, const char *attr, pcilib_value_t *val) {
+    int err;
+
+    assert(reg < ctx->num_reg);
+
+    err = pcilib_get_xml_attr(ctx, ctx->register_ctx[reg].xml, attr, val);
+/*
+        // Shall we return from parrent register if not found?
+    if ((err == PCILIB_ERROR_NOTFOUND)&&(ctx->registers[reg].type == PCILIB_REGISTER_TYPE_BITS)) {
+        pcilib_register_t parent = pcilib_find_standard_register_by_addr(ctx, ctx->registers[reg].addr);
+        err = pcilib_get_xml_attr(ctx, ctx->register_ctx[parent].xml, attr, val);
+    }
+*/
+    return err;
+}
+
+int pcilib_get_register_attr(pcilib_t *ctx, const char *bank, const char *regname, const char *attr, pcilib_value_t *val) {
+    pcilib_register_t reg;
+    
+    reg = pcilib_find_register(ctx, bank, regname);
+    if (reg == PCILIB_REGISTER_INVALID) {
+        pcilib_error("Register (%s) is not found", regname);
+        return PCILIB_ERROR_NOTFOUND;
+    }
+
+    return pcilib_get_register_attr_by_id(ctx, reg, attr, val);
+}
diff --git a/pcilib/view.c b/pcilib/view.c
index 8df5fc4..d14e8aa 100644
--- a/pcilib/view.c
+++ b/pcilib/view.c
@@ -10,7 +10,7 @@
 #include "error.h"
 #include "value.h"
 
-int pcilib_add_views(pcilib_t *ctx, size_t n, const pcilib_view_description_t *desc) {
+int pcilib_add_views_custom(pcilib_t *ctx, size_t n, const pcilib_view_description_t *desc, pcilib_view_context_t **refs) {
     size_t i;
     void *ptr;
 
@@ -76,6 +76,8 @@ int pcilib_add_views(pcilib_t *ctx, size_t n, const pcilib_view_description_t *d
         view_ctx->view = ctx->num_views + i;
         view_ctx->name = v->name;
 
+        if (refs) refs[i] = view_ctx;
+
         HASH_ADD_KEYPTR(hh, ctx->view_hash, view_ctx->name, strlen(view_ctx->name), view_ctx);
         ctx->views[ctx->num_views + i] = cur;
 
@@ -88,6 +90,11 @@ int pcilib_add_views(pcilib_t *ctx, size_t n, const pcilib_view_description_t *d
     return 0;
 }
 
+int pcilib_add_views(pcilib_t *ctx, size_t n, const pcilib_view_description_t *desc) {
+    return pcilib_add_views_custom(ctx, n, desc, NULL);
+}
+
+
 void pcilib_clean_views(pcilib_t *ctx, pcilib_view_t start) {
     pcilib_view_t i;
     pcilib_view_context_t *view_ctx, *tmp;
@@ -201,17 +208,16 @@ 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, const char *unit_cname, int write_direction, pcilib_view_configuration_t *cfg) {
+static int pcilib_detect_view_configuration(pcilib_t *ctx, pcilib_register_t reg, const char *view_cname, const char *unit_cname, int write_direction, pcilib_view_configuration_t *cfg) {
     int err = 0;
     pcilib_view_t view;
+    const char *regname;
     pcilib_view_context_t *view_ctx;
     pcilib_unit_transform_t *trans = NULL;
-    pcilib_register_t reg = PCILIB_REGISTER_INVALID;
 
     char *view_name = alloca(strlen(view_cname) + 1);
     const char *unit_name;
 
-
     strcpy(view_name, view_cname);
 
     if (unit_cname) unit_name = unit_cname;
@@ -223,14 +229,10 @@ static int pcilib_detect_view_configuration(pcilib_t *ctx, const char *bank, con
         }
     }
 
+    if (reg == PCILIB_REGISTER_INVALID) regname = NULL;
+    else regname = ctx->registers[reg].name;
 
     if (regname) {
-	reg = pcilib_find_register(ctx, bank, regname);
-	if (reg == PCILIB_REGISTER_INVALID) {
-	    pcilib_error("Can't find the specified register %s", regname);
-	    return PCILIB_ERROR_NOTFOUND;
-	}
-	
 	if (unit_name) view_ctx = pcilib_find_register_view_context_by_name(ctx, reg, view_name);
 	else err = pcilib_detect_register_view_and_unit(ctx, reg, view_name, write_direction, &view_ctx, &trans);
 
@@ -267,15 +269,19 @@ static int pcilib_detect_view_configuration(pcilib_t *ctx, const char *bank, con
     return 0;
 }
 
-
-int pcilib_read_register_view(pcilib_t *ctx, const char *bank, const char *regname, const char *view, pcilib_value_t *val) {
+int pcilib_read_register_view_by_id(pcilib_t *ctx, pcilib_register_t reg, const char *view, pcilib_value_t *val) {
     int err;
 
+    const char *regname;
+
     pcilib_view_description_t *v;
     pcilib_view_configuration_t cfg;
     pcilib_register_value_t regvalue = 0;
 
-    err = pcilib_detect_view_configuration(ctx, bank, regname, view, NULL, 0, &cfg);
+    if (reg == PCILIB_REGISTER_INVALID) regname = NULL;
+    else regname = ctx->registers[reg].name;
+
+    err = pcilib_detect_view_configuration(ctx, reg, view, NULL, 0, &cfg);
     if (err) return err;
 
     v = ctx->views[cfg.view->view];
@@ -321,16 +327,37 @@ int pcilib_read_register_view(pcilib_t *ctx, const char *bank, const char *regna
     return 0;
 }
 
+int pcilib_read_register_view(pcilib_t *ctx, const char *bank, const char *regname, const char *view, pcilib_value_t *val) {
+    pcilib_register_t reg;
+
+    if (regname) {
+	reg = pcilib_find_register(ctx, bank, regname);
+	if (reg == PCILIB_REGISTER_INVALID) {
+	    pcilib_error("Register (%s) is not found", regname);
+	    return PCILIB_ERROR_NOTFOUND;
+	}
+    } else {
+        reg = PCILIB_REGISTER_INVALID;
+    }
+
+    return pcilib_read_register_view_by_id(ctx, reg, view, val);
+}
+
 
-int pcilib_write_register_view(pcilib_t *ctx, const char *bank, const char *regname, const char *view, const pcilib_value_t *valarg) {
+int pcilib_write_register_view_by_id(pcilib_t *ctx, pcilib_register_t reg, const char *view, const pcilib_value_t *valarg) {
     int err;
     pcilib_value_t val = {0};
 
+    const char *regname;
+
     pcilib_view_description_t *v;
     pcilib_view_configuration_t cfg;
     pcilib_register_value_t regvalue = 0;
 
-    err = pcilib_detect_view_configuration(ctx, bank, regname, view, valarg->unit, 1, &cfg);
+    if (reg == PCILIB_REGISTER_INVALID) regname = NULL;
+    else regname = ctx->registers[reg].name;
+
+    err = pcilib_detect_view_configuration(ctx, reg, view, valarg->unit, 1, &cfg);
     if (err) return err;
 
     v = ctx->views[cfg.view->view];
@@ -378,3 +405,19 @@ int pcilib_write_register_view(pcilib_t *ctx, const char *bank, const char *regn
 
     return 0;
 }
+
+int pcilib_write_register_view(pcilib_t *ctx, const char *bank, const char *regname, const char *view, const pcilib_value_t *val) {
+    pcilib_register_t reg;
+
+    if (regname) {
+	reg = pcilib_find_register(ctx, bank, regname);
+	if (reg == PCILIB_REGISTER_INVALID) {
+	    pcilib_error("Register (%s) is not found", regname);
+	    return PCILIB_ERROR_NOTFOUND;
+	}
+    } else {
+        reg = PCILIB_REGISTER_INVALID;
+    }
+
+    return pcilib_write_register_view_by_id(ctx, reg, view, val);
+}
diff --git a/pcilib/view.h b/pcilib/view.h
index 606a688..ec775b7 100644
--- a/pcilib/view.h
+++ b/pcilib/view.h
@@ -38,6 +38,7 @@ struct pcilib_view_description_s {
 struct pcilib_view_context_s {
     const char *name;
     pcilib_view_t view;
+    pcilib_xml_node_t *xml;
     UT_hash_handle hh;
 };
 
@@ -57,6 +58,19 @@ extern "C" {
  */
 int pcilib_add_views(pcilib_t *ctx, size_t n, const pcilib_view_description_t *desc);
 
+/**
+ * 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
+ * @param[out] refs - fills allocated view contexts. On error context is undefined.
+ * @return - error or 0 on success
+ */
+int pcilib_add_views_custom(pcilib_t *ctx, size_t n, const pcilib_view_description_t *desc, pcilib_view_context_t **refs);
+
 /**
  * Destroys data associated with views. This is an internal function and will
  * be called during clean-up.
diff --git a/pcilib/xml.c b/pcilib/xml.c
index 4ecd16d..09e7cba 100644
--- a/pcilib/xml.c
+++ b/pcilib/xml.c
@@ -516,6 +516,7 @@ static int pcilib_xml_create_transform_view(pcilib_t *ctx, xmlXPathContextPtr xp
     int err;
     xmlAttrPtr cur;
     const char *value, *name;
+    pcilib_view_context_t *view_ctx;
 
     pcilib_transform_view_description_t desc = {0};
 
@@ -554,8 +555,11 @@ static int pcilib_xml_create_transform_view(pcilib_t *ctx, xmlXPathContextPtr xp
         } 
     }
 
+    err = pcilib_add_views_custom(ctx, 1, (pcilib_view_description_t*)&desc, &view_ctx);
+    if (err) return err;
 
-    return pcilib_add_views(ctx, 1, (pcilib_view_description_t*)&desc);
+    view_ctx->xml = node;
+    return 0;
 }
 
 
@@ -626,6 +630,7 @@ static int pcilib_xml_create_enum_view(pcilib_t *ctx, xmlXPathContextPtr xpath,
     xmlXPathObjectPtr nodes;
     xmlNodeSetPtr nodeset;
 
+    pcilib_view_context_t *view_ctx;
     pcilib_enum_view_description_t desc = {0};
 
     desc.base.type = PCILIB_TYPE_STRING;
@@ -676,9 +681,13 @@ static int pcilib_xml_create_enum_view(pcilib_t *ctx, xmlXPathContextPtr xpath,
     xmlXPathFreeObject(nodes);
 
 
-    err = pcilib_add_views(ctx, 1, (pcilib_view_description_t*)&desc);
-    if (err) free(desc.names);
-    return err;
+    err = pcilib_add_views_custom(ctx, 1, (pcilib_view_description_t*)&desc, &view_ctx);
+    if (err) {
+        free(desc.names);
+        return err;
+    }
+    view_ctx->xml = node;
+    return 0;
 }
 
 static int pcilib_xml_parse_unit_transform(pcilib_t *ctx, xmlXPathContextPtr xpath, xmlDocPtr doc, xmlNodePtr node, pcilib_unit_transform_t *desc) {
@@ -1064,3 +1073,14 @@ void pcilib_free_xml(pcilib_t *ctx) {
         xmlMemoryDump();
     */
 }
+
+int pcilib_get_xml_attr(pcilib_t *ctx, pcilib_xml_node_t *node, const char *attr, pcilib_value_t *val) {
+    xmlAttr *prop;
+    xmlChar *str;
+
+    prop = xmlHasProp(node, BAD_CAST attr);
+    if ((!prop)||(!prop->children)) return PCILIB_ERROR_NOTFOUND;
+
+    str = prop->children->content;
+    return pcilib_set_value_from_static_string(ctx, val, (const char*)str);
+}
diff --git a/pcilib/xml.h b/pcilib/xml.h
index 6ad8676..10bc154 100644
--- a/pcilib/xml.h
+++ b/pcilib/xml.h
@@ -59,6 +59,9 @@ void pcilib_free_xml(pcilib_t *ctx);
 */
 int pcilib_process_xml(pcilib_t *ctx, const char *location);
 
+int pcilib_get_xml_attr(pcilib_t *ctx, pcilib_xml_node_t *node, const char *attr, pcilib_value_t *val);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/pcitool/cli.c b/pcitool/cli.c
index 2956306..71919a2 100644
--- a/pcitool/cli.c
+++ b/pcitool/cli.c
@@ -78,6 +78,7 @@ typedef enum {
     MODE_READ,
     MODE_READ_REGISTER,
     MODE_READ_PROPERTY,
+    MODE_READ_ATTR,
     MODE_WRITE,
     MODE_WRITE_REGISTER,
     MODE_WRITE_PROPERTY,
@@ -261,12 +262,21 @@ void Usage(int argc, char *argv[], const char *format, ...) {
 "  Modes:\n"
 "   -i [target]			- Device or Register (target) Info\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"
+"   -r <addr|dmaX|reg|prop>	- Read Data/Register/Property\n"
+"   -w <addr|dmaX|reg|prop>	- Write Data/Register/Property\n"
 "   --benchmark <barX|dmaX>	- Performance Evaluation\n"
 "   --reset			- Reset board\n"
 "   --help			- Help message\n"
 "\n"
+"  Property/Register Modes:\n"
+"   -r <reg>/view[:unit]        - Read register view\n"
+"   -w <reg>/view[:unit]        - Write register view\n"
+"   -r <reg>/unit               - Read register, detect view based on unit\n"
+"   -w <reg>/unit               - Write register, detect view based on unt\n"
+"   -r <prop>[:unit]            - Read property\n"
+"   -w <prop>[:unit]            - Write property\n"
+"   -r <prop|reg>@attr          - Read register/property attribute\n"
+"\n"
 "  Event Modes:\n"
 "   --trigger [event]		- Trigger Events\n"
 "   -g [event]			- Grab Events\n"
@@ -1203,7 +1213,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 *view, 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, const char *attr) {
     int i;
     int err;
     const char *format;
@@ -1216,9 +1226,29 @@ int ReadRegister(pcilib_t *handle, const pcilib_model_description_t *model_info,
 	// Adding DMA registers
     pcilib_get_dma_description(handle);
 
-    if (reg||view) {
-        if (view) {
-            pcilib_value_t val = {0};
+    if (reg||view||attr) {
+        pcilib_value_t val = {0};
+        if (attr) {
+            if (reg) err = pcilib_get_register_attr(handle, bank, reg, attr, &val);
+            else if (view) err = pcilib_get_property_attr(handle, view, attr, &val);
+            else if (bank) err = pcilib_get_register_bank_attr(handle, bank, attr, &val);
+            else err = PCILIB_ERROR_INVALID_ARGUMENT;
+
+            if (err) {
+                if (err == PCILIB_ERROR_NOTFOUND)
+                    Error("Attribute %s is not found", attr);
+                else
+                    Error("Error (%i) reading attribute %s", err, attr);
+            }
+
+            err = pcilib_convert_value_type(handle, &val, PCILIB_TYPE_STRING);
+            if (err) Error("Error converting attribute %s to string", attr);
+
+            printf("%s = %s", attr, val.sval);
+            if ((val.unit)&&(strcasecmp(val.unit, "name")))
+                printf(" %s", val.unit);
+            printf(" (for %s)\n", (reg?reg:(view?view:bank)));
+        } else if (view) {
             if (reg) {
                 err = pcilib_read_register_view(handle, bank, reg, view, &val);
                 if (err) Error("Error reading view %s of register %s", view, reg);
@@ -2865,6 +2895,7 @@ int main(int argc, char **argv) {
     const char *reg = NULL;
     const char *view = NULL;
     const char *unit = NULL;
+    const char *attr = NULL;
     const char *bank = NULL;
     char **data = NULL;
     const char *event = NULL;
@@ -3477,12 +3508,23 @@ int main(int argc, char **argv) {
 		}
 	    }
 	} else {
-            view = strchr(addr, '/');
-            unit = strchr((view?view:addr), ':');
+	    const char *spec;
+
+            attr = strchr(addr, '@');
+            if (attr) {
+                size_t spec_size = strlen(addr) - strlen(attr);
+                spec = strndupa(addr, spec_size);
+                attr++;
+            } else {
+                spec = addr;
+            }
+
+            view = strchr(spec, '/');
+            unit = strchr((view?view:spec), ':');
 
             if (view||unit) {
-                size_t reg_size = strlen(addr) - strlen(view?view:unit);
-                if (reg_size) reg = strndupa(addr, reg_size);
+                size_t reg_size = strlen(spec) - strlen(view?view:unit);
+                if (reg_size) reg = strndupa(spec, reg_size);
                 else reg = NULL;
 
                 if ((reg)&&(view)) view++;
@@ -3495,15 +3537,22 @@ int main(int argc, char **argv) {
                     unit = NULL;
                 }
             } else {
-                reg = addr;
+                if (*spec) reg = spec;
+                else reg = NULL;
             }
 
             if (reg) {
 	        if (pcilib_find_register(handle, bank, reg) == PCILIB_REGISTER_INVALID) {
 	            Usage(argc, argv, "Invalid address (%s) is specified", addr);
-	        } else {
-		    ++mode;
 	        }
+	    }
+
+            if (attr) {
+                if (mode == MODE_WRITE)
+                    Error("Writting of attributes is not supported");
+                mode += 3;
+            } else if (reg) {
+		mode += 1;
 	    } else {
 	        mode += 2;
 	    }
@@ -3608,7 +3657,8 @@ int main(int argc, char **argv) {
      break;
      case MODE_READ_REGISTER:
      case MODE_READ_PROPERTY:
-        if ((reg)||(view)||(!addr)) ReadRegister(handle, model_info, bank, reg, view, unit);
+     case MODE_READ_ATTR:
+        if ((reg)||(view)||(attr)||(!addr)) ReadRegister(handle, model_info, bank, reg, view, unit, attr);
 	else ReadRegisterRange(handle, model_info, bank, start, addr_shift, size, ofile);
      break;
      case MODE_WRITE:
-- 
cgit v1.2.3