takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

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 "mbed_assert.h"
00019 #include "mbed_critical.h"
00020 #include <algorithm>
00021 #include <string.h>
00022 
00023 static inline uint32_t align_down(bd_size_t val, bd_size_t size)
00024 {
00025     return val / size * size;
00026 }
00027 
00028 BufferedBlockDevice::BufferedBlockDevice(BlockDevice *bd)
00029     : _bd(bd), _bd_program_size(0), _curr_aligned_addr(0), _flushed(true), _cache(0), _init_ref_count(0), _is_initialized(false)
00030 {
00031 }
00032 
00033 BufferedBlockDevice::~BufferedBlockDevice()
00034 {
00035     deinit();
00036 }
00037 
00038 int BufferedBlockDevice::init()
00039 {
00040     uint32_t val = core_util_atomic_incr_u32(&_init_ref_count, 1);
00041 
00042     if (val != 1) {
00043         return BD_ERROR_OK;
00044     }
00045 
00046     int err = _bd->init();
00047     if (err) {
00048         return err;
00049     }
00050 
00051     _bd_program_size = _bd->get_program_size();
00052 
00053     if (!_cache) {
00054         _cache = new uint8_t[_bd_program_size];
00055     }
00056 
00057     _curr_aligned_addr = _bd->size();
00058     _flushed = true;
00059 
00060     _is_initialized = true;
00061     return BD_ERROR_OK;
00062 }
00063 
00064 int BufferedBlockDevice::deinit()
00065 {
00066     if (!_is_initialized) {
00067         return BD_ERROR_OK;
00068     }
00069 
00070     uint32_t val = core_util_atomic_decr_u32(&_init_ref_count, 1);
00071 
00072     if (val) {
00073         return BD_ERROR_OK;
00074     }
00075 
00076     delete[] _cache;
00077     _cache = 0;
00078     _is_initialized = false;
00079     return _bd->deinit();
00080 }
00081 
00082 int BufferedBlockDevice::flush()
00083 {
00084     if (!_is_initialized) {
00085         return BD_ERROR_DEVICE_ERROR;
00086     }
00087 
00088     if (!_flushed) {
00089         int ret = _bd->program(_cache, _curr_aligned_addr, _bd_program_size);
00090         if (ret) {
00091             return ret;
00092         }
00093         _flushed = true;
00094     }
00095     return 0;
00096 }
00097 
00098 int BufferedBlockDevice::sync()
00099 {
00100     if (!_is_initialized) {
00101         return BD_ERROR_DEVICE_ERROR;
00102     }
00103 
00104     int ret = flush();
00105     if (ret) {
00106         return ret;
00107     }
00108     return _bd->sync();
00109 }
00110 
00111 int BufferedBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
00112 {
00113     MBED_ASSERT(_cache);
00114     if (!_is_initialized) {
00115         return BD_ERROR_DEVICE_ERROR;
00116     }
00117 
00118     bool moved_unit = false;
00119 
00120     bd_addr_t aligned_addr = align_down(addr, _bd_program_size);
00121 
00122     uint8_t *buf = static_cast<uint8_t *> (b);
00123 
00124     if (aligned_addr != _curr_aligned_addr) {
00125         // Need to flush if moved to another program unit
00126         flush();
00127         _curr_aligned_addr = aligned_addr;
00128         moved_unit = true;
00129     }
00130 
00131     while (size) {
00132         _curr_aligned_addr = align_down(addr, _bd_program_size);
00133         if (moved_unit) {
00134             int ret = _bd->read(_cache, _curr_aligned_addr, _bd_program_size);
00135             if (ret) {
00136                 return ret;
00137             }
00138         }
00139         bd_addr_t offs_in_buf = addr - _curr_aligned_addr;
00140         bd_size_t chunk = std::min(_bd_program_size - offs_in_buf, size);
00141         memcpy(buf, _cache + offs_in_buf, chunk);
00142         moved_unit = true;
00143         buf += chunk;
00144         addr += chunk;
00145         size -= chunk;
00146     }
00147 
00148     return 0;
00149 }
00150 
00151 int BufferedBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
00152 {
00153     if (!_is_initialized) {
00154         return BD_ERROR_DEVICE_ERROR;
00155     }
00156 
00157     int ret;
00158     bool moved_unit = false;
00159 
00160     bd_addr_t aligned_addr = align_down(addr, _bd_program_size);
00161 
00162     const uint8_t *buf = static_cast <const uint8_t *> (b);
00163 
00164     // Need to flush if moved to another program unit
00165     if (aligned_addr != _curr_aligned_addr) {
00166         flush();
00167         _curr_aligned_addr = aligned_addr;
00168         moved_unit = true;
00169     }
00170 
00171     while (size) {
00172         _curr_aligned_addr = align_down(addr, _bd_program_size);
00173         bd_addr_t offs_in_buf = addr - _curr_aligned_addr;
00174         bd_size_t chunk = std::min(_bd_program_size - offs_in_buf, size);
00175         const uint8_t *prog_buf;
00176         if (chunk < _bd_program_size) {
00177             // If moved a unit, and program doesn't cover entire unit, it means we don't have the entire
00178             // program unit cached - need to complete it from underlying BD
00179             if (moved_unit) {
00180                 ret = _bd->read(_cache, _curr_aligned_addr, _bd_program_size);
00181                 if (ret) {
00182                     return ret;
00183                 }
00184             }
00185             memcpy(_cache + offs_in_buf, buf, chunk);
00186             prog_buf = _cache;
00187         } else {
00188             // No need to copy data to our cache on each iteration. Just make sure it's updated
00189             // on the last iteration, when size is not greater than program size (can't be smaller, as
00190             // this is covered in the previous condition).
00191             prog_buf = buf;
00192             if (size == _bd_program_size) {
00193                 memcpy(_cache, buf, _bd_program_size);
00194             }
00195         }
00196 
00197         // Don't flush on the last iteration, just on all preceding ones.
00198         if (size > chunk) {
00199             ret = _bd->program(prog_buf, _curr_aligned_addr, _bd_program_size);
00200             if (ret) {
00201                 return ret;
00202             }
00203             _bd->sync();
00204         } else {
00205             _flushed = false;
00206         }
00207 
00208         moved_unit = true;
00209         buf += chunk;
00210         addr += chunk;
00211         size -= chunk;
00212     }
00213 
00214     return 0;
00215 }
00216 
00217 int BufferedBlockDevice::erase(bd_addr_t addr, bd_size_t size)
00218 {
00219     MBED_ASSERT(is_valid_erase(addr, size));
00220     if (!_is_initialized) {
00221         return BD_ERROR_DEVICE_ERROR;
00222     }
00223 
00224     return _bd->erase(addr, size);
00225 }
00226 
00227 int BufferedBlockDevice::trim(bd_addr_t addr, bd_size_t size)
00228 {
00229     MBED_ASSERT(is_valid_erase(addr, size));
00230     if (!_is_initialized) {
00231         return BD_ERROR_DEVICE_ERROR;
00232     }
00233 
00234     if ((_curr_aligned_addr >= addr) && (_curr_aligned_addr <= addr + size)) {
00235         _flushed = true;
00236         _curr_aligned_addr = _bd->size();
00237     }
00238     return _bd->trim(addr, size);
00239 }
00240 
00241 bd_size_t BufferedBlockDevice::get_read_size() const
00242 {
00243     return 1;
00244 }
00245 
00246 bd_size_t BufferedBlockDevice::get_program_size() const
00247 {
00248     return 1;
00249 }
00250 
00251 bd_size_t BufferedBlockDevice::get_erase_size() const
00252 {
00253     if (!_is_initialized) {
00254         return BD_ERROR_DEVICE_ERROR;
00255     }
00256 
00257     return _bd->get_erase_size();
00258 }
00259 
00260 bd_size_t BufferedBlockDevice::get_erase_size(bd_addr_t addr) const
00261 {
00262     if (!_is_initialized) {
00263         return BD_ERROR_DEVICE_ERROR;
00264     }
00265 
00266     return _bd->get_erase_size(addr);
00267 }
00268 
00269 int BufferedBlockDevice::get_erase_value() const
00270 {
00271     if (!_is_initialized) {
00272         return BD_ERROR_DEVICE_ERROR;
00273     }
00274 
00275     return _bd->get_erase_value();
00276 }
00277 
00278 bd_size_t BufferedBlockDevice::size() const
00279 {
00280     if (!_is_initialized) {
00281         return BD_ERROR_DEVICE_ERROR;
00282     }
00283 
00284     return _bd->size();
00285 }