1 /*
2 * Copyright (C) 2020-2021 Alibaba Group Holding Limited
3 */
4
5 #include <string.h>
6 #include <aos/device_core.h>
7
8 #define REF_COUNT_PENDING UINT32_MAX
9 #define REF_COUNT_MAX (UINT32_MAX - 1)
10
11 static struct k_rbtree_root_t device_map = RBT_ROOT;
12 static aos_mutex_t device_map_mutex;
13
device_map_lock(void)14 static void device_map_lock(void)
15 {
16 (void)aos_mutex_lock(&device_map_mutex, AOS_WAIT_FOREVER);
17 }
18
device_map_unlock(void)19 static void device_map_unlock(void)
20 {
21 (void)aos_mutex_unlock(&device_map_mutex);
22 }
23
device_forbid_removal(aos_dev_t * dev)24 static void device_forbid_removal(aos_dev_t *dev)
25 {
26 (void)aos_sem_wait(&dev->rb_sem, AOS_WAIT_FOREVER);
27 }
28
device_allow_removal(aos_dev_t * dev)29 static void device_allow_removal(aos_dev_t *dev)
30 {
31 (void)aos_sem_signal(&dev->rb_sem);
32 }
33
device_wait_removal(aos_dev_t * dev)34 static void device_wait_removal(aos_dev_t *dev)
35 {
36 (void)aos_sem_wait(&dev->rb_sem, AOS_WAIT_FOREVER);
37 }
38
compare_device(aos_dev_type_t a_type,uint32_t a_id,aos_dev_type_t b_type,uint32_t b_id)39 static int compare_device(aos_dev_type_t a_type, uint32_t a_id, aos_dev_type_t b_type, uint32_t b_id)
40 {
41 if (a_type < b_type)
42 return -1;
43
44 if (a_type > b_type)
45 return 1;
46
47 if (a_id < b_id)
48 return -1;
49
50 if (a_id > b_id)
51 return 1;
52
53 return 0;
54 }
55
find_device(aos_dev_type_t type,uint32_t id)56 static aos_dev_t *find_device(aos_dev_type_t type, uint32_t id)
57 {
58 struct k_rbtree_node_t *node = device_map.rbt_node;
59
60 while (node) {
61 aos_dev_t *dev = k_rbtree_entry(node, aos_dev_t, rb_node);
62 int r = compare_device(type, id, dev->type, dev->id);
63
64 if (r < 0)
65 node = node->rbt_left;
66 else if (r > 0)
67 node = node->rbt_right;
68 else
69 break;
70 }
71
72 return node ? k_rbtree_entry(node, aos_dev_t, rb_node) : NULL;
73 }
74
insert_device(aos_dev_t * dev)75 static void insert_device(aos_dev_t *dev)
76 {
77 struct k_rbtree_node_t **link = &device_map.rbt_node;
78 struct k_rbtree_node_t *parent = NULL;
79
80 while (*link) {
81 aos_dev_t *tmp_dev;
82 int r;
83
84 parent = *link;
85 tmp_dev = k_rbtree_entry(*link, aos_dev_t, rb_node);
86 r = compare_device(dev->type, dev->id, tmp_dev->type, tmp_dev->id);
87 link = (r < 0) ? &(*link)->rbt_left : &(*link)->rbt_right;
88 }
89
90 k_rbtree_link_node(&dev->rb_node, parent, link);
91 k_rbtree_insert_color(&dev->rb_node, &device_map);
92 }
93
remove_device(aos_dev_t * dev)94 static void remove_device(aos_dev_t *dev)
95 {
96 k_rbtree_erase(&dev->rb_node, &device_map);
97 }
98
99 #ifdef AOS_COMP_VFS
add_to_vfs(aos_dev_t * dev)100 static aos_status_t add_to_vfs(aos_dev_t *dev)
101 {
102 const char prefix[] = "/dev/";
103 char path[sizeof(prefix) - 1 + sizeof(dev->vfs_helper.name)];
104
105 if (dev->vfs_helper.name[0] == '\0' || !dev->vfs_helper.ops)
106 return 0;
107
108 memcpy(path, prefix, sizeof(prefix) - 1);
109 strncpy(&path[sizeof(prefix) - 1], dev->vfs_helper.name, sizeof(dev->vfs_helper.name) - 1);
110 path[sizeof(path) - 1] = '\0';
111
112 return aos_register_driver(path, dev->vfs_helper.ops, dev);
113 }
114
remove_from_vfs(aos_dev_t * dev)115 static void remove_from_vfs(aos_dev_t *dev)
116 {
117 const char prefix[] = "/dev/";
118 char path[sizeof(prefix) - 1 + sizeof(dev->vfs_helper.name)];
119
120 if (dev->vfs_helper.name[0] == '\0' || !dev->vfs_helper.ops)
121 return;
122
123 memcpy(path, prefix, sizeof(prefix) - 1);
124 strncpy(&path[sizeof(prefix) - 1], dev->vfs_helper.name, sizeof(dev->vfs_helper.name) - 1);
125 path[sizeof(path) - 1] = '\0';
126 (void)aos_unregister_driver(path);
127 }
128 #endif /* AOS_COMP_VFS */
129
aos_dev_register(aos_dev_t * dev)130 aos_status_t aos_dev_register(aos_dev_t *dev)
131 {
132 aos_status_t ret;
133
134 if (!dev)
135 return -EINVAL;
136
137 ret = aos_sem_new(&dev->rb_sem, 1);
138 if (ret)
139 return ret;
140
141 ret = aos_mutex_new(&dev->mutex);
142 if (ret) {
143 aos_sem_free(&dev->rb_sem);
144 return ret;
145 }
146
147 dev->ref_count = REF_COUNT_PENDING;
148 device_map_lock();
149
150 if (find_device(dev->type, dev->id)) {
151 device_map_unlock();
152 aos_mutex_free(&dev->mutex);
153 aos_sem_free(&dev->rb_sem);
154 return -EEXIST;
155 }
156
157 insert_device(dev);
158 device_map_unlock();
159
160 #ifdef AOS_COMP_VFS
161 ret = add_to_vfs(dev);
162 if (ret) {
163 device_map_lock();
164 remove_device(dev);
165 device_map_unlock();
166 aos_mutex_free(&dev->mutex);
167 aos_sem_free(&dev->rb_sem);
168 return ret;
169 }
170 #endif
171
172 aos_dev_lock(dev);
173 dev->ref_count = 0;
174 aos_dev_unlock(dev);
175
176 return 0;
177 }
178
aos_dev_unregister(aos_dev_type_t type,uint32_t id)179 aos_status_t aos_dev_unregister(aos_dev_type_t type, uint32_t id)
180 {
181 aos_dev_t *dev;
182 aos_status_t ret;
183
184 device_map_lock();
185 dev = find_device(type, id);
186 if (!dev) {
187 device_map_unlock();
188 return -ENODEV;
189 }
190
191 device_forbid_removal(dev);
192 device_map_unlock();
193 aos_dev_lock(dev);
194
195 if (dev->ref_count == REF_COUNT_PENDING) {
196 aos_dev_unlock(dev);
197 device_allow_removal(dev);
198 return -ENODEV;
199 } else if (dev->ref_count > 0) {
200 aos_dev_unlock(dev);
201 device_allow_removal(dev);
202 return -EBUSY;
203 } else {
204 dev->ref_count = REF_COUNT_PENDING;
205 }
206
207 aos_dev_unlock(dev);
208 device_allow_removal(dev);
209 #ifdef AOS_COMP_VFS
210 remove_from_vfs(dev);
211 #endif
212 device_map_lock();
213 device_wait_removal(dev);
214 remove_device(dev);
215 device_map_unlock();
216 aos_mutex_free(&dev->mutex);
217 aos_sem_free(&dev->rb_sem);
218
219 if (dev->ops && dev->ops->unregister)
220 dev->ops->unregister(dev);
221
222 return 0;
223 }
224
aos_dev_ref(aos_dev_ref_t * ref,aos_dev_t * dev)225 aos_status_t aos_dev_ref(aos_dev_ref_t *ref, aos_dev_t *dev)
226 {
227 if (!ref)
228 return -EINVAL;
229
230 if (!dev) {
231 aos_dev_ref_init(ref);
232 return -EINVAL;
233 }
234
235 aos_dev_lock(dev);
236
237 if (dev->ref_count == REF_COUNT_PENDING) {
238 aos_dev_unlock(dev);
239 aos_dev_ref_init(ref);
240 return -ENODEV;
241 } else if (dev->ref_count == REF_COUNT_MAX) {
242 aos_dev_unlock(dev);
243 aos_dev_ref_init(ref);
244 return -EBUSY;
245 }
246
247 ref->dev = dev;
248 ref->pdata = NULL;
249
250 if (dev->ops && dev->ops->get) {
251 aos_status_t ret;
252
253 ret = dev->ops->get(ref);
254 if (ret) {
255 aos_dev_unlock(dev);
256 aos_dev_ref_init(ref);
257 return ret;
258 }
259 }
260
261 dev->ref_count++;
262 aos_dev_unlock(dev);
263
264 return 0;
265 }
266
aos_dev_get(aos_dev_ref_t * ref,aos_dev_type_t type,uint32_t id)267 aos_status_t aos_dev_get(aos_dev_ref_t *ref, aos_dev_type_t type, uint32_t id)
268 {
269 aos_dev_t *dev;
270 aos_status_t ret;
271
272 if (!ref)
273 return -EINVAL;
274
275 device_map_lock();
276 dev = find_device(type, id);
277 if (!dev) {
278 device_map_unlock();
279 aos_dev_ref_init(ref);
280 return -ENODEV;
281 }
282
283 device_forbid_removal(dev);
284 device_map_unlock();
285 ret = aos_dev_ref(ref, dev);
286 device_allow_removal(dev);
287
288 return ret;
289 }
290
aos_dev_put(aos_dev_ref_t * ref)291 void aos_dev_put(aos_dev_ref_t *ref)
292 {
293 if (!ref || !aos_dev_ref_is_valid(ref))
294 return;
295
296 aos_dev_lock(ref->dev);
297 ref->dev->ref_count--;
298
299 if (ref->dev->ops && ref->dev->ops->put)
300 ref->dev->ops->put(ref);
301
302 aos_dev_unlock(ref->dev);
303 aos_dev_ref_init(ref);
304 }
305
device_core_init(void)306 static int device_core_init(void)
307 {
308 return (int)aos_mutex_new(&device_map_mutex);
309 }
310
311 CORE_DRIVER_ENTRY(device_core_init)
312