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.
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 }
Generated on Tue Aug 9 2022 00:37:03 by
