X-Git-Url: https://scm.lunaixsky.com/lunaix-os.git/blobdiff_plain/99f2ed669601a61f4f0210e0b481ff877cd9bea7..35a7d633d3f16c1e0539af6ca5d8e7482926cd93:/lunaix-os/hal/devtree/dtm.c diff --git a/lunaix-os/hal/devtree/dtm.c b/lunaix-os/hal/devtree/dtm.c new file mode 100644 index 0000000..69ec2e5 --- /dev/null +++ b/lunaix-os/hal/devtree/dtm.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include + +#include + +LOG_MODULE("dtm") + +static DECLARE_HASHTABLE(registry, 32); +static struct device_cat* dt_category; + +struct figura +{ + char val; + bool optional; +}; + +#define hash(def) ((unsigned int)__ptr(def)) + +static struct dtm_driver_record* +__locate_record(struct device_def* def) +{ + struct dtm_driver_record *p, *n; + + hashtable_hash_foreach(registry, hash(def), p, n, node) + { + if (p->def == def) { + return p; + } + } + + return NULL; +} + + +static inline void +__get_patternlet(const char* str, unsigned i, size_t len, + struct figura* fig) +{ + fig->optional = (i + 1 < len && str[i + 1] == '?'); + + if (i >= len) { + fig->val = 0; + return; + } + + fig->val = str[i]; +} + +/** + * A simplified regular expression matcher: + * 1. '*' matches any substring including empty string + * 2. '?' mark the prefixed character optional (an epsilon transition) + */ +static bool +__try_match(const char* str, const char* pattern, size_t pat_sz) +{ + unsigned j = 0, i = 0; + int saved_star = -1, saved_pos = 0; + size_t str_sz = strlen(str); + + char c; + struct figura p0, p1; + + while (i < str_sz) { + c = str[i++]; + __get_patternlet(pattern, j, pat_sz, &p0); + __get_patternlet(pattern, j + 1, pat_sz, &p1); + + if (p0.val == c) { + j += 1 + !!p0.optional; + saved_pos = (int)i; + continue; + } + + if (p0.val == '*') { + saved_pos = i; + saved_star = (int)j; + + if (p1.optional || p1.val == c) { + ++j; --i; + } + + continue; + } + + if (p0.optional) { + --i; j += 2; + continue; + } + + if (saved_star < 0) { + return false; + } + + j = (unsigned)saved_star; + i = (unsigned)saved_pos; + } + + return j + 1 >= pat_sz; +} + +static struct device_meta* +__try_create_categorical(struct dt_node_base *p) +{ + if (!p) return NULL; + + struct device_meta* parent = NULL; + struct device_cat* cat; + + parent = __try_create_categorical(p->parent); + parent = parent ?: dev_meta(dt_category); + + if (!p->compat.size) { + return parent; + } + + if (p->binded_dev) { + cat = changeling_reveal(p->binded_dev, devcat_morpher); + } + else { + cat = device_addcat(parent, HSTR_VAL(dt_mobj(p)->name)); + p->binded_dev = dev_mobj(cat); + } + + return dev_meta(cat); +} + +static bool +compat_matched(struct dtm_driver_record* rec, struct dt_node_base *base) +{ + const char *compat; + struct dtm_driver_info *p, *n; + + list_for_each(p, n, rec->infos.first, node) + { + size_t pat_len = strlen(p->pattern); + dtprop_strlst_foreach(compat, &base->compat) + { + if (__try_match(compat, p->pattern, pat_len)) { + return true; + } + } + } + + return false; +} + +static int +dtm_try_create_from(struct device_def* def) +{ + int err; + const char *name; + struct dt_context* dtctx; + struct dtm_driver_record* rec; + struct dt_node_base *p, *n; + + dtctx = dt_main_context(); + + rec = __locate_record(def); + if (!rec) { + return ENOENT; + } + + llist_for_each(p, n, &dtctx->nodes, nodes) + { + if (!p->compat.size) { + continue; + } + + if (!compat_matched(rec, p)) { + continue; + } + + __try_create_categorical(p); + + if ((err = def->create(def, dt_mobj(p)))) { + name = HSTR_VAL(dt_mobj(p)->name); + WARN("failed to bind devtree node %s, err=%d", name, err); + } + } + + return 0; +} + +void +dtm_register_entry(struct device_def* def, const char* pattern) +{ + struct dtm_driver_info* info; + struct dtm_driver_record* record; + + info = valloc(sizeof(*info)); + info->pattern = pattern; + + record = __locate_record(def); + if (!record) { + record = valloc(sizeof(*record)); + record->def = def; + list_head_init(&record->infos); + + hashtable_hash_in(registry, &record->node, hash(def)); + } + + list_add(&record->infos, &info->node); + + device_chain_loader(def, dtm_try_create_from); +} + +static void +dtm_init() +{ + hashtable_init(registry); + + dt_category = device_addcat(NULL, "tree"); +} +owloysius_fetch_init(dtm_init, on_sysconf); \ No newline at end of file