rewrite the device subsystem interfaces (#48)
[lunaix-os.git] / lunaix-os / hal / devtree / dtm.c
1 #include <hal/devtreem.h>
2 #include <lunaix/device.h>
3 #include <lunaix/mm/valloc.h>
4 #include <lunaix/syslog.h>
5 #include <lunaix/owloysius.h>
6 #include <lunaix/status.h>
7
8 #include <klibc/string.h>
9
10 LOG_MODULE("dtm")
11
12 static DECLARE_HASHTABLE(registry,    32);
13 static struct device_cat* dt_category;
14
15 struct figura
16 {
17     char val;
18     bool optional;
19 };
20
21 #define hash(def)   ((unsigned int)__ptr(def))
22
23 static struct dtm_driver_record*
24 __locate_record(struct device_def* def)
25 {
26     struct dtm_driver_record *p, *n;
27
28     hashtable_hash_foreach(registry, hash(def), p, n, node)
29     {
30         if (p->def == def) {
31             return p;
32         }
33     }
34
35     return NULL;
36 }
37
38
39 static inline void
40 __get_patternlet(const char* str, unsigned i, size_t len, 
41                  struct figura* fig)
42 {
43     fig->optional = (i + 1 < len && str[i + 1] == '?');
44
45     if (i >= len) {
46         fig->val = 0;
47         return;
48     }
49
50     fig->val = str[i];
51 }
52
53 /**
54  * A simplified regular expression matcher:
55  *      1. '*' matches any substring including empty string
56  *      2. '?' mark the prefixed character optional (an epsilon transition)
57  */
58 static bool
59 __try_match(const char* str, const char* pattern, size_t pat_sz)
60 {
61     unsigned j = 0, i = 0;
62     int saved_star = -1, saved_pos = 0;
63     size_t str_sz = strlen(str);
64
65     char c;
66     struct figura p0, p1;
67
68     while (i < str_sz) {
69         c = str[i++];
70         __get_patternlet(pattern, j, pat_sz, &p0);
71         __get_patternlet(pattern, j + 1, pat_sz, &p1);
72
73         if (p0.val == c) {
74             j += 1 + !!p0.optional;
75             saved_pos = (int)i;
76             continue;
77         }
78
79         if (p0.val == '*') {
80             saved_pos = i;
81             saved_star = (int)j;
82
83             if (p1.optional || p1.val == c) {
84                 ++j; --i;
85             }
86
87             continue;
88         }
89
90         if (p0.optional) {
91             --i; j += 2;
92             continue;
93         }
94
95         if (saved_star < 0) {
96             return false;
97         }
98
99         j = (unsigned)saved_star;
100         i = (unsigned)saved_pos;
101     }
102     
103     return j + 1 >= pat_sz;
104 }
105
106 static struct device_meta*
107 __try_create_categorical(struct dt_node_base *p)
108 {
109     if (!p) return NULL;
110
111     struct device_meta* parent = NULL;
112     struct device_cat* cat;
113
114     parent = __try_create_categorical(p->parent);
115     parent = parent ?: dev_meta(dt_category);
116
117     if (!p->compat.size) {
118         return parent;
119     }
120
121     if (p->binded_dev) {
122         cat = changeling_reveal(p->binded_dev, devcat_morpher);
123     }
124     else {
125         cat = device_addcat(parent, HSTR_VAL(dt_mobj(p)->name));
126         p->binded_dev = dev_mobj(cat);
127     }
128
129     return dev_meta(cat);
130 }
131
132 static bool
133 compat_matched(struct dtm_driver_record* rec, struct dt_node_base *base)
134 {
135     const char *compat;
136     struct dtm_driver_info *p, *n;
137
138     list_for_each(p, n, rec->infos.first, node)
139     {
140         size_t pat_len = strlen(p->pattern);
141         dtprop_strlst_foreach(compat, &base->compat)
142         {
143             if (__try_match(compat, p->pattern, pat_len)) {
144                 return true;
145             }
146         }
147     }
148
149     return false;
150 }
151
152 static int
153 dtm_try_create_from(struct device_def* def)
154 {
155     int err;
156     const char *name;
157     struct dt_context* dtctx;
158     struct dtm_driver_record* rec;
159     struct dt_node_base *p, *n;
160     
161     dtctx = dt_main_context();
162
163     rec = __locate_record(def);
164     if (!rec) {
165         return ENOENT;
166     }
167
168     llist_for_each(p, n, &dtctx->nodes, nodes)
169     {
170         if (!p->compat.size) {
171             continue;
172         }
173
174         if (!compat_matched(rec, p)) {
175             continue;
176         }
177
178         __try_create_categorical(p);
179
180         if ((err = def->create(def, dt_mobj(p)))) {
181             name = HSTR_VAL(dt_mobj(p)->name);
182             WARN("failed to bind devtree node %s, err=%d", name, err);
183         }
184     }
185
186     return 0;
187 }
188
189 void
190 dtm_register_entry(struct device_def* def, const char* pattern)
191 {
192     struct dtm_driver_info* info;
193     struct dtm_driver_record* record;
194     
195     info = valloc(sizeof(*info));
196     info->pattern = pattern;
197
198     record = __locate_record(def);
199     if (!record) {
200         record = valloc(sizeof(*record));
201         record->def = def;
202         list_head_init(&record->infos);
203
204         hashtable_hash_in(registry, &record->node, hash(def));
205     }
206
207     list_add(&record->infos, &info->node);
208
209     device_chain_loader(def, dtm_try_create_from);
210 }
211
212 static void
213 dtm_init()
214 {
215     hashtable_init(registry);
216
217     dt_category = device_addcat(NULL, "tree");
218 }
219 owloysius_fetch_init(dtm_init, on_sysconf);