summaryrefslogtreecommitdiffstats
path: root/pcilib
diff options
context:
space:
mode:
authorSuren A. Chilingaryan <csa@suren.me>2015-10-19 20:31:56 +0200
committerSuren A. Chilingaryan <csa@suren.me>2015-10-19 20:31:56 +0200
commit6ae98bfdd390638c1b020cbc576a590504fe24cf (patch)
tree69836a90b3d48d4f31f1f779591ba7657cc81d7d /pcilib
parent55255f2ce3a2234850249efcabd9ba32d0a89a9c (diff)
downloadpcitool-6ae98bfdd390638c1b020cbc576a590504fe24cf.tar.gz
pcitool-6ae98bfdd390638c1b020cbc576a590504fe24cf.tar.bz2
pcitool-6ae98bfdd390638c1b020cbc576a590504fe24cf.tar.xz
pcitool-6ae98bfdd390638c1b020cbc576a590504fe24cf.zip
Support multiple XML files per folder
Diffstat (limited to 'pcilib')
-rw-r--r--pcilib/xml.c230
-rw-r--r--pcilib/xml.h43
2 files changed, 183 insertions, 90 deletions
diff --git a/pcilib/xml.c b/pcilib/xml.c
index 1238ba8..bc48a34 100644
--- a/pcilib/xml.c
+++ b/pcilib/xml.c
@@ -78,6 +78,17 @@ static xmlNodePtr pcilib_xml_get_parent_register_node(xmlDocPtr doc, xmlNodePtr
}
*/
+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);
+}
+
static int pcilib_xml_parse_view_reference(pcilib_t *ctx, xmlDocPtr doc, xmlNodePtr node, pcilib_view_reference_t *desc) {
xmlAttr *cur;
char *value, *name;
@@ -873,11 +884,14 @@ static int pcilib_xml_process_document(pcilib_t *ctx, xmlDocPtr doc, xmlXPathCon
return 0;
}
-static int pcilib_xml_load_xsd(pcilib_t *ctx, char *xsd_filename) {
+static int pcilib_xml_load_xsd_file(pcilib_t *ctx, char *xsd_filename, xmlSchemaPtr *schema, xmlSchemaValidCtxtPtr *validator) {
int err;
xmlSchemaParserCtxtPtr ctxt;
+ *schema = NULL;
+ *validator = NULL;
+
/** we first parse the xsd file for AST with validation*/
ctxt = xmlSchemaNewParserCtxt(xsd_filename);
if (!ctxt) {
@@ -887,8 +901,8 @@ static int pcilib_xml_load_xsd(pcilib_t *ctx, char *xsd_filename) {
return PCILIB_ERROR_FAILED;
}
- ctx->xml.schema = xmlSchemaParse(ctxt);
- if (!ctx->xml.schema) {
+ *schema = xmlSchemaParse(ctxt);
+ if (!*schema) {
xmlErrorPtr xmlerr = xmlGetLastError();
xmlSchemaFreeParserCtxt(ctxt);
if (xmlerr) pcilib_error("Failed to parse XML schema, xmlSchemaParse reported error %d - %s", xmlerr->code, xmlerr->message);
@@ -898,15 +912,15 @@ static int pcilib_xml_load_xsd(pcilib_t *ctx, char *xsd_filename) {
xmlSchemaFreeParserCtxt(ctxt);
- ctx->xml.validator = xmlSchemaNewValidCtxt(ctx->xml.schema);
- if (!ctx->xml.validator) {
+ *validator = xmlSchemaNewValidCtxt(*schema);
+ if (!*validator) {
xmlErrorPtr xmlerr = xmlGetLastError();
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;
}
- err = xmlSchemaSetValidOptions(ctx->xml.validator, XML_SCHEMA_VAL_VC_I_CREATE);
+ err = xmlSchemaSetValidOptions(*validator, XML_SCHEMA_VAL_VC_I_CREATE);
if (err) {
xmlErrorPtr xmlerr = xmlGetLastError();
if (xmlerr) pcilib_error("xmlSchemaSetValidOptions reported error %d - %s", xmlerr->code, xmlerr->message);
@@ -917,16 +931,45 @@ static int pcilib_xml_load_xsd(pcilib_t *ctx, char *xsd_filename) {
return 0;
}
-static int pcilib_xml_load_file(pcilib_t *ctx, const char *path, const char *name) {
+
+static int pcilib_xml_load_xsd(pcilib_t *ctx, char *model_dir) {
+ int err;
+
+ struct stat st;
+ char *xsd_path;
+
+ xsd_path = (char*)alloca(strlen(model_dir) + 32);
+ if (!xsd_path) return PCILIB_ERROR_MEMORY;
+
+ sprintf(xsd_path, "%s/model.xsd", model_dir);
+ if (stat(xsd_path, &st)) {
+ pcilib_info("XML models are not present, missing parts schema");
+ return PCILIB_ERROR_NOTFOUND;
+ }
+
+ err = pcilib_xml_load_xsd_file(ctx, xsd_path, &ctx->xml.parts_schema, &ctx->xml.parts_validator);
+ if (err) return err;
+
+ sprintf(xsd_path, "%s/references.xsd", model_dir);
+ if (stat(xsd_path, &st)) {
+ pcilib_info("XML models are not present, missing schema");
+ return PCILIB_ERROR_NOTFOUND;
+ }
+
+ return pcilib_xml_load_xsd_file(ctx, xsd_path, &ctx->xml.schema, &ctx->xml.validator);
+}
+
+
+
+static xmlDocPtr pcilib_xml_load_file(pcilib_t *ctx, const char *path, const char *name) {
int err;
char *full_name;
xmlDocPtr doc;
- xmlXPathContextPtr xpath;
full_name = (char*)alloca(strlen(path) + strlen(name) + 2);
if (!name) {
pcilib_error("Error allocating %zu bytes of memory in stack to create a file name", strlen(path) + strlen(name) + 2);
- return PCILIB_ERROR_MEMORY;
+ return NULL;
}
sprintf(full_name, "%s/%s", path, name);
@@ -936,104 +979,139 @@ static int pcilib_xml_load_file(pcilib_t *ctx, const char *path, const char *nam
xmlErrorPtr xmlerr = xmlCtxtGetLastError(ctx->xml.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 PCILIB_ERROR_INVALID_DATA;
+ return NULL;
}
- err = xmlSchemaValidateDoc(ctx->xml.validator, doc);
+ err = xmlSchemaValidateDoc(ctx->xml.parts_validator, doc);
if (err) {
xmlErrorPtr xmlerr = xmlCtxtGetLastError(ctx->xml.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);
- return PCILIB_ERROR_VERIFY;
- }
-
- xpath = xmlXPathNewContext(doc);
- if (!xpath) {
- xmlErrorPtr xmlerr = xmlGetLastError();
- xmlFreeDoc(doc);
- if (xmlerr) pcilib_error("Document %s: xmlXpathNewContext reported error %d - %s for document %s", full_name, xmlerr->code, xmlerr->message);
- else pcilib_error("Error creating XPath context for %s", full_name);
- return PCILIB_ERROR_FAILED;
- }
-
- // This can only partially fail... Therefore we need to keep XML and just return the error...
- err = pcilib_xml_process_document(ctx, doc, xpath);
-
- if (ctx->xml.num_files == PCILIB_MAX_MODEL_FILES) {
- xmlFreeDoc(doc);
- xmlXPathFreeContext(xpath);
- pcilib_error("Too many XML files for a model, only up to %zu are supported", PCILIB_MAX_MODEL_FILES);
- return PCILIB_ERROR_TOOBIG;
+ return NULL;
}
- ctx->xml.docs[ctx->xml.num_files] = doc;
- ctx->xml.xpath[ctx->xml.num_files] = xpath;
- ctx->xml.num_files++;
-
- return err;
+ return doc;
}
-
-int pcilib_process_xml(pcilib_t *ctx, const char *location) {
+static int pcilib_process_xml_internal(pcilib_t *ctx, const char *model, const char *location) {
int err;
DIR *rep;
struct dirent *file = NULL;
char *model_dir, *model_path;
+ xmlDocPtr doc = NULL;
+ xmlNodePtr root = NULL;
+ xmlXPathContextPtr xpath;
+
+ if (ctx->xml.num_files == PCILIB_MAX_MODEL_FILES) {
+ pcilib_error("Too many XML locations for a model, only up to %zu are supported", PCILIB_MAX_MODEL_FILES);
+ return PCILIB_ERROR_TOOBIG;
+ }
+
model_dir = getenv("PCILIB_MODEL_DIR");
if (!model_dir) model_dir = PCILIB_MODEL_DIR;
- model_path = (char*)alloca(strlen(model_dir) + strlen(location) + 2);
+ if (!model) model = ctx->model;
+ if (!location) location = "";
+
+ model_path = (char*)alloca(strlen(model_dir) + strlen(model) + strlen(location) + 3);
if (!model_path) return PCILIB_ERROR_MEMORY;
- sprintf(model_path, "%s/%s", model_dir, location);
+ sprintf(model_path, "%s/%s/%s", model_dir, model, location);
rep = opendir(model_path);
if (!rep) return PCILIB_ERROR_NOTFOUND;
while ((file = readdir(rep)) != NULL) {
+ xmlDocPtr newdoc;
+
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;
- err = pcilib_xml_load_file(ctx, model_path, file->d_name);
- if (err) pcilib_error("Error processing XML file %s", file->d_name);
- }
+ newdoc = pcilib_xml_load_file(ctx, model_path, file->d_name);
+ if (!newdoc) {
+ pcilib_error("Error processing XML file %s", file->d_name);
+ continue;
+ }
+
+ if (doc) {
+ xmlNodePtr node;
+ node = xmlDocGetRootElement(newdoc);
+ if (node) node = xmlFirstElementChild(node);
+ if (node) node = xmlDocCopyNodeList(doc, node);
+ xmlFreeDoc(newdoc);
+
+ if ((!node)||(!xmlAddChildList(root, node))) {
+ xmlErrorPtr xmlerr = xmlCtxtGetLastError(ctx->xml.parser);
+ if (node) xmlFreeNode(node);
+ if (xmlerr) pcilib_error("Error manipulating XML tree of %s, libXML2 reported error %d - %s", file->d_name, xmlerr->code, xmlerr->message);
+ else pcilib_error("Error manipulating XML tree of %s", file->d_name);
+ continue;
+ }
+ } else {
+ root = xmlDocGetRootElement(newdoc);
+ if (!root) {
+ xmlErrorPtr xmlerr = xmlCtxtGetLastError(ctx->xml.parser);
+ xmlFreeDoc(newdoc);
+ if (xmlerr) pcilib_error("Error manipulating XML tree of %s, libXML2 reported error %d - %s", file->d_name, xmlerr->code, xmlerr->message);
+ else pcilib_error("Error manipulating XML tree of %s", file->d_name);
+ continue;
+ }
+ doc = newdoc;
+ // This is undocumented, but should be fine...
+ if (doc->URL) xmlFree((xmlChar*)doc->URL);
+ doc->URL = xmlStrdup(BAD_CAST model_path);
+ }
+ }
closedir(rep);
- return 0;
+ if (!doc)
+ return 0;
+
+ err = xmlSchemaValidateDoc(ctx->xml.validator, doc);
+ if (err) {
+ xmlErrorPtr xmlerr = xmlCtxtGetLastError(ctx->xml.parser);
+ xmlFreeDoc(doc);
+ if (xmlerr) pcilib_error("Error validating %s, xmlSchemaValidateDoc reported error %d - %s", model_path, xmlerr->code, xmlerr->message);
+ else pcilib_error("Error validating %s", model_path);
+ return PCILIB_ERROR_VERIFY;
+ }
+
+ xpath = xmlXPathNewContext(doc);
+ if (!xpath) {
+ xmlErrorPtr xmlerr = xmlGetLastError();
+ xmlFreeDoc(doc);
+ if (xmlerr) pcilib_error("Document %s: xmlXpathNewContext reported error %d - %s for document %s", model_path, xmlerr->code, xmlerr->message);
+ else pcilib_error("Error creating XPath context for %s", model_path);
+ return PCILIB_ERROR_FAILED;
+ }
+
+ // This can only partially fail... Therefore we need to keep XML and just return the error...
+ err = pcilib_xml_process_document(ctx, doc, xpath);
+
+ ctx->xml.docs[ctx->xml.num_files] = doc;
+ ctx->xml.xpath[ctx->xml.num_files] = xpath;
+ ctx->xml.num_files++;
+
+ return err;
}
+int pcilib_process_xml(pcilib_t *ctx, const char *location) {
+ return pcilib_process_xml_internal(ctx, NULL, location);
+}
-/** pcilib_init_xml
- * this function will initialize the registers and banks from the xml files
- * @param[in,out] ctx the pciilib_t running that gets filled with structures
- * @param[in] model the current model of ctx
- * @return an error code
- */
int pcilib_init_xml(pcilib_t *ctx, const char *model) {
int err;
char *model_dir;
- char *xsd_path;
-
- struct stat st;
model_dir = getenv("PCILIB_MODEL_DIR");
if (!model_dir) model_dir = PCILIB_MODEL_DIR;
- xsd_path = (char*)alloca(strlen(model_dir) + 16);
- if (!xsd_path) return PCILIB_ERROR_MEMORY;
-
- sprintf(xsd_path, "%s/model.xsd", model_dir);
- if (stat(xsd_path, &st)) {
- pcilib_info("XML models are not present");
- return PCILIB_ERROR_NOTFOUND;
- }
-
ctx->xml.parser = xmlNewParserCtxt();
if (!ctx->xml.parser) {
xmlErrorPtr xmlerr = xmlGetLastError();
@@ -1042,16 +1120,12 @@ int pcilib_init_xml(pcilib_t *ctx, const char *model) {
return PCILIB_ERROR_FAILED;
}
- err = pcilib_xml_load_xsd(ctx, xsd_path);
+ err = pcilib_xml_load_xsd(ctx, model_dir);
if (err) return err;
- return pcilib_process_xml(ctx, model);
+ return pcilib_process_xml_internal(ctx, model, NULL);
}
-/** pcilib_free_xml
- * this function free the xml parts of the pcilib_t running, and some libxml ashes
- * @param[in] pci the pcilib_t running
-*/
void pcilib_free_xml(pcilib_t *ctx) {
int i;
@@ -1085,7 +1159,16 @@ void pcilib_free_xml(pcilib_t *ctx) {
if (ctx->xml.schema) {
xmlSchemaFree(ctx->xml.schema);
ctx->xml.schema = NULL;
+ }
+
+ if (ctx->xml.parts_validator) {
+ xmlSchemaFreeValidCtxt(ctx->xml.parts_validator);
+ ctx->xml.parts_validator = NULL;
+ }
+ if (ctx->xml.parts_schema) {
+ xmlSchemaFree(ctx->xml.parts_schema);
+ ctx->xml.parts_schema = NULL;
}
if (ctx->xml.parser) {
@@ -1099,14 +1182,3 @@ 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 10bc154..e4a5744 100644
--- a/pcilib/xml.h
+++ b/pcilib/xml.h
@@ -28,8 +28,10 @@ struct pcilib_xml_s {
xmlXPathContextPtr xpath[PCILIB_MAX_MODEL_FILES]; /**< Per-file XPath context */
xmlParserCtxtPtr parser; /**< Pointer to the XML parser context */
- xmlSchemaPtr schema; /**< Pointer to the parsed xsd schema */
+ xmlSchemaPtr schema; /**< Pointer to the parsed xsd schema */
xmlSchemaValidCtxtPtr validator; /**< Pointer to the XML validation context */
+ xmlSchemaPtr parts_schema; /**< Pointer to the parsed xsd schema capable of validating individual XML files - no check for cross-references */
+ xmlSchemaValidCtxtPtr parts_validator; /**< Pointer to the XML validation context capable of validating individual XML files - no check for cross-references */
xmlNodePtr bank_nodes[PCILIB_MAX_REGISTER_BANKS]; /**< pointer to xml nodes of banks in the xml file */
};
@@ -38,27 +40,46 @@ struct pcilib_xml_s {
extern "C" {
#endif
-/**
- * this function gets the xml files and validates them, before filling the pcilib_t struct with the registers and banks of those files
- *@param[in,out] ctx the pcilib_t struct running that gets filled with banks and registers
- *@param[in] model the name of the model
+/** pcilib_init_xml
+ * Initializes XML stack and loads a default set of XML files. The default location for model XML files is
+ * /usr/local/share/pcilib/models/<current_model>. This can be altered using CMake PCILIB_MODEL_DIR variable
+ * while building or using PCILIB_MODEL_DIR environmental variable dynamicly. More XML files can be added
+ * later using pcilib_process_xml call.
+ * @param[in,out] ctx - pcilib context
+ * @param[in] model - the name of the model
+ * @return - error or 0 on success
*/
int pcilib_init_xml(pcilib_t *ctx, const char *model);
/** pcilib_free_xml
* this function free the xml parts of the pcilib_t running, and some libxml ashes
* @param[in] ctx the pcilib_t running
-*/
+ */
void pcilib_free_xml(pcilib_t *ctx);
-
/** pcilib_process_xml
- * this function free the xml parts of the pcilib_t running, and some libxml ashes
- * @param[in] ctx the pcilib_t running
- * @param[in] location of XML files relative to the PCILIB_MODEL_DIR
-*/
+ * Processes a bunch of XML files in the specified directory. During the initialization, all XML files
+ * in the corresponding model directory will be loaded. This function allows to additionally load XML
+ * files from the specified subdirectories of the model directory. I.e. the XML files from the
+ * /usr/local/share/pcilib/models/<current_model>/<location> will be loaded. As with pcilib_init_xml,
+ * the directory can be adjusted using CMake build configuration or PCILIB_MODEL_DIR environmental
+ * variable.
+ * @param[in] ctx - pcilib context
+ * @param[in] location - Specifies sub-directory with XML files relative to the model directory.
+ * @return - error or 0 on success
+ */
int pcilib_process_xml(pcilib_t *ctx, const char *location);
+/** pcilib_get_xml_attr
+ * This is an internal function which returns a specified node attribute in the pcilib_value_t structure.
+ * This function should not be used directly. Instead subsystem specific calls like pcilib_get_register_attr,
+ * pcilib_get_property_attr, ...have to be used.
+ * @param[in] ctx - pcilib context
+ * @param[in] node - LibXML2 node
+ * @param[in] attr - attribute name
+ * @param[out] val - the result will be returned in this variable. Prior to first usage pcilib_value_t variable should be initalized to 0.
+ * @return - error or 0 on success
+ */
int pcilib_get_xml_attr(pcilib_t *ctx, pcilib_xml_node_t *node, const char *attr, pcilib_value_t *val);