Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers BufferedBlockDevice.cpp Source File

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