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