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