1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-02-25     GuEe-GUI     the first version
9  */
10 
11 #define DBG_TAG "rtdm.blk"
12 #define DBG_LVL DBG_INFO
13 #include <rtdbg.h>
14 
15 #include "blk_dev.h"
16 #include "blk_dfs.h"
17 
blk_remove_all(struct rt_blk_disk * disk)18 static void blk_remove_all(struct rt_blk_disk *disk)
19 {
20     struct rt_blk_device *blk, *blk_next;
21 
22     /* Remove all partitions */
23     rt_list_for_each_entry_safe(blk, blk_next, &disk->part_nodes, list)
24     {
25         disk_remove_blk_dev(blk, RT_TRUE);
26     }
27 }
28 
blk_open(rt_device_t dev,rt_uint16_t oflag)29 static rt_err_t blk_open(rt_device_t dev, rt_uint16_t oflag)
30 {
31     struct rt_blk_disk *disk = to_blk_disk(dev);
32 
33     if (disk->read_only && (oflag & RT_DEVICE_OFLAG_WRONLY))
34     {
35         return -RT_EINVAL;
36     }
37 
38     return RT_EOK;
39 }
40 
blk_close(rt_device_t dev)41 static rt_err_t blk_close(rt_device_t dev)
42 {
43     return RT_EOK;
44 }
45 
blk_read(rt_device_t dev,rt_off_t sector,void * buffer,rt_size_t sector_count)46 static rt_ssize_t blk_read(rt_device_t dev, rt_off_t sector,
47         void *buffer, rt_size_t sector_count)
48 {
49     rt_ssize_t res;
50     struct rt_blk_disk *disk = to_blk_disk(dev);
51 
52     rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
53 
54     res = disk->ops->read(disk, sector, buffer, sector_count);
55 
56     rt_sem_release(&disk->usr_lock);
57 
58     return res;
59 }
60 
blk_write(rt_device_t dev,rt_off_t sector,const void * buffer,rt_size_t sector_count)61 static rt_ssize_t blk_write(rt_device_t dev, rt_off_t sector,
62         const void *buffer, rt_size_t sector_count)
63 {
64     rt_ssize_t res;
65     struct rt_blk_disk *disk = to_blk_disk(dev);
66 
67     if (!disk->read_only)
68     {
69         rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
70 
71         res = disk->ops->write(disk, sector, buffer, sector_count);
72 
73         rt_sem_release(&disk->usr_lock);
74 
75         return res;
76     }
77 
78     return -RT_ENOSYS;
79 }
80 
blk_parallel_read(rt_device_t dev,rt_off_t sector,void * buffer,rt_size_t sector_count)81 static rt_ssize_t blk_parallel_read(rt_device_t dev, rt_off_t sector,
82         void *buffer, rt_size_t sector_count)
83 {
84     struct rt_blk_disk *disk = to_blk_disk(dev);
85 
86     return disk->ops->read(disk, sector, buffer, sector_count);
87 }
88 
blk_parallel_write(rt_device_t dev,rt_off_t sector,const void * buffer,rt_size_t sector_count)89 static rt_ssize_t blk_parallel_write(rt_device_t dev, rt_off_t sector,
90         const void *buffer, rt_size_t sector_count)
91 {
92     struct rt_blk_disk *disk = to_blk_disk(dev);
93 
94     if (!disk->read_only)
95     {
96         return disk->ops->write(disk, sector, buffer, sector_count);
97     }
98 
99     return -RT_ENOSYS;
100 }
101 
blk_control(rt_device_t dev,int cmd,void * args)102 static rt_err_t blk_control(rt_device_t dev, int cmd, void *args)
103 {
104     rt_err_t err;
105     struct rt_blk_disk *disk = to_blk_disk(dev);
106 
107     switch (cmd)
108     {
109     case RT_DEVICE_CTRL_BLK_GETGEOME:
110         if (args)
111         {
112             err = disk->ops->getgeome(disk, args);
113         }
114         else
115         {
116             err = -RT_EINVAL;
117         }
118 
119         break;
120 
121     case RT_DEVICE_CTRL_BLK_SYNC:
122         if (disk->ops->sync)
123         {
124             rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
125 
126             spin_lock(&disk->lock);
127 
128             err = disk->ops->sync(disk);
129 
130             spin_unlock(&disk->lock);
131 
132             rt_sem_release(&disk->usr_lock);
133         }
134         else
135         {
136             err = -RT_ENOSYS;
137         }
138         break;
139 
140     case RT_DEVICE_CTRL_BLK_ERASE:
141         if (disk->ops->erase)
142         {
143             rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
144 
145             spin_lock(&disk->lock);
146 
147             if (disk->parent.ref_count != 1)
148             {
149                 err = -RT_EBUSY;
150                 goto _unlock;
151             }
152 
153             blk_remove_all(disk);
154 
155             err = disk->ops->erase(disk);
156 
157         _unlock:
158             spin_unlock(&disk->lock);
159 
160             rt_sem_release(&disk->usr_lock);
161         }
162         else
163         {
164             err = -RT_ENOSYS;
165         }
166         break;
167 
168     case RT_DEVICE_CTRL_BLK_AUTOREFRESH:
169         if (disk->ops->autorefresh)
170         {
171             err = disk->ops->autorefresh(disk, !!args);
172         }
173         else
174         {
175             err = -RT_ENOSYS;
176         }
177         break;
178 
179     case RT_DEVICE_CTRL_BLK_PARTITION:
180         err = -RT_EINVAL;
181         break;
182 
183     case RT_DEVICE_CTRL_BLK_SSIZEGET:
184         device_get_blk_ssize(dev, args);
185         err = RT_EOK;
186         break;
187 
188     case RT_DEVICE_CTRL_ALL_BLK_SSIZEGET:
189         device_get_all_blk_ssize(dev, args);
190         err = RT_EOK;
191         break;
192 
193     default:
194         if (disk->ops->control)
195         {
196             err = disk->ops->control(disk, RT_NULL, cmd, args);
197         }
198         else
199         {
200             err = -RT_ENOSYS;
201         }
202         break;
203     }
204 
205     return err;
206 }
207 
208 #ifdef RT_USING_DEVICE_OPS
209 const static struct rt_device_ops blk_ops =
210 {
211     .open = blk_open,
212     .close = blk_close,
213     .read = blk_read,
214     .write = blk_write,
215     .control = blk_control,
216 };
217 
218 const static struct rt_device_ops blk_parallel_ops =
219 {
220     .open = blk_open,
221     .close = blk_close,
222     .read = blk_parallel_read,
223     .write = blk_parallel_write,
224     .control = blk_control,
225 };
226 #endif /* RT_USING_DEVICE_OPS */
227 
rt_hw_blk_disk_register(struct rt_blk_disk * disk)228 rt_err_t rt_hw_blk_disk_register(struct rt_blk_disk *disk)
229 {
230     rt_err_t err;
231 #ifdef RT_USING_DM
232     int device_id;
233 #endif
234     const char *disk_name;
235     rt_uint16_t flags = RT_DEVICE_FLAG_RDONLY;
236 
237     if (!disk || !disk->ops)
238     {
239         return -RT_EINVAL;
240     }
241 
242 #ifdef RT_USING_DM
243     if (!disk->ida)
244     {
245         return -RT_EINVAL;
246     }
247 #endif
248 
249 #if RT_NAME_MAX > 0
250     if (disk->parent.parent.name[0] == '\0')
251 #else
252     if (disk->parent.parent.name)
253 #endif
254     {
255         return -RT_EINVAL;
256     }
257 
258 #ifdef RT_USING_DM
259     if ((device_id = rt_dm_ida_alloc(disk->ida)) < 0)
260     {
261         return -RT_EFULL;
262     }
263 #endif
264 
265     disk->__magic = RT_BLK_DISK_MAGIC;
266     disk_name = to_disk_name(disk);
267 
268     err = rt_sem_init(&disk->usr_lock, disk_name, 1, RT_IPC_FLAG_PRIO);
269 
270     if (err)
271     {
272     #ifdef RT_USING_DM
273         rt_dm_ida_free(disk->ida, device_id);
274     #endif
275 
276         LOG_E("%s: Init user mutex error = %s", rt_strerror(err));
277 
278         return err;
279     }
280 
281     rt_list_init(&disk->part_nodes);
282     rt_spin_lock_init(&disk->lock);
283 
284     disk->parent.type = RT_Device_Class_Block;
285 #ifdef RT_USING_DEVICE_OPS
286     if (disk->parallel_io)
287     {
288         disk->parent.ops = &blk_parallel_ops;
289     }
290     else
291     {
292         disk->parent.ops = &blk_ops;
293     }
294 #else
295     disk->parent.open = blk_open;
296     disk->parent.close = blk_close;
297 
298     if (disk->parallel_io)
299     {
300         disk->parent.read = blk_parallel_read;
301         disk->parent.write = blk_parallel_write;
302     }
303     else
304     {
305         disk->parent.read = blk_read;
306         disk->parent.write = blk_write;
307     }
308     disk->parent.control = blk_control;
309 #endif
310 
311     if (!disk->ops->write)
312     {
313         disk->read_only = RT_TRUE;
314     }
315 
316     if (!disk->read_only)
317     {
318         flags |= RT_DEVICE_FLAG_WRONLY;
319     }
320 
321 #ifdef RT_USING_DM
322     disk->parent.master_id = disk->ida->master_id;
323     disk->parent.device_id = device_id;
324 #endif
325     device_set_blk_fops(&disk->parent);
326 
327     err = rt_device_register(&disk->parent, disk_name, flags);
328 
329     if (err)
330     {
331         rt_sem_detach(&disk->usr_lock);
332     }
333 
334     /* Ignore partition scanning errors */
335     rt_blk_disk_probe_partition(disk);
336 
337     return err;
338 }
339 
rt_hw_blk_disk_unregister(struct rt_blk_disk * disk)340 rt_err_t rt_hw_blk_disk_unregister(struct rt_blk_disk *disk)
341 {
342     rt_err_t err;
343 
344     if (!disk)
345     {
346         return -RT_EINVAL;
347     }
348 
349     spin_lock(&disk->lock);
350 
351     if (disk->parent.ref_count > 0)
352     {
353         err = -RT_EBUSY;
354         goto _unlock;
355     }
356 
357     /* Flush all data */
358     if (disk->ops->sync)
359     {
360         err = disk->ops->sync(disk);
361 
362         if (err)
363         {
364             LOG_E("%s: Sync error = %s", to_disk_name(disk), rt_strerror(err));
365 
366             goto _unlock;
367         }
368     }
369 
370     rt_sem_detach(&disk->usr_lock);
371 
372     blk_remove_all(disk);
373 
374 #ifdef RT_USING_DM
375     rt_dm_ida_free(disk->ida, disk->parent.device_id);
376 #endif
377 
378     err = rt_device_unregister(&disk->parent);
379 
380 _unlock:
381     spin_unlock(&disk->lock);
382 
383     return err;
384 }
385 
rt_blk_disk_get_capacity(struct rt_blk_disk * disk)386 rt_ssize_t rt_blk_disk_get_capacity(struct rt_blk_disk *disk)
387 {
388     rt_ssize_t res;
389     struct rt_device_blk_geometry geometry;
390 
391     if (!disk)
392     {
393         return -RT_EINVAL;
394     }
395 
396     res = disk->ops->getgeome(disk, &geometry);
397 
398     if (!res)
399     {
400         return geometry.sector_count;
401     }
402 
403     return res;
404 }
405 
rt_blk_disk_get_logical_block_size(struct rt_blk_disk * disk)406 rt_ssize_t rt_blk_disk_get_logical_block_size(struct rt_blk_disk *disk)
407 {
408     rt_ssize_t res;
409     struct rt_device_blk_geometry geometry;
410 
411     if (!disk)
412     {
413         return -RT_EINVAL;
414     }
415 
416     res = disk->ops->getgeome(disk, &geometry);
417 
418     if (!res)
419     {
420         return geometry.bytes_per_sector;
421     }
422 
423     return res;
424 }
425 
426 #ifdef RT_USING_DFS_MNTTABLE
blk_dfs_mnt_table(void)427 static int blk_dfs_mnt_table(void)
428 {
429     rt_ubase_t level;
430     struct rt_object *obj;
431     struct rt_device *dev;
432     struct rt_blk_disk *disk;
433     struct rt_blk_device *blk_dev;
434     struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Device);
435 
436     level = rt_hw_interrupt_disable();
437 
438     rt_list_for_each_entry(obj, &info->object_list, list)
439     {
440         dev = rt_container_of(obj, struct rt_device, parent);
441 
442         if (dev->type != RT_Device_Class_Block)
443         {
444             continue;
445         }
446 
447         disk = to_blk_disk(dev);
448 
449         if (disk->__magic != RT_BLK_DISK_MAGIC)
450         {
451             continue;
452         }
453 
454         if (disk->max_partitions == RT_BLK_PARTITION_NONE)
455         {
456             dfs_mount_device(&disk->parent);
457             continue;
458         }
459 
460         rt_list_for_each_entry(blk_dev, &disk->part_nodes, list)
461         {
462             dfs_mount_device(&blk_dev->parent);
463         }
464     }
465 
466     rt_hw_interrupt_enable(level);
467 
468     return 0;
469 }
470 INIT_ENV_EXPORT(blk_dfs_mnt_table);
471 #endif /* RT_USING_DFS_MNTTABLE */
472 
473 #if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH)
convert_size(struct rt_device_blk_geometry * geome,rt_size_t sector_count,rt_size_t * out_cap,rt_size_t * out_minor)474 const char *convert_size(struct rt_device_blk_geometry *geome,
475         rt_size_t sector_count, rt_size_t *out_cap, rt_size_t *out_minor)
476 {
477     rt_size_t cap, minor = 0;
478     int size_index = 0;
479     const char *size_name[] = { "B", "K", "M", "G", "T", "P", "E" };
480 
481     cap = geome->bytes_per_sector * sector_count;
482 
483     for (size_index = 0; size_index < RT_ARRAY_SIZE(size_name) - 1; ++size_index)
484     {
485         if (cap < 1024)
486         {
487             break;
488         }
489 
490         /* Only one decimal point */
491         minor = (cap % 1024) * 10 / 1024;
492         cap = cap / 1024;
493     }
494 
495     *out_cap = cap;
496     *out_minor = minor;
497 
498     return size_name[size_index];
499 }
500 
list_blk(int argc,char ** argv)501 static int list_blk(int argc, char**argv)
502 {
503     rt_ubase_t level;
504     rt_size_t cap, minor;
505     const char *size_name;
506     struct rt_object *obj;
507     struct rt_device *dev;
508     struct rt_blk_disk *disk;
509     struct rt_blk_device *blk_dev;
510     struct rt_device_blk_geometry geome;
511     struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Device);
512 
513     level = rt_hw_interrupt_disable();
514 
515     rt_kprintf("%-*.s MAJ:MIN RM SIZE\tRO TYPE MOUNTPOINT\n", RT_NAME_MAX, "NAME");
516 
517     rt_list_for_each_entry(obj, &info->object_list, list)
518     {
519         dev = rt_container_of(obj, struct rt_device, parent);
520 
521         if (dev->type != RT_Device_Class_Block)
522         {
523             continue;
524         }
525 
526         disk = to_blk_disk(dev);
527 
528         if (disk->__magic != RT_BLK_DISK_MAGIC)
529         {
530             continue;
531         }
532 
533         if (disk->ops->getgeome(disk, &geome))
534         {
535             continue;
536         }
537 
538         size_name = convert_size(&geome, geome.sector_count, &cap, &minor);
539 
540         rt_kprintf("%-*.s %3u.%-3u  %u %u.%u%s\t%u  disk %s\n",
541                 RT_NAME_MAX, to_disk_name(disk),
542         #ifdef RT_USING_DM
543                 disk->parent.master_id, disk->parent.device_id,
544         #else
545                 0, 0,
546         #endif
547                 disk->removable, cap, minor, size_name, disk->read_only,
548                 disk->max_partitions != RT_BLK_PARTITION_NONE ? "\b" :
549                     (dfs_filesystem_get_mounted_path(&disk->parent) ? : "\b"));
550 
551         rt_list_for_each_entry(blk_dev, &disk->part_nodes, list)
552         {
553             size_name = convert_size(&geome, blk_dev->sector_count, &cap, &minor);
554 
555             rt_kprintf("%c--%-*.s %3u.%-3u  %u %u.%u%s\t%u  part %s\n",
556                     blk_dev->list.next != &disk->part_nodes ? '|' : '`',
557                     RT_NAME_MAX - 3, to_blk_name(blk_dev),
558             #ifdef RT_USING_DM
559                     blk_dev->parent.master_id, blk_dev->parent.device_id,
560             #else
561                     0, 0,
562             #endif
563                     disk->removable, cap, minor, size_name, disk->read_only,
564                     dfs_filesystem_get_mounted_path(&blk_dev->parent) ? : "");
565         }
566     }
567 
568     rt_hw_interrupt_enable(level);
569 
570     return 0;
571 }
572 MSH_CMD_EXPORT(list_blk, dump all of blks information);
573 #endif /* RT_USING_CONSOLE && RT_USING_MSH */
574