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