Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
BufferedBlockDevice.cpp
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2018 ARM Limited 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); 00005 * you may not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, 00012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 00017 #include "BufferedBlockDevice.h" 00018 #include "platform/mbed_assert.h" 00019 #include "platform/mbed_atomic.h" 00020 #include <algorithm> 00021 #include <string.h> 00022 00023 namespace mbed { 00024 00025 static inline uint32_t align_down(bd_size_t val, bd_size_t size) 00026 { 00027 return val / size * size; 00028 } 00029 00030 BufferedBlockDevice::BufferedBlockDevice(BlockDevice *bd) 00031 : _bd(bd), _bd_program_size(0), _bd_read_size(0), _bd_size(0), _write_cache_addr(0), _write_cache_valid(false), 00032 _write_cache(0), _read_buf(0), _init_ref_count(0), _is_initialized(false) 00033 { 00034 MBED_ASSERT(_bd); 00035 } 00036 00037 BufferedBlockDevice::~BufferedBlockDevice() 00038 { 00039 deinit(); 00040 } 00041 00042 int BufferedBlockDevice::init() 00043 { 00044 uint32_t val = core_util_atomic_incr_u32 (&_init_ref_count, 1); 00045 00046 if (val != 1) { 00047 return BD_ERROR_OK; 00048 } 00049 00050 int err = _bd->init(); 00051 if (err) { 00052 return err; 00053 } 00054 00055 _bd_read_size = _bd->get_read_size(); 00056 _bd_program_size = _bd->get_program_size(); 00057 _bd_size = _bd->size(); 00058 00059 if (!_write_cache) { 00060 _write_cache = new uint8_t[_bd_program_size]; 00061 } 00062 00063 if (!_read_buf) { 00064 _read_buf = new uint8_t[_bd_read_size]; 00065 } 00066 00067 invalidate_write_cache(); 00068 00069 _is_initialized = true; 00070 return BD_ERROR_OK; 00071 } 00072 00073 int BufferedBlockDevice::deinit() 00074 { 00075 if (!_is_initialized) { 00076 return BD_ERROR_OK; 00077 } 00078 00079 // Flush out all data from buffers 00080 int err = sync(); 00081 if (err) { 00082 return err; 00083 } 00084 00085 uint32_t val = core_util_atomic_decr_u32 (&_init_ref_count, 1); 00086 00087 if (val) { 00088 return BD_ERROR_OK; 00089 } 00090 00091 delete[] _write_cache; 00092 _write_cache = 0; 00093 delete[] _read_buf; 00094 _read_buf = 0; 00095 _is_initialized = false; 00096 return _bd->deinit(); 00097 } 00098 00099 int BufferedBlockDevice::flush() 00100 { 00101 MBED_ASSERT(_write_cache); 00102 if (!_is_initialized) { 00103 return BD_ERROR_DEVICE_ERROR; 00104 } 00105 00106 if (_write_cache_valid) { 00107 int ret = _bd->program(_write_cache, _write_cache_addr, _bd_program_size); 00108 if (ret) { 00109 return ret; 00110 } 00111 invalidate_write_cache(); 00112 } 00113 return 0; 00114 } 00115 00116 void BufferedBlockDevice::invalidate_write_cache() 00117 { 00118 _write_cache_addr = _bd_size; 00119 _write_cache_valid = false; 00120 } 00121 00122 int BufferedBlockDevice::sync() 00123 { 00124 if (!_is_initialized) { 00125 return BD_ERROR_DEVICE_ERROR; 00126 } 00127 00128 MBED_ASSERT(_write_cache); 00129 int ret = flush(); 00130 if (ret) { 00131 return ret; 00132 } 00133 return _bd->sync(); 00134 } 00135 00136 int BufferedBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size) 00137 { 00138 if (!_is_initialized) { 00139 return BD_ERROR_DEVICE_ERROR; 00140 } 00141 00142 MBED_ASSERT(_write_cache && _read_buf); 00143 00144 if (!is_valid_read(addr, size)) { 00145 return BD_ERROR_DEVICE_ERROR; 00146 } 00147 00148 // Common case - no need to involve write cache or read buffer 00149 if (_bd->is_valid_read(addr, size) && 00150 ((addr + size <= _write_cache_addr) || (addr > _write_cache_addr + _bd_program_size))) { 00151 return _bd->read(b, addr, size); 00152 } 00153 00154 uint8_t *buf = static_cast<uint8_t *>(b); 00155 00156 // Read logic: Split read to chunks, according to whether we cross the write cache 00157 while (size) { 00158 bd_size_t chunk; 00159 bool read_from_bd = true; 00160 if (_write_cache_valid && addr < _write_cache_addr) { 00161 chunk = std::min(size, _write_cache_addr - addr); 00162 } else if (_write_cache_valid && (addr >= _write_cache_addr) && (addr < _write_cache_addr + _bd_program_size)) { 00163 // One case we need to take our data from cache 00164 chunk = std::min(size, _bd_program_size - addr % _bd_program_size); 00165 memcpy(buf, _write_cache + addr % _bd_program_size, chunk); 00166 read_from_bd = false; 00167 } else { 00168 chunk = size; 00169 } 00170 00171 // Now, in case we read from the BD, make sure we are aligned with its read size. 00172 // If not, use read buffer as a helper. 00173 if (read_from_bd) { 00174 bd_size_t offs_in_read_buf = addr % _bd_read_size; 00175 int ret; 00176 if (offs_in_read_buf || (chunk < _bd_read_size)) { 00177 chunk = std::min(chunk, _bd_read_size - offs_in_read_buf); 00178 ret = _bd->read(_read_buf, addr - offs_in_read_buf, _bd_read_size); 00179 memcpy(buf, _read_buf + offs_in_read_buf, chunk); 00180 } else { 00181 chunk = align_down(chunk, _bd_read_size); 00182 ret = _bd->read(buf, addr, chunk); 00183 } 00184 if (ret) { 00185 return ret; 00186 } 00187 } 00188 00189 buf += chunk; 00190 addr += chunk; 00191 size -= chunk; 00192 } 00193 00194 return 0; 00195 } 00196 00197 int BufferedBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size) 00198 { 00199 if (!_is_initialized) { 00200 return BD_ERROR_DEVICE_ERROR; 00201 } 00202 00203 MBED_ASSERT(_write_cache); 00204 00205 int ret; 00206 00207 bd_addr_t aligned_addr = align_down(addr, _bd_program_size); 00208 00209 const uint8_t *buf = static_cast <const uint8_t *>(b); 00210 00211 // Need to flush if moved to another program unit 00212 if (aligned_addr != _write_cache_addr) { 00213 ret = flush(); 00214 if (ret) { 00215 return ret; 00216 } 00217 } 00218 00219 // Write logic: Keep data in cache as long as we don't reach the end of the program unit. 00220 // Otherwise, program to the underlying BD. 00221 while (size) { 00222 _write_cache_addr = align_down(addr, _bd_program_size); 00223 bd_addr_t offs_in_buf = addr - _write_cache_addr; 00224 bd_size_t chunk; 00225 if (offs_in_buf) { 00226 chunk = std::min(_bd_program_size - offs_in_buf, size); 00227 } else if (size >= _bd_program_size) { 00228 chunk = align_down(size, _bd_program_size); 00229 } else { 00230 chunk = size; 00231 } 00232 00233 const uint8_t *prog_buf; 00234 if (chunk < _bd_program_size) { 00235 // If cache not valid, and program doesn't cover an entire unit, it means we need to 00236 // read it from the underlying BD 00237 if (!_write_cache_valid) { 00238 ret = _bd->read(_write_cache, _write_cache_addr, _bd_program_size); 00239 if (ret) { 00240 return ret; 00241 } 00242 } 00243 memcpy(_write_cache + offs_in_buf, buf, chunk); 00244 prog_buf = _write_cache; 00245 } else { 00246 prog_buf = buf; 00247 } 00248 00249 // Only program if we reached the end of a program unit 00250 if (!((offs_in_buf + chunk) % _bd_program_size)) { 00251 ret = _bd->program(prog_buf, _write_cache_addr, std::max(chunk, _bd_program_size)); 00252 if (ret) { 00253 return ret; 00254 } 00255 invalidate_write_cache(); 00256 ret = _bd->sync(); 00257 if (ret) { 00258 return ret; 00259 } 00260 } else { 00261 _write_cache_valid = true; 00262 } 00263 00264 buf += chunk; 00265 addr += chunk; 00266 size -= chunk; 00267 } 00268 00269 return 0; 00270 } 00271 00272 int BufferedBlockDevice::erase(bd_addr_t addr, bd_size_t size) 00273 { 00274 MBED_ASSERT(is_valid_erase(addr, size)); 00275 if (!_is_initialized) { 00276 return BD_ERROR_DEVICE_ERROR; 00277 } 00278 00279 if ((_write_cache_addr >= addr) && (_write_cache_addr <= addr + size)) { 00280 invalidate_write_cache(); 00281 } 00282 return _bd->erase(addr, size); 00283 } 00284 00285 int BufferedBlockDevice::trim(bd_addr_t addr, bd_size_t size) 00286 { 00287 MBED_ASSERT(is_valid_erase(addr, size)); 00288 if (!_is_initialized) { 00289 return BD_ERROR_DEVICE_ERROR; 00290 } 00291 00292 if ((_write_cache_addr >= addr) && (_write_cache_addr <= addr + size)) { 00293 invalidate_write_cache(); 00294 } 00295 return _bd->trim(addr, size); 00296 } 00297 00298 bd_size_t BufferedBlockDevice::get_read_size() const 00299 { 00300 return 1; 00301 } 00302 00303 bd_size_t BufferedBlockDevice::get_program_size() const 00304 { 00305 return 1; 00306 } 00307 00308 bd_size_t BufferedBlockDevice::get_erase_size() const 00309 { 00310 if (!_is_initialized) { 00311 return 0; 00312 } 00313 00314 return _bd->get_erase_size(); 00315 } 00316 00317 bd_size_t BufferedBlockDevice::get_erase_size(bd_addr_t addr) const 00318 { 00319 if (!_is_initialized) { 00320 return 0; 00321 } 00322 00323 return _bd->get_erase_size(addr); 00324 } 00325 00326 int BufferedBlockDevice::get_erase_value() const 00327 { 00328 if (!_is_initialized) { 00329 return BD_ERROR_DEVICE_ERROR; 00330 } 00331 00332 return _bd->get_erase_value(); 00333 } 00334 00335 bd_size_t BufferedBlockDevice::size() const 00336 { 00337 if (!_is_initialized) { 00338 return 0; 00339 } 00340 00341 return _bd_size; 00342 } 00343 00344 const char *BufferedBlockDevice::get_type() const 00345 { 00346 return _bd->get_type(); 00347 } 00348 00349 } // namespace mbed
Generated on Tue Jul 12 2022 13:54:03 by
