1/*
2 * This file is part of the MicroPython project, http://micropython.org/
3 *
4 * Original template for this file comes from:
5 * Low level disk I/O module skeleton for FatFs, (C)ChaN, 2013
6 *
7 * The MIT License (MIT)
8 *
9 * Copyright (c) 2013, 2014 Damien P. George
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 * THE SOFTWARE.
28 */
29
30#include "py/mpconfig.h"
31#if MICROPY_VFS && MICROPY_VFS_FAT
32
33#include <stdint.h>
34#include <stdio.h>
35
36#include "py/mphal.h"
37
38#include "py/runtime.h"
39#include "py/binary.h"
40#include "py/objarray.h"
41#include "py/mperrno.h"
42#include "lib/oofatfs/ff.h"
43#include "lib/oofatfs/diskio.h"
44#include "extmod/vfs_fat.h"
45
46typedef void *bdev_t;
47STATIC fs_user_mount_t *disk_get_device(void *bdev) {
48 return (fs_user_mount_t *)bdev;
49}
50
51/*-----------------------------------------------------------------------*/
52/* Read Sector(s) */
53/*-----------------------------------------------------------------------*/
54
55DRESULT disk_read(
56 bdev_t pdrv, /* Physical drive nmuber (0..) */
57 BYTE *buff, /* Data buffer to store read data */
58 DWORD sector, /* Sector address (LBA) */
59 UINT count /* Number of sectors to read (1..128) */
60 ) {
61 fs_user_mount_t *vfs = disk_get_device(pdrv);
62 if (vfs == NULL) {
63 return RES_PARERR;
64 }
65
66 int ret = mp_vfs_blockdev_read(&vfs->blockdev, sector, count, buff);
67
68 return ret == 0 ? RES_OK : RES_ERROR;
69}
70
71/*-----------------------------------------------------------------------*/
72/* Write Sector(s) */
73/*-----------------------------------------------------------------------*/
74
75DRESULT disk_write(
76 bdev_t pdrv, /* Physical drive nmuber (0..) */
77 const BYTE *buff, /* Data to be written */
78 DWORD sector, /* Sector address (LBA) */
79 UINT count /* Number of sectors to write (1..128) */
80 ) {
81 fs_user_mount_t *vfs = disk_get_device(pdrv);
82 if (vfs == NULL) {
83 return RES_PARERR;
84 }
85
86 int ret = mp_vfs_blockdev_write(&vfs->blockdev, sector, count, buff);
87
88 if (ret == -MP_EROFS) {
89 // read-only block device
90 return RES_WRPRT;
91 }
92
93 return ret == 0 ? RES_OK : RES_ERROR;
94}
95
96
97/*-----------------------------------------------------------------------*/
98/* Miscellaneous Functions */
99/*-----------------------------------------------------------------------*/
100
101DRESULT disk_ioctl(
102 bdev_t pdrv, /* Physical drive nmuber (0..) */
103 BYTE cmd, /* Control code */
104 void *buff /* Buffer to send/receive control data */
105 ) {
106 fs_user_mount_t *vfs = disk_get_device(pdrv);
107 if (vfs == NULL) {
108 return RES_PARERR;
109 }
110
111 // First part: call the relevant method of the underlying block device
112 static const uint8_t op_map[8] = {
113 [CTRL_SYNC] = MP_BLOCKDEV_IOCTL_SYNC,
114 [GET_SECTOR_COUNT] = MP_BLOCKDEV_IOCTL_BLOCK_COUNT,
115 [GET_SECTOR_SIZE] = MP_BLOCKDEV_IOCTL_BLOCK_SIZE,
116 [IOCTL_INIT] = MP_BLOCKDEV_IOCTL_INIT,
117 };
118 uint8_t bp_op = op_map[cmd & 7];
119 mp_obj_t ret = mp_const_none;
120 if (bp_op != 0) {
121 ret = mp_vfs_blockdev_ioctl(&vfs->blockdev, bp_op, 0);
122 }
123
124 // Second part: convert the result for return
125 switch (cmd) {
126 case CTRL_SYNC:
127 return RES_OK;
128
129 case GET_SECTOR_COUNT: {
130 *((DWORD *)buff) = mp_obj_get_int(ret);
131 return RES_OK;
132 }
133
134 case GET_SECTOR_SIZE: {
135 if (ret == mp_const_none) {
136 // Default sector size
137 *((WORD *)buff) = 512;
138 } else {
139 *((WORD *)buff) = mp_obj_get_int(ret);
140 }
141 // need to store ssize because we use it in disk_read/disk_write
142 vfs->blockdev.block_size = *((WORD *)buff);
143 return RES_OK;
144 }
145
146 case GET_BLOCK_SIZE:
147 *((DWORD *)buff) = 1; // erase block size in units of sector size
148 return RES_OK;
149
150 case IOCTL_INIT:
151 case IOCTL_STATUS: {
152 DSTATUS stat;
153 if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) {
154 // error initialising
155 stat = STA_NOINIT;
156 } else if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL) {
157 stat = STA_PROTECT;
158 } else {
159 stat = 0;
160 }
161 *((DSTATUS *)buff) = stat;
162 return RES_OK;
163 }
164
165 default:
166 return RES_PARERR;
167 }
168}
169
170#endif // MICROPY_VFS && MICROPY_VFS_FAT
171