summaryrefslogtreecommitdiffstats
path: root/pcilib
diff options
context:
space:
mode:
authorSuren A. Chilingaryan <csa@suren.me>2016-03-08 02:15:51 +0100
committerSuren A. Chilingaryan <csa@suren.me>2016-03-08 02:15:51 +0100
commiteec7e74d2eccf811f0d25bb40fa514dc47655d67 (patch)
tree1b2c199cb356da7fd299d5b53a632cf9a535ca3e /pcilib
parent92b9df25f5bbb2f70ee2626907e410affc02bdeb (diff)
downloadpcitool-eec7e74d2eccf811f0d25bb40fa514dc47655d67.tar.gz
pcitool-eec7e74d2eccf811f0d25bb40fa514dc47655d67.tar.bz2
pcitool-eec7e74d2eccf811f0d25bb40fa514dc47655d67.tar.xz
pcitool-eec7e74d2eccf811f0d25bb40fa514dc47655d67.zip
Support XML configuration of device models
Diffstat (limited to 'pcilib')
-rw-r--r--pcilib/pci.c26
-rw-r--r--pcilib/xml.c196
-rw-r--r--pcilib/xml.h10
3 files changed, 209 insertions, 23 deletions
diff --git a/pcilib/pci.c b/pcilib/pci.c
index c9cd1d2..6451559 100644
--- a/pcilib/pci.c
+++ b/pcilib/pci.c
@@ -86,13 +86,6 @@ static int pcilib_detect_model(pcilib_t *ctx, const char *model) {
pcilib_add_register_ranges(ctx, PCILIB_MODEL_MODIFICATON_FLAGS_DEFAULT, 0, model_info->ranges);
}
- // Load XML registers
-
- // Check for all installed models
- // memcpy(&ctx->model_info, model, sizeof(pcilib_model_description_t));
- // how we reconcile the banks from event model and dma description? The banks specified in the DMA description should override corresponding banks of events...
-
-
if (!model_info) {
if ((model)&&(strcasecmp(model, "pci"))/*&&(no xml)*/)
return PCILIB_ERROR_NOTFOUND;
@@ -108,6 +101,7 @@ static int pcilib_detect_model(pcilib_t *ctx, const char *model) {
pcilib_t *pcilib_open(const char *device, const char *model) {
int err, xmlerr;
pcilib_t *ctx = malloc(sizeof(pcilib_t));
+ const pcilib_board_info_t *board_info;
const pcilib_driver_version_t *drv_version;
if (!model)
@@ -138,6 +132,17 @@ pcilib_t *pcilib_open(const char *device, const char *model) {
return ctx;
}
+ board_info = pcilib_get_board_info(ctx);
+ if (!board_info) {
+ pcilib_error("Failed to enumerate PCI device");
+ pcilib_close(ctx);
+ return NULL;
+ }
+
+ // Check if model is specified in the XML configuration
+ if (!model)
+ model = pcilib_detect_xml_model(ctx, board_info->vendor_id, board_info->device_id);
+
err = pcilib_init_locking(ctx);
if (err) {
pcilib_error("Error (%i) initializing locking subsystem", err);
@@ -151,6 +156,7 @@ pcilib_t *pcilib_open(const char *device, const char *model) {
pcilib_free_py(ctx);
}
+
ctx->alloc_reg = PCILIB_DEFAULT_REGISTER_SPACE;
ctx->alloc_views = PCILIB_DEFAULT_VIEW_SPACE;
ctx->alloc_units = PCILIB_DEFAULT_UNIT_SPACE;
@@ -179,11 +185,7 @@ pcilib_t *pcilib_open(const char *device, const char *model) {
err = pcilib_detect_model(ctx, model);
if ((err)&&(err != PCILIB_ERROR_NOTFOUND)) {
- const pcilib_board_info_t *board_info = pcilib_get_board_info(ctx);
- if (board_info)
- pcilib_error("Error (%i) configuring model %s (%x:%x)", err, (model?model:""), board_info->vendor_id, board_info->device_id);
- else
- pcilib_error("Error (%i) configuring model %s", err, (model?model:""));
+ pcilib_error("Error (%i) configuring model %s (%x:%x)", err, (model?model:""), board_info->vendor_id, board_info->device_id);
pcilib_close(ctx);
return NULL;
}
diff --git a/pcilib/xml.c b/pcilib/xml.c
index b980a83..467c9c7 100644
--- a/pcilib/xml.c
+++ b/pcilib/xml.c
@@ -49,12 +49,11 @@
#define REGISTERS_PATH ((xmlChar*)"./register") /**< all standard registers nodes */
#define BIT_REGISTERS_PATH ((xmlChar*)"./field") /**< all bits registers nodes */
#define REGISTER_VIEWS_PATH ((xmlChar*)"./view") /**< supported register & field views */
-#define TRANSFORM_VIEWS_PATH ((xmlChar*)"/model/transform") /**< path to complete nodes of views */
+#define TRANSFORM_VIEWS_PATH ((xmlChar*)"/model/transform") /**< path to complete nodes of views */
#define ENUM_VIEWS_PATH ((xmlChar*)"/model/enum") /**< path to complete nodes of views */
#define ENUM_ELEMENTS_PATH ((xmlChar*)"./name") /**< all elements in the enum */
#define UNITS_PATH ((xmlChar*)"/model/unit") /**< path to complete nodes of units */
-#define UNIT_TRANSFORMS_PATH ((xmlChar*)"./transform") /**< all transforms of the unit */
-
+#define UNIT_TRANSFORMS_PATH ((xmlChar*)"./transform") /**< all transforms of the unit */
static const char *pcilib_xml_bank_default_format = "0x%lx";
@@ -892,7 +891,7 @@ static int pcilib_xml_process_document(pcilib_t *ctx, xmlDocPtr doc, xmlXPathCon
return 0;
}
-static int pcilib_xml_load_xsd_file(pcilib_t *ctx, char *xsd_filename, xmlSchemaPtr *schema, xmlSchemaValidCtxtPtr *validator) {
+static int pcilib_xml_load_xsd_file(pcilib_t *ctx, const char *xsd_filename, xmlSchemaPtr *schema, xmlSchemaValidCtxtPtr *validator) {
int err;
xmlSchemaParserCtxtPtr ctxt;
@@ -923,6 +922,7 @@ static int pcilib_xml_load_xsd_file(pcilib_t *ctx, char *xsd_filename, xmlSchema
*validator = xmlSchemaNewValidCtxt(*schema);
if (!*validator) {
xmlErrorPtr xmlerr = xmlGetLastError();
+ xmlSchemaFree(*schema); *schema = NULL;
if (xmlerr) pcilib_error("xmlSchemaNewValidCtxt reported error %d - %s", xmlerr->code, xmlerr->message);
else pcilib_error("Failed to create a validation context");
return PCILIB_ERROR_FAILED;
@@ -931,6 +931,8 @@ static int pcilib_xml_load_xsd_file(pcilib_t *ctx, char *xsd_filename, xmlSchema
err = xmlSchemaSetValidOptions(*validator, XML_SCHEMA_VAL_VC_I_CREATE);
if (err) {
xmlErrorPtr xmlerr = xmlGetLastError();
+ xmlSchemaFreeValidCtxt(*validator); *validator = NULL;
+ xmlSchemaFree(*schema); *schema = NULL;
if (xmlerr) pcilib_error("xmlSchemaSetValidOptions reported error %d - %s", xmlerr->code, xmlerr->message);
else pcilib_error("Failed to configure the validation context to populate default attributes");
return PCILIB_ERROR_FAILED;
@@ -939,8 +941,87 @@ static int pcilib_xml_load_xsd_file(pcilib_t *ctx, char *xsd_filename, xmlSchema
return 0;
}
+/*
+static xmlDocPtr pcilib_xml_load_xml_file(pcilib_t *ctx, const char *xsd_filename, const char *xml_filename) {
+ int err;
+ xmlDocPtr doc;
+ xmlSchemaPtr schema;
+ xmlSchemaValidCtxtPtr validator;
+ xmlParserCtxtPtr parser;
+
+ err = pcilib_xml_load_xsd_file(ctx, xsd_filename, &schema, &validator);
+ if (err) {
+ pcilib_error("Error (%i) parsing the devices schema (%s)", err, xsd_filename);
+ return NULL;
+ }
-static int pcilib_xml_load_xsd(pcilib_t *ctx, char *model_dir) {
+ parser = xmlNewParserCtxt();
+ if (!parser) {
+ xmlErrorPtr xmlerr = xmlGetLastError();
+ xmlSchemaFree(schema);
+ xmlSchemaFreeValidCtxt(validator);
+ if (xmlerr) pcilib_error("xmlNewParserCtxt reported error %d (%s)", xmlerr->code, xmlerr->message);
+ else pcilib_error("Failed to create an XML parser context");
+ return NULL;
+ }
+
+ doc = xmlCtxtReadFile(parser, xml_filename, NULL, 0);
+ if (!doc) {
+ xmlErrorPtr xmlerr = xmlGetLastError();
+ xmlFreeParserCtxt(parser);
+ xmlSchemaFree(schema);
+ xmlSchemaFreeValidCtxt(validator);
+ if (xmlerr) pcilib_error("Error parsing %s, xmlCtxtReadFile reported error %d - %s", xml_filename, xmlerr->code, xmlerr->message);
+ else pcilib_error("Error parsing %s", xml_filename);
+ return NULL;
+ }
+
+ err = xmlSchemaValidateDoc(validator, doc);
+ if (err) {
+ xmlErrorPtr xmlerr = xmlCtxtGetLastError(parser);
+ xmlFreeDoc(doc);
+ xmlFreeParserCtxt(parser);
+ xmlSchemaFree(schema);
+ xmlSchemaFreeValidCtxt(validator);
+ if (xmlerr) pcilib_error("Error validating %s, xmlSchemaValidateDoc reported error %d - %s", xml_filename, xmlerr->code, xmlerr->message);
+ else pcilib_error("Error validating %s", xml_filename);
+ return NULL;
+ }
+
+ xmlFreeParserCtxt(parser);
+ xmlSchemaFree(schema);
+ xmlSchemaFreeValidCtxt(validator);
+
+ return doc;
+}
+*/
+
+static xmlXPathObjectPtr pcilib_xml_eval_xpath_expression(pcilib_t *ctx, xmlDocPtr doc, const xmlChar *query) {
+ xmlXPathContextPtr xpath;
+ xmlXPathObjectPtr nodes;
+
+ xpath = xmlXPathNewContext(doc);
+ if (!xpath) {
+ xmlErrorPtr xmlerr = xmlGetLastError();
+ if (xmlerr) pcilib_error("xmlXpathNewContext reported error %d - %s", xmlerr->code, xmlerr->message);
+ else pcilib_error("Error creating XPath context");
+ return NULL;
+ }
+
+ nodes = xmlXPathEvalExpression(query, xpath);
+ if (!nodes) {
+ xmlErrorPtr xmlerr = xmlGetLastError();
+ xmlXPathFreeContext(xpath);
+ if (xmlerr) pcilib_error("Failed to parse XPath expression %s, xmlXPathEvalExpression reported error %d - %s", query, xmlerr->code, xmlerr->message);
+ else pcilib_error("Failed to parse XPath expression %s", query);
+ return NULL;
+ }
+
+ xmlXPathFreeContext(xpath);
+ return nodes;
+}
+
+static int pcilib_xml_load_xsd(pcilib_t *ctx, const char *model_dir) {
int err;
struct stat st;
@@ -969,7 +1050,7 @@ static int pcilib_xml_load_xsd(pcilib_t *ctx, char *model_dir) {
-static xmlDocPtr pcilib_xml_load_file(pcilib_t *ctx, const char *path, const char *name) {
+static xmlDocPtr pcilib_xml_load_file(pcilib_t *ctx, xmlParserCtxtPtr parser, xmlSchemaValidCtxtPtr validator, const char *path, const char *name) {
int err;
char *full_name;
xmlDocPtr doc;
@@ -982,17 +1063,17 @@ static xmlDocPtr pcilib_xml_load_file(pcilib_t *ctx, const char *path, const cha
sprintf(full_name, "%s/%s", path, name);
- doc = xmlCtxtReadFile(ctx->xml.parser, full_name, NULL, 0);
+ doc = xmlCtxtReadFile(parser, full_name, NULL, 0);
if (!doc) {
- xmlErrorPtr xmlerr = xmlCtxtGetLastError(ctx->xml.parser);
+ xmlErrorPtr xmlerr = xmlCtxtGetLastError(parser);
if (xmlerr) pcilib_error("Error parsing %s, xmlCtxtReadFile reported error %d - %s", full_name, xmlerr->code, xmlerr->message);
else pcilib_error("Error parsing %s", full_name);
return NULL;
}
- err = xmlSchemaValidateDoc(ctx->xml.parts_validator, doc);
+ err = xmlSchemaValidateDoc(validator, doc);
if (err) {
- xmlErrorPtr xmlerr = xmlCtxtGetLastError(ctx->xml.parser);
+ xmlErrorPtr xmlerr = xmlCtxtGetLastError(parser);
xmlFreeDoc(doc);
if (xmlerr) pcilib_error("Error validating %s, xmlSchemaValidateDoc reported error %d - %s", full_name, xmlerr->code, xmlerr->message);
else pcilib_error("Error validating %s", full_name);
@@ -1002,6 +1083,11 @@ static xmlDocPtr pcilib_xml_load_file(pcilib_t *ctx, const char *path, const cha
return doc;
}
+static xmlDocPtr pcilib_xml_load_model_file(pcilib_t *ctx, const char *path, const char *name) {
+ return pcilib_xml_load_file(ctx, ctx->xml.parser, ctx->xml.parts_validator, path, name);
+}
+
+
static int pcilib_process_xml_internal(pcilib_t *ctx, const char *model, const char *location) {
int err;
@@ -1039,7 +1125,7 @@ static int pcilib_process_xml_internal(pcilib_t *ctx, const char *model, const c
if ((len < 4)||(strcasecmp(file->d_name + len - 4, ".xml"))) continue;
if (file->d_type != DT_REG) continue;
- newdoc = pcilib_xml_load_file(ctx, model_path, file->d_name);
+ newdoc = pcilib_xml_load_model_file(ctx, model_path, file->d_name);
if (!newdoc) {
pcilib_error("Error processing XML file %s", file->d_name);
continue;
@@ -1190,3 +1276,91 @@ void pcilib_free_xml(pcilib_t *ctx) {
xmlMemoryDump();
*/
}
+
+
+char *pcilib_detect_xml_model(pcilib_t *ctx, unsigned int vendor_id, unsigned int device_id) {
+ int err;
+ char *model = NULL;
+ xmlSchemaPtr schema; /**< Pointer to the parsed xsd schema */
+ xmlSchemaValidCtxtPtr validator; /**< Pointer to the XML validation context */
+ xmlParserCtxtPtr parser; /**< Pointer to the XML parser context */
+
+ DIR *rep;
+ struct dirent *file = NULL;
+ struct stat st;
+ const char *data_dir;
+ char *xsd_path, *xml_path;
+
+ xmlXPathObjectPtr nodes;
+ xmlChar xpath_query[64];
+
+ xmlStrPrintf(xpath_query, sizeof(xpath_query), (xmlChar*)"/devices/device[@vendor=%x and @device=%x]/@model", vendor_id, device_id);
+
+ data_dir = getenv("PCILIB_DATA_DIR");
+ if (!data_dir) data_dir = PCILIB_DATA_DIR;
+
+ xsd_path = (char*)alloca(strlen(data_dir) + 32);
+ xml_path = (char*)alloca(strlen(data_dir) + 32);
+ if ((!xsd_path)||(!xml_path)) {
+ pcilib_error("Error allocating stack memory");
+ return NULL;
+ }
+
+ sprintf(xsd_path, "%s/devices.xsd", data_dir);
+ sprintf(xml_path, "%s/devices", data_dir);
+ if (stat(xsd_path, &st)||stat(xml_path, &st)) {
+ pcilib_info("No XML devices are defined, missing devices schema or list");
+ return NULL;
+ }
+
+ parser = xmlNewParserCtxt();
+ if (!parser) {
+ xmlErrorPtr xmlerr = xmlGetLastError();
+ if (xmlerr) pcilib_error("xmlNewParserCtxt reported error %d (%s)", xmlerr->code, xmlerr->message);
+ else pcilib_error("Failed to create an XML parser context");
+ return NULL;
+ }
+
+ err = pcilib_xml_load_xsd_file(ctx, xsd_path, &schema, &validator);
+ if (err) {
+ xmlFreeParserCtxt(parser);
+ pcilib_error("Error (%i) parsing the device schema (%s)", err, xsd_path);
+ return NULL;
+ }
+
+ rep = opendir(xml_path);
+ if (!rep) goto cleanup;
+
+ while ((model == NULL)&&((file = readdir(rep)) != NULL)) {
+ xmlDocPtr doc;
+
+ size_t len = strlen(file->d_name);
+ if ((len < 4)||(strcasecmp(file->d_name + len - 4, ".xml"))) continue;
+ if (file->d_type != DT_REG) continue;
+
+ doc = pcilib_xml_load_file(ctx, parser, validator, xml_path, file->d_name);
+ if (!doc) continue;
+
+ nodes = pcilib_xml_eval_xpath_expression(ctx, doc, xpath_query);
+ if (!nodes) {
+ xmlFreeDoc(doc);
+ continue;
+ }
+
+ if (!xmlXPathNodeSetIsEmpty(nodes->nodesetval)) {
+ xmlNodePtr node = nodes->nodesetval->nodeTab[0];
+ model = strdup((char*)node->children->content);
+ }
+
+ xmlXPathFreeObject(nodes);
+ xmlFreeDoc(doc);
+ }
+ closedir(rep);
+
+cleanup:
+ xmlSchemaFree(schema);
+ xmlSchemaFreeValidCtxt(validator);
+ xmlFreeParserCtxt(parser);
+
+ return model;
+}
diff --git a/pcilib/xml.h b/pcilib/xml.h
index d8a9693..f6fc91b 100644
--- a/pcilib/xml.h
+++ b/pcilib/xml.h
@@ -40,6 +40,16 @@ struct pcilib_xml_s {
extern "C" {
#endif
+/**
+ * Resolves device model from vendor and device ids using XML configuration
+ * The association of vendor/device id pairs with model are provided devices.xml under PCILIB_MODEL_DIR
+ * @param[in,out] ctx - pcilib context
+ * @param[in] vendor_id - the vendor id
+ * @param[in] device_id - the device id
+ * @return - the name of model or NULL on an error and unknown device. It is caller responsibility to free returned string.
+ */
+char *pcilib_detect_xml_model(pcilib_t *ctx, unsigned int vendor_id, unsigned int device_id);
+
/** Initializes XML stack and loads a default set of XML files.
* The default location for XML files is /usr/local/share/pcilib/models/@b{model}.
* This can be altered using CMake PCILIB_MODEL_DIR variable while building or using