1 /****************************************************************************
2 * boot/nuttx/src/flash_map_backend/flash_map_backend.c
3 *
4 * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 ****************************************************************************/
19
20 /****************************************************************************
21 * Included Files
22 ****************************************************************************/
23
24 #include <nuttx/config.h>
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <sys/ioctl.h>
32 #include <unistd.h>
33
34 #include <nuttx/fs/fs.h>
35 #include <nuttx/mtd/mtd.h>
36
37 #include <bootutil/bootutil_log.h>
38
39 #include "flash_map_backend/flash_map_backend.h"
40 #include "os/os_malloc.h"
41 #include "sysflash/sysflash.h"
42
43 /****************************************************************************
44 * Pre-processor Definitions
45 ****************************************************************************/
46
47 #define ARRAYSIZE(x) (sizeof((x)) / sizeof((x)[0]))
48
49 /****************************************************************************
50 * Private Types
51 ****************************************************************************/
52
53 struct flash_device_s
54 {
55 /* Reference to the flash area configuration parameters */
56
57 struct flash_area *fa_cfg;
58
59 /* Geometry characteristics of the underlying MTD device */
60
61 struct mtd_geometry_s mtdgeo;
62
63 /* Partition information */
64
65 struct partition_info_s partinfo;
66
67 int fd; /* File descriptor for an open flash area */
68 uint32_t refs; /* Reference counter */
69 uint8_t erase_state; /* Byte value of the flash erased state */
70 };
71
72 /****************************************************************************
73 * Private Data
74 ****************************************************************************/
75
76 static struct flash_area g_primary_img0 =
77 {
78 .fa_id = FLASH_AREA_IMAGE_PRIMARY(0),
79 .fa_device_id = 0,
80 .fa_off = 0,
81 .fa_size = 0,
82 .fa_mtd_path = CONFIG_MCUBOOT_PRIMARY_SLOT_PATH
83 };
84
85 static struct flash_device_s g_primary_priv =
86 {
87 .fa_cfg = &g_primary_img0,
88 .mtdgeo =
89 {
90 0
91 },
92 .partinfo =
93 {
94 0
95 },
96 .fd = -1,
97 .refs = 0,
98 .erase_state = CONFIG_MCUBOOT_DEFAULT_FLASH_ERASE_STATE
99 };
100
101 static struct flash_area g_secondary_img0 =
102 {
103 .fa_id = FLASH_AREA_IMAGE_SECONDARY(0),
104 .fa_device_id = 0,
105 .fa_off = 0,
106 .fa_size = 0,
107 .fa_mtd_path = CONFIG_MCUBOOT_SECONDARY_SLOT_PATH
108 };
109
110 static struct flash_device_s g_secondary_priv =
111 {
112 .fa_cfg = &g_secondary_img0,
113 .mtdgeo =
114 {
115 0
116 },
117 .partinfo =
118 {
119 0
120 },
121 .fd = -1,
122 .refs = 0,
123 .erase_state = CONFIG_MCUBOOT_DEFAULT_FLASH_ERASE_STATE
124 };
125
126 static struct flash_area g_scratch_img0 =
127 {
128 .fa_id = FLASH_AREA_IMAGE_SCRATCH,
129 .fa_device_id = 0,
130 .fa_off = 0,
131 .fa_size = 0,
132 .fa_mtd_path = CONFIG_MCUBOOT_SCRATCH_PATH
133 };
134
135 static struct flash_device_s g_scratch_priv =
136 {
137 .fa_cfg = &g_scratch_img0,
138 .mtdgeo =
139 {
140 0
141 },
142 .partinfo =
143 {
144 0
145 },
146 .fd = -1,
147 .refs = 0,
148 .erase_state = CONFIG_MCUBOOT_DEFAULT_FLASH_ERASE_STATE
149 };
150
151 static struct flash_device_s *g_flash_devices[] =
152 {
153 &g_primary_priv,
154 &g_secondary_priv,
155 &g_scratch_priv,
156 };
157
158 /****************************************************************************
159 * Private Functions
160 ****************************************************************************/
161
162 /****************************************************************************
163 * Name: lookup_flash_device_by_id
164 *
165 * Description:
166 * Retrieve flash device from a given flash area ID.
167 *
168 * Input Parameters:
169 * fa_id - ID of the flash area.
170 *
171 * Returned Value:
172 * Reference to the found flash device, or NULL in case it does not exist.
173 *
174 ****************************************************************************/
175
lookup_flash_device_by_id(uint8_t fa_id)176 static struct flash_device_s *lookup_flash_device_by_id(uint8_t fa_id)
177 {
178 size_t i;
179
180 for (i = 0; i < ARRAYSIZE(g_flash_devices); i++)
181 {
182 struct flash_device_s *dev = g_flash_devices[i];
183
184 if (fa_id == dev->fa_cfg->fa_id)
185 {
186 return dev;
187 }
188 }
189
190 return NULL;
191 }
192
193 /****************************************************************************
194 * Name: lookup_flash_device_by_offset
195 *
196 * Description:
197 * Retrieve flash device from a given flash area offset.
198 *
199 * Input Parameters:
200 * offset - Offset of the flash area.
201 *
202 * Returned Value:
203 * Reference to the found flash device, or NULL in case it does not exist.
204 *
205 ****************************************************************************/
206
lookup_flash_device_by_offset(uint32_t offset)207 static struct flash_device_s *lookup_flash_device_by_offset(uint32_t offset)
208 {
209 size_t i;
210
211 for (i = 0; i < ARRAYSIZE(g_flash_devices); i++)
212 {
213 struct flash_device_s *dev = g_flash_devices[i];
214
215 if (offset == dev->fa_cfg->fa_off)
216 {
217 return dev;
218 }
219 }
220
221 return NULL;
222 }
223
224 /****************************************************************************
225 * Public Functions
226 ****************************************************************************/
227
228 /****************************************************************************
229 * Name: flash_area_open
230 *
231 * Description:
232 * Retrieve flash area from the flash map for a given ID.
233 *
234 * Input Parameters:
235 * id - ID of the flash area.
236 *
237 * Output Parameters:
238 * fa - Pointer which will contain the reference to flash_area.
239 * If ID is unknown, it will be NULL on output.
240 *
241 * Returned Value:
242 * Zero on success, or negative value in case of error.
243 *
244 ****************************************************************************/
245
flash_area_open(uint8_t id,const struct flash_area ** fa)246 int flash_area_open(uint8_t id, const struct flash_area **fa)
247 {
248 struct flash_device_s *dev;
249 int fd;
250 int ret;
251
252 BOOT_LOG_INF("ID:%" PRIu8, id);
253
254 dev = lookup_flash_device_by_id(id);
255 if (dev == NULL)
256 {
257 BOOT_LOG_ERR("Undefined flash area: %" PRIu8, id);
258
259 return ERROR;
260 }
261
262 *fa = dev->fa_cfg;
263
264 if (dev->refs++ > 0)
265 {
266 BOOT_LOG_INF("Flash area ID %" PRIu8 " already open, count: %" PRIu32 " (+)",
267 id, dev->refs);
268
269 return OK;
270 }
271
272 fd = open(dev->fa_cfg->fa_mtd_path, O_RDWR);
273 if (fd < 0)
274 {
275 int errcode = errno;
276
277 BOOT_LOG_ERR("Error opening MTD device: %d", errcode);
278
279 goto errout;
280 }
281
282 ret = ioctl(fd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&dev->mtdgeo));
283 if (ret < 0)
284 {
285 int errcode = errno;
286
287 BOOT_LOG_ERR("Error retrieving MTD device geometry: %d", errcode);
288
289 goto errout_with_fd;
290 }
291
292 ret = ioctl(fd, BIOC_PARTINFO, (unsigned long)((uintptr_t)&dev->partinfo));
293 if (ret < 0)
294 {
295 int errcode = errno;
296
297 BOOT_LOG_ERR("Error retrieving MTD partition info: %d", errcode);
298
299 goto errout_with_fd;
300 }
301
302 ret = ioctl(fd, MTDIOC_ERASESTATE,
303 (unsigned long)((uintptr_t)&dev->erase_state));
304 if (ret < 0)
305 {
306 int errcode = errno;
307
308 BOOT_LOG_ERR("Error retrieving MTD device erase state: %d", errcode);
309
310 goto errout_with_fd;
311 }
312
313 dev->fa_cfg->fa_off = dev->partinfo.startsector * dev->partinfo.sectorsize;
314 dev->fa_cfg->fa_size = dev->partinfo.numsectors * dev->partinfo.sectorsize;
315
316 BOOT_LOG_INF("Flash area offset: 0x%" PRIx32, dev->fa_cfg->fa_off);
317 BOOT_LOG_INF("Flash area size: %" PRIu32, dev->fa_cfg->fa_size);
318 BOOT_LOG_INF("MTD erase state: 0x%" PRIx8, dev->erase_state);
319
320 dev->fd = fd;
321
322 BOOT_LOG_INF("Flash area %" PRIu8 " open, count: %" PRIu32 " (+)", id, dev->refs);
323
324 return OK;
325
326 errout_with_fd:
327 close(fd);
328
329 errout:
330 --dev->refs;
331
332 return ERROR;
333 }
334
335 /****************************************************************************
336 * Name: flash_area_close
337 *
338 * Description:
339 * Close a given flash area.
340 *
341 * Input Parameters:
342 * fa - Flash area to be closed.
343 *
344 * Returned Value:
345 * None.
346 *
347 ****************************************************************************/
348
flash_area_close(const struct flash_area * fa)349 void flash_area_close(const struct flash_area *fa)
350 {
351 BOOT_LOG_INF("ID:%" PRIu8, fa->fa_id);
352
353 struct flash_device_s *dev = lookup_flash_device_by_id(fa->fa_id);
354
355 DEBUGASSERT(dev != NULL);
356
357 if (dev->refs == 0)
358 {
359 /* No need to close an unopened flash area, avoid an overflow of the
360 * counter.
361 */
362
363 return;
364 }
365
366 BOOT_LOG_INF("Close request for flash area %" PRIu8 ", count: %" PRIu32 " (-)",
367 fa->fa_id, dev->refs);
368
369 if (--dev->refs == 0)
370 {
371 close(dev->fd);
372 dev->fd = -1;
373
374 BOOT_LOG_INF("Flash area %" PRIu8 " closed", fa->fa_id);
375 }
376 }
377
378 /****************************************************************************
379 * Name: flash_area_read
380 *
381 * Description:
382 * Read data from flash area.
383 * Area readout boundaries are asserted before read request. API has the
384 * same limitation regarding read-block alignment and size as the
385 * underlying flash driver.
386 *
387 * Input Parameters:
388 * fa - Flash area to be read.
389 * off - Offset relative from beginning of flash area to be read.
390 * len - Number of bytes to read.
391 *
392 * Output Parameters:
393 * dst - Buffer to store read data.
394 *
395 * Returned Value:
396 * Zero on success, or negative value in case of error.
397 *
398 ****************************************************************************/
399
flash_area_read(const struct flash_area * fa,uint32_t off,void * dst,uint32_t len)400 int flash_area_read(const struct flash_area *fa, uint32_t off,
401 void *dst, uint32_t len)
402 {
403 struct flash_device_s *dev;
404 off_t seekpos;
405 ssize_t nbytes;
406
407 BOOT_LOG_INF("ID:%" PRIu8 " offset:%" PRIu32 " length:%" PRIu32,
408 fa->fa_id, off, len);
409
410 dev = lookup_flash_device_by_id(fa->fa_id);
411
412 DEBUGASSERT(dev != NULL);
413
414 if (off + len > fa->fa_size)
415 {
416 BOOT_LOG_ERR("Attempt to read out of flash area bounds");
417
418 return ERROR;
419 }
420
421 /* Reposition the file offset from the beginning of the flash area */
422
423 seekpos = lseek(dev->fd, (off_t)off, SEEK_SET);
424 if (seekpos != (off_t)off)
425 {
426 int errcode = errno;
427
428 BOOT_LOG_ERR("Seek to offset %" PRIu32 " failed: %d", off, errcode);
429
430 return ERROR;
431 }
432
433 /* Read the flash block into memory */
434
435 nbytes = read(dev->fd, dst, len);
436 if (nbytes < 0)
437 {
438 int errcode = errno;
439
440 BOOT_LOG_ERR("Read from %s failed: %d", fa->fa_mtd_path, errcode);
441
442 return ERROR;
443 }
444
445 return OK;
446 }
447
448 /****************************************************************************
449 * Name: flash_area_write
450 *
451 * Description:
452 * Write data to flash area.
453 * Area write boundaries are asserted before write request. API has the
454 * same limitation regarding write-block alignment and size as the
455 * underlying flash driver.
456 *
457 * Input Parameters:
458 * fa - Flash area to be written.
459 * off - Offset relative from beginning of flash area to be written.
460 * src - Buffer with data to be written.
461 * len - Number of bytes to write.
462 *
463 * Returned Value:
464 * Zero on success, or negative value in case of error.
465 *
466 ****************************************************************************/
467
flash_area_write(const struct flash_area * fa,uint32_t off,const void * src,uint32_t len)468 int flash_area_write(const struct flash_area *fa, uint32_t off,
469 const void *src, uint32_t len)
470 {
471 struct flash_device_s *dev;
472 off_t seekpos;
473 ssize_t nbytes;
474
475 BOOT_LOG_INF("ID:%" PRIu8 " offset:%" PRIu32 " length:%" PRIu32,
476 fa->fa_id, off, len);
477
478 dev = lookup_flash_device_by_id(fa->fa_id);
479
480 DEBUGASSERT(dev != NULL);
481
482 if (off + len > fa->fa_size)
483 {
484 BOOT_LOG_ERR("Attempt to write out of flash area bounds");
485
486 return ERROR;
487 }
488
489 /* Reposition the file offset from the beginning of the flash area */
490
491 seekpos = lseek(dev->fd, (off_t)off, SEEK_SET);
492 if (seekpos != (off_t)off)
493 {
494 int errcode = errno;
495
496 BOOT_LOG_ERR("Seek to offset %" PRIu32 " failed: %d", off, errcode);
497
498 return ERROR;
499 }
500
501 /* Write the buffer to the flash block */
502
503 nbytes = write(dev->fd, src, len);
504 if (nbytes < 0)
505 {
506 int errcode = errno;
507
508 BOOT_LOG_ERR("Write to %s failed: %d", fa->fa_mtd_path, errcode);
509
510 return ERROR;
511 }
512
513 return OK;
514 }
515
516 /****************************************************************************
517 * Name: flash_area_erase
518 *
519 * Description:
520 * Erase a given flash area range.
521 * Area boundaries are asserted before erase request. API has the same
522 * limitation regarding erase-block alignment and size as the underlying
523 * flash driver.
524 *
525 * Input Parameters:
526 * fa - Flash area to be erased.
527 * off - Offset relative from beginning of flash area to be erased.
528 * len - Number of bytes to be erase.
529 *
530 * Returned Value:
531 * Zero on success, or negative value in case of error.
532 *
533 ****************************************************************************/
534
flash_area_erase(const struct flash_area * fa,uint32_t off,uint32_t len)535 int flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len)
536 {
537 int ret;
538 void *buffer;
539 size_t i;
540 struct flash_device_s *dev = lookup_flash_device_by_id(fa->fa_id);
541 const size_t sector_size = dev->mtdgeo.erasesize;
542 const uint8_t erase_val = dev->erase_state;
543
544 BOOT_LOG_INF("ID:%" PRIu8 " offset:%" PRIu32 " length:%" PRIu32,
545 fa->fa_id, off, len);
546
547 buffer = malloc(sector_size);
548 if (buffer == NULL)
549 {
550 BOOT_LOG_ERR("Failed to allocate erase buffer");
551
552 return ERROR;
553 }
554
555 memset(buffer, erase_val, sector_size);
556
557 i = 0;
558
559 do
560 {
561 BOOT_LOG_DBG("Erasing %zu bytes at offset %" PRIu32,
562 sector_size, off + i);
563
564 ret = flash_area_write(fa, off + i, buffer, sector_size);
565 i += sector_size;
566 }
567 while (ret == OK && i < (len - sector_size));
568
569 if (ret == OK)
570 {
571 BOOT_LOG_DBG("Erasing %" PRIu32 " bytes at offset %" PRIu32,
572 len - i, off + i);
573
574 ret = flash_area_write(fa, off + i, buffer, len - i);
575 }
576
577 free(buffer);
578
579 return ret;
580 }
581
582 /****************************************************************************
583 * Name: flash_area_align
584 *
585 * Description:
586 * Get write block size of the flash area.
587 * Write block size might be treated as read block size, although most
588 * drivers support unaligned readout.
589 *
590 * Input Parameters:
591 * fa - Flash area.
592 *
593 * Returned Value:
594 * Alignment restriction for flash writes in the given flash area.
595 *
596 ****************************************************************************/
597
flash_area_align(const struct flash_area * fa)598 uint32_t flash_area_align(const struct flash_area *fa)
599 {
600 /* MTD access alignment is handled by the character and block device
601 * drivers.
602 */
603
604 const uint32_t minimum_write_length = 1;
605
606 BOOT_LOG_INF("ID:%" PRIu8 " align:%" PRIu32,
607 fa->fa_id, minimum_write_length);
608
609 return minimum_write_length;
610 }
611
612 /****************************************************************************
613 * Name: flash_area_erased_val
614 *
615 * Description:
616 * Get the value expected to be read when accessing any erased flash byte.
617 * This API is compatible with the MCUboot's porting layer.
618 *
619 * Input Parameters:
620 * fa - Flash area.
621 *
622 * Returned Value:
623 * Byte value of erased memory.
624 *
625 ****************************************************************************/
626
flash_area_erased_val(const struct flash_area * fa)627 uint8_t flash_area_erased_val(const struct flash_area *fa)
628 {
629 struct flash_device_s *dev;
630 uint8_t erased_val;
631
632 dev = lookup_flash_device_by_id(fa->fa_id);
633
634 DEBUGASSERT(dev != NULL);
635
636 erased_val = dev->erase_state;
637
638 BOOT_LOG_INF("ID:%" PRIu8 " erased_val:0x%" PRIx8, fa->fa_id, erased_val);
639
640 return erased_val;
641 }
642
643 /****************************************************************************
644 * Name: flash_area_get_sectors
645 *
646 * Description:
647 * Retrieve info about sectors within the area.
648 *
649 * Input Parameters:
650 * fa_id - ID of the flash area whose info will be retrieved.
651 * count - On input, represents the capacity of the sectors buffer.
652 *
653 * Output Parameters:
654 * count - On output, it shall contain the number of retrieved sectors.
655 * sectors - Buffer for sectors data.
656 *
657 * Returned Value:
658 * Zero on success, or negative value in case of error.
659 *
660 ****************************************************************************/
661
flash_area_get_sectors(int fa_id,uint32_t * count,struct flash_sector * sectors)662 int flash_area_get_sectors(int fa_id, uint32_t *count,
663 struct flash_sector *sectors)
664 {
665 size_t off;
666 uint32_t total_count = 0;
667 struct flash_device_s *dev = lookup_flash_device_by_id(fa_id);
668 const size_t sector_size = dev->mtdgeo.erasesize;
669 const struct flash_area *fa = fa = dev->fa_cfg;
670
671 for (off = 0; off < fa->fa_size; off += sector_size)
672 {
673 /* Note: Offset here is relative to flash area, not device */
674
675 sectors[total_count].fs_off = off;
676 sectors[total_count].fs_size = sector_size;
677 total_count++;
678 }
679
680 *count = total_count;
681
682 DEBUGASSERT(total_count == dev->mtdgeo.neraseblocks);
683
684 BOOT_LOG_INF("ID:%d count:%" PRIu32, fa_id, *count);
685
686 return OK;
687 }
688
689 /****************************************************************************
690 * Name: flash_area_id_from_multi_image_slot
691 *
692 * Description:
693 * Return the flash area ID for a given slot and a given image index
694 * (in case of a multi-image setup).
695 *
696 * Input Parameters:
697 * image_index - Index of the image.
698 * slot - Image slot, which may be 0 (primary) or 1 (secondary).
699 *
700 * Returned Value:
701 * Flash area ID (0 or 1), or negative value in case the requested slot
702 * is invalid.
703 *
704 ****************************************************************************/
705
flash_area_id_from_multi_image_slot(int image_index,int slot)706 int flash_area_id_from_multi_image_slot(int image_index, int slot)
707 {
708 BOOT_LOG_INF("image_index:%d slot:%d", image_index, slot);
709
710 switch (slot)
711 {
712 case 0:
713 return FLASH_AREA_IMAGE_PRIMARY(image_index);
714 case 1:
715 return FLASH_AREA_IMAGE_SECONDARY(image_index);
716 }
717
718 BOOT_LOG_ERR("Unexpected Request: image_index:%d, slot:%d",
719 image_index, slot);
720
721 return ERROR; /* flash_area_open will fail on that */
722 }
723
724 /****************************************************************************
725 * Name: flash_area_id_from_image_slot
726 *
727 * Description:
728 * Return the flash area ID for a given slot.
729 *
730 * Input Parameters:
731 * slot - Image slot, which may be 0 (primary) or 1 (secondary).
732 *
733 * Returned Value:
734 * Flash area ID (0 or 1), or negative value in case the requested slot
735 * is invalid.
736 *
737 ****************************************************************************/
738
flash_area_id_from_image_slot(int slot)739 int flash_area_id_from_image_slot(int slot)
740 {
741 BOOT_LOG_INF("slot:%d", slot);
742
743 return flash_area_id_from_multi_image_slot(0, slot);
744 }
745
746 /****************************************************************************
747 * Name: flash_area_id_to_multi_image_slot
748 *
749 * Description:
750 * Convert the specified flash area ID and image index (in case of a
751 * multi-image setup) to an image slot index.
752 *
753 * Input Parameters:
754 * image_index - Index of the image.
755 * area_id - Unique identifier that is represented by fa_id in the
756 * flash_area struct.
757 * Returned Value:
758 * Image slot index (0 or 1), or negative value in case ID doesn't
759 * correspond to an image slot.
760 *
761 ****************************************************************************/
762
flash_area_id_to_multi_image_slot(int image_index,int area_id)763 int flash_area_id_to_multi_image_slot(int image_index, int area_id)
764 {
765 BOOT_LOG_INF("image_index:%d area_id:%d", image_index, area_id);
766
767 if (area_id == FLASH_AREA_IMAGE_PRIMARY(image_index))
768 {
769 return 0;
770 }
771
772 if (area_id == FLASH_AREA_IMAGE_SECONDARY(image_index))
773 {
774 return 1;
775 }
776
777 BOOT_LOG_ERR("Unexpected Request: image_index:%d, area_id:%d",
778 image_index, area_id);
779
780 return ERROR; /* flash_area_open will fail on that */
781 }
782
783 /****************************************************************************
784 * Name: flash_area_id_from_image_offset
785 *
786 * Description:
787 * Return the flash area ID for a given image offset.
788 *
789 * Input Parameters:
790 * offset - Image offset.
791 *
792 * Returned Value:
793 * Flash area ID (0 or 1), or negative value in case the requested offset
794 * is invalid.
795 *
796 ****************************************************************************/
797
flash_area_id_from_image_offset(uint32_t offset)798 int flash_area_id_from_image_offset(uint32_t offset)
799 {
800 struct flash_device_s *dev = lookup_flash_device_by_offset(offset);
801
802 BOOT_LOG_INF("offset:%" PRIu32, offset);
803
804 if (dev != NULL)
805 {
806 return dev->fa_cfg->fa_id;
807 }
808
809 BOOT_LOG_ERR("Unexpected Request: offset:%" PRIu32, offset);
810
811 return ERROR; /* flash_area_open will fail on that */
812 }
813
814 /****************************************************************************
815 * Name: flash_area_get_sector
816 *
817 * Description:
818 * Retrieve the flash sector a given offset belongs to.
819 *
820 * Input Parameters:
821 * fap - flash area structure
822 * off - address offset.
823 * sector - flash sector
824 *
825 * Returned Value:
826 * Returns 0 on success, or an error code on failure.
827 *
828 ****************************************************************************/
829
flash_area_get_sector(const struct flash_area * fap,off_t off,struct flash_sector * fs)830 int flash_area_get_sector(const struct flash_area *fap, off_t off,
831 struct flash_sector *fs)
832 {
833 off_t offset = fap->fa_off + off;
834 struct flash_device_s *dev = lookup_flash_device_by_offset(offset);
835 if (dev == NULL)
836 {
837 return -errno;
838 }
839
840 fs->fs_off = (offset / dev->mtdgeo.erasesize) * dev->mtdgeo.erasesize;
841 fs->fs_size = dev->mtdgeo.erasesize;
842
843 return 0;
844 }
845