diff options
| -rw-r--r-- | docs/ToDo | 7 | ||||
| -rw-r--r-- | pcilib/bank.c | 19 | ||||
| -rw-r--r-- | pcilib/bank.h | 4 | ||||
| -rw-r--r-- | pcilib/pcilib.h | 6 | ||||
| -rw-r--r-- | pcilib/property.c | 14 | ||||
| -rw-r--r-- | pcilib/register.c | 29 | ||||
| -rw-r--r-- | pcilib/view.c | 73 | ||||
| -rw-r--r-- | pcilib/view.h | 14 | ||||
| -rw-r--r-- | pcilib/xml.c | 28 | ||||
| -rw-r--r-- | pcilib/xml.h | 3 | ||||
| -rw-r--r-- | pcitool/cli.c | 78 | 
11 files changed, 236 insertions, 39 deletions
| @@ -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;  }; @@ -58,6 +59,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.   * @param[in,out] ctx - pcilib context 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: | 
