diff options
Diffstat (limited to 'pcilib/property.c')
-rw-r--r-- | pcilib/property.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/pcilib/property.c b/pcilib/property.c new file mode 100644 index 0000000..23c92d1 --- /dev/null +++ b/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); +} |