Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:14

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup libblock
0007  *
0008  * @brief Flash Disk Block Device Implementation
0009  */
0010 
0011 /*
0012  * Copyright (C) 2007 Chris Johns
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 /*
0037  * The use of pages can vary. The rtems_fdisk_seg_*_page set
0038  * routines use an absolute page number relative to the segment
0039  * while all other page numbers are relative to the number of
0040  * page descriptor pages a segment has. You need to add the
0041  * number of page descriptor pages (pages_desc) to the page number
0042  * when call the rtems_fdisk_seg_*_page functions.
0043  *
0044  * You must always show the page number as relative in any trace
0045  * or error message as device-segment-page and if you have to
0046  * the page number as absolute use device-segment~page. This
0047  * can be seen in the page copy routine.
0048  *
0049  * The code is like this to avoid needing the pass the pages_desc
0050  * value around. It is only used in selected places and so the
0051  * extra parameter was avoided.
0052  */
0053 
0054 #ifdef HAVE_CONFIG_H
0055 #include "config.h"
0056 #endif
0057 
0058 #include <rtems.h>
0059 #include <rtems/libio.h>
0060 #include <errno.h>
0061 #include <stdlib.h>
0062 #include <stdio.h>
0063 #include <string.h>
0064 #include <unistd.h>
0065 #include <inttypes.h>
0066 
0067 #include <rtems/blkdev.h>
0068 #include <rtems/flashdisk.h>
0069 #include <rtems/thread.h>
0070 
0071 /**
0072  * Control tracing. It can be compiled out of the code for small
0073  * footprint targets. Leave in by default.
0074  */
0075 #if !defined (RTEMS_FDISK_TRACE)
0076 #define RTEMS_FDISK_TRACE 1
0077 #endif
0078 
0079 /**
0080  * The start of a segment has a segment control table. This hold the CRC and
0081  * block number for the page.
0082  *
0083  * @todo A sequence number for the block could be added. This would
0084  *       mean a larger descriptor size. Need to make the sequence
0085  *       large like 20+ bits so a large file system would not have
0086  *       more blocks available than the sequence number.
0087  */
0088 typedef struct rtems_fdisk_page_desc
0089 {
0090   uint16_t crc;       /**< The page's checksum. */
0091   uint16_t flags;     /**< The flags for the page. */
0092   uint32_t block;     /**< The block number. */
0093 } rtems_fdisk_page_desc;
0094 
0095 /**
0096  * Flag the page as active.
0097  */
0098 #define RTEMS_FDISK_PAGE_ACTIVE (1 << 0)
0099 
0100 /**
0101  * Flag the page as used.
0102  */
0103 #define RTEMS_FDISK_PAGE_USED (1 << 1)
0104 
0105 /**
0106  * Flash Segment Control holds the pointer to the segment, number of
0107  * pages, various page stats and the memory copy of the page descriptors.
0108  */
0109 typedef struct rtems_fdisk_segment_ctl
0110 {
0111   /**
0112    * Segments with available pages are maintained as a linked list.
0113    */
0114   struct rtems_fdisk_segment_ctl* next;
0115 
0116   /**
0117    * The descriptor provided by the low-level driver.
0118    */
0119   const rtems_fdisk_segment_desc* descriptor;
0120 
0121   /**
0122    * The device this segment resides on.
0123    */
0124   uint32_t device;
0125 
0126   /**
0127    * The segment in the device. This must be within the
0128    * segment descriptor.
0129    */
0130   uint32_t segment;
0131 
0132   /**
0133    * The in-memory ocpy of the page descriptors found at
0134    * the start of the segment in the flash device.
0135    */
0136   rtems_fdisk_page_desc* page_descriptors;
0137 
0138   /*
0139    * Page stats.
0140    *
0141    * A bad page does not checksum or is not erased or has invalid flags.
0142    */
0143   uint32_t pages;         /**< Total number of pages in the segment. */
0144   uint32_t pages_desc;    /**< Number of pages used for page descriptors. */
0145   uint32_t pages_active;  /**< Number of pages flagged as active. */
0146   uint32_t pages_used;    /**< Number of pages flagged as used. */
0147   uint32_t pages_bad;     /**< Number of pages detected as bad. */
0148 
0149   uint32_t failed;        /**< The segment has failed. */
0150 
0151   uint32_t erased;        /**< Counter to debugging. Wear support would
0152                                remove this. */
0153 } rtems_fdisk_segment_ctl;
0154 
0155 /**
0156  * Segment control table queue.
0157  */
0158 typedef struct rtems_fdisk_segment_ctl_queue
0159 {
0160   rtems_fdisk_segment_ctl* head;
0161   rtems_fdisk_segment_ctl* tail;
0162   uint32_t                 count;
0163 } rtems_fdisk_segment_ctl_queue;
0164 
0165 /**
0166  * Flash Device Control holds the segment controls
0167  */
0168 typedef struct rtems_fdisk_device_ctl
0169 {
0170   rtems_fdisk_segment_ctl*       segments;      /**< Segment controls. */
0171   uint32_t                       segment_count; /**< Segment control count. */
0172   const rtems_fdisk_device_desc* descriptor;    /**< Device descriptor. */
0173 } rtems_fdisk_device_ctl;
0174 
0175 /**
0176  * The Block control holds the segment and page with the data.
0177  */
0178 typedef struct rtems_fdisk_block_ctl
0179 {
0180   rtems_fdisk_segment_ctl* segment; /**< The segment with the block. */
0181   uint32_t                 page;    /**< The page in the segment. */
0182 } rtems_fdisk_block_ctl;
0183 
0184 /**
0185  * The virtual block table holds the mapping for blocks as seen by the disk
0186  * drivers to the device, segment and page numbers of the physical device.
0187  */
0188 typedef struct rtems_flashdisk
0189 {
0190   rtems_device_major_number major;         /**< The driver's major number. */
0191   rtems_device_minor_number minor;         /**< The driver's minor number. */
0192 
0193   uint32_t flags;                          /**< configuration flags. */
0194 
0195   uint32_t compact_segs;                   /**< Max segs to compact at once. */
0196   uint32_t avail_compact_segs;             /**< The number of segments when
0197                                                 compaction occurs when writing. */
0198 
0199   uint32_t               block_size;       /**< The block size for this disk. */
0200   rtems_fdisk_block_ctl* blocks;           /**< The block to segment-page
0201                                                 mappings. */
0202   uint32_t block_count;                    /**< The number of avail. blocks. */
0203   uint32_t unavail_blocks;                 /**< The number of unavail blocks. */
0204   uint32_t starvation_threshold;           /**< Erased blocks starvation threshold. */
0205   uint32_t erased_blocks;                  /**< The number of erased blocks. */
0206 
0207   rtems_fdisk_device_ctl* devices;         /**< The flash devices for this
0208                                                 disk. */
0209   uint32_t                device_count;    /**< The number of flash devices. */
0210 
0211   rtems_fdisk_segment_ctl_queue available; /**< The queue of segments with
0212                                                 available pages. */
0213   rtems_fdisk_segment_ctl_queue used;      /**< The list of segments with all
0214                                                 pages used. */
0215   rtems_fdisk_segment_ctl_queue erase;     /**< The list of segments to be
0216                                                 erased. */
0217   rtems_fdisk_segment_ctl_queue failed;    /**< The list of segments that failed
0218                                                 when being erased. */
0219   rtems_mutex lock;                        /**< Mutex for threading protection.*/
0220 
0221   uint8_t* copy_buffer;                    /**< Copy buf used during compacting */
0222 
0223   uint32_t info_level;                     /**< The info trace level. */
0224 
0225   uint32_t starvations;                    /**< Erased blocks starvations counter. */
0226 } rtems_flashdisk;
0227 
0228 /**
0229  * The CRC16 factor table. Created during initialisation.
0230  */
0231 static uint16_t* rtems_fdisk_crc16_factor;
0232 
0233 /**
0234  * Calculate the CRC16 checksum.
0235  *
0236  * @param _b The byte to checksum.
0237  * @param _c The current checksum.
0238  */
0239 #define rtems_fdisk_calc_crc16(_b, _c) \
0240   rtems_fdisk_crc16_factor[((_b) ^ ((_c) & 0xff)) & 0xff] ^ (((_c) >> 8) & 0xff)
0241 
0242 /**
0243  * Generate the CRC table.
0244  *
0245  * @param pattern The seed pattern for the table of factors.
0246  * @relval RTEMS_SUCCESSFUL The table was generated.
0247  * @retval RTEMS_NO_MEMORY The table could not be allocated from the heap.
0248  */
0249 static rtems_status_code
0250 rtems_fdisk_crc16_gen_factors (uint16_t pattern)
0251 {
0252   uint32_t b;
0253 
0254   rtems_fdisk_crc16_factor = malloc (sizeof (uint16_t) * 256);
0255   if (!rtems_fdisk_crc16_factor)
0256     return RTEMS_NO_MEMORY;
0257 
0258   for (b = 0; b < 256; b++)
0259   {
0260     uint32_t i;
0261     uint16_t v = b;
0262     for (i = 8; i--;)
0263       v = v & 1 ? (v >> 1) ^ pattern : v >> 1;
0264     rtems_fdisk_crc16_factor[b] = v & 0xffff;
0265   }
0266   return RTEMS_SUCCESSFUL;
0267 }
0268 
0269 #if RTEMS_FDISK_TRACE
0270 /**
0271  * Print a message to the flash disk output and flush it.
0272  *
0273  * @param fd The flashdisk control structure.
0274  * @param format The format string. See printf for details.
0275  * @param ... The arguments for the format text.
0276  * @return int The number of bytes written to the output.
0277  */
0278 static int
0279 rtems_fdisk_printf (const rtems_flashdisk* fd, const char *format, ...)
0280 {
0281   int ret = 0;
0282   if (fd->info_level >= 3)
0283   {
0284     va_list args;
0285     va_start (args, format);
0286     fprintf (stdout, "fdisk:");
0287     ret =  vfprintf (stdout, format, args);
0288     fprintf (stdout, "\n");
0289     fflush (stdout);
0290     va_end (args);
0291   }
0292   return ret;
0293 }
0294 
0295 static bool
0296 rtems_fdisk_is_erased_blocks_starvation (rtems_flashdisk* fd)
0297 {
0298   bool starvation = fd->erased_blocks < fd->starvation_threshold;
0299 
0300   if (starvation)
0301     fd->starvations++;
0302 
0303   return starvation;
0304 }
0305 
0306 /**
0307  * Print a info message to the flash disk output and flush it.
0308  *
0309  * @param fd The flashdisk control structure.
0310  * @param format The format string. See printf for details.
0311  * @param ... The arguments for the format text.
0312  * @return int The number of bytes written to the output.
0313  */
0314 static int
0315 rtems_fdisk_info (const rtems_flashdisk* fd, const char *format, ...)
0316 {
0317   int ret = 0;
0318   if (fd->info_level >= 2)
0319   {
0320     va_list args;
0321     va_start (args, format);
0322     fprintf (stdout, "fdisk:");
0323     ret =  vfprintf (stdout, format, args);
0324     fprintf (stdout, "\n");
0325     fflush (stdout);
0326     va_end (args);
0327   }
0328   return ret;
0329 }
0330 
0331 /**
0332  * Print a warning to the flash disk output and flush it.
0333  *
0334  * @param fd The flashdisk control structure.
0335  * @param format The format string. See printf for details.
0336  * @param ... The arguments for the format text.
0337  * @return int The number of bytes written to the output.
0338  */
0339 static int
0340 rtems_fdisk_warning (const rtems_flashdisk* fd, const char *format, ...)
0341 {
0342   int ret = 0;
0343   if (fd->info_level >= 1)
0344   {
0345     va_list args;
0346     va_start (args, format);
0347     fprintf (stdout, "fdisk:warning:");
0348     ret =  vfprintf (stdout, format, args);
0349     fprintf (stdout, "\n");
0350     fflush (stdout);
0351     va_end (args);
0352   }
0353   return ret;
0354 }
0355 #endif
0356 
0357 /**
0358  * Print an error to the flash disk output and flush it.
0359  *
0360  * @param format The format string. See printf for details.
0361  * @param ... The arguments for the format text.
0362  * @return int The number of bytes written to the output.
0363  */
0364 static int
0365 rtems_fdisk_error (const char *format, ...)
0366 {
0367   int ret;
0368   va_list args;
0369   va_start (args, format);
0370   fprintf (stderr, "fdisk:error:");
0371   ret =  vfprintf (stderr, format, args);
0372   fprintf (stderr, "\n");
0373   fflush (stderr);
0374   va_end (args);
0375   return ret;
0376 }
0377 
0378 /**
0379  * Print an abort message, flush it then abort the program.
0380  *
0381  * @param format The format string. See printf for details.
0382  * @param ... The arguments for the format text.
0383  */
0384 static void
0385 rtems_fdisk_abort (const char *format, ...)
0386 {
0387   va_list args;
0388   va_start (args, format);
0389   fprintf (stderr, "fdisk:abort:");
0390   vfprintf (stderr, format, args);
0391   fprintf (stderr, "\n");
0392   fflush (stderr);
0393   va_end (args);
0394   exit (1);
0395 }
0396 
0397 /**
0398  * Initialise the segment control queue.
0399  */
0400 static void
0401 rtems_fdisk_segment_queue_init (rtems_fdisk_segment_ctl_queue* queue)
0402 {
0403   queue->head = queue->tail = 0;
0404   queue->count = 0;
0405 }
0406 
0407 /**
0408  * Push to the head of the segment control queue.
0409  */
0410 static void
0411 rtems_fdisk_segment_queue_push_head (rtems_fdisk_segment_ctl_queue* queue,
0412                                      rtems_fdisk_segment_ctl*       sc)
0413 {
0414   if (sc)
0415   {
0416     sc->next = queue->head;
0417     queue->head = sc;
0418 
0419     if (queue->tail == 0)
0420       queue->tail = sc;
0421     queue->count++;
0422   }
0423 }
0424 
0425 /**
0426  * Pop the head of the segment control queue.
0427  */
0428 static rtems_fdisk_segment_ctl*
0429 rtems_fdisk_segment_queue_pop_head (rtems_fdisk_segment_ctl_queue* queue)
0430 {
0431   if (queue->head)
0432   {
0433     rtems_fdisk_segment_ctl* sc = queue->head;
0434 
0435     queue->head = sc->next;
0436     if (!queue->head)
0437       queue->tail = 0;
0438 
0439     queue->count--;
0440 
0441     sc->next = 0;
0442 
0443     return sc;
0444   }
0445 
0446   return 0;
0447 }
0448 
0449 /**
0450  * Push to the tail of the segment control queue.
0451  */
0452 static void
0453 rtems_fdisk_segment_queue_push_tail (rtems_fdisk_segment_ctl_queue* queue,
0454                                      rtems_fdisk_segment_ctl*       sc)
0455 {
0456   if (sc)
0457   {
0458     sc->next = 0;
0459 
0460     if (queue->head)
0461     {
0462       queue->tail->next = sc;
0463       queue->tail       = sc;
0464     }
0465     else
0466     {
0467       queue->head = queue->tail = sc;
0468     }
0469 
0470     queue->count++;
0471   }
0472 }
0473 
0474 /**
0475  * Remove from the segment control queue.
0476  */
0477 static void
0478 rtems_fdisk_segment_queue_remove (rtems_fdisk_segment_ctl_queue* queue,
0479                                   rtems_fdisk_segment_ctl*       sc)
0480 {
0481   rtems_fdisk_segment_ctl* prev = 0;
0482   rtems_fdisk_segment_ctl* it = queue->head;
0483 
0484   /*
0485    * Do not change sc->next as sc could be on another queue.
0486    */
0487 
0488   while (it)
0489   {
0490     if (sc == it)
0491     {
0492       if (prev == 0)
0493       {
0494         queue->head = sc->next;
0495         if (queue->head == 0)
0496           queue->tail = 0;
0497       }
0498       else
0499       {
0500         prev->next = sc->next;
0501         if (queue->tail == sc)
0502           queue->tail = prev;
0503       }
0504       sc->next = 0;
0505       queue->count--;
0506       break;
0507     }
0508 
0509     prev = it;
0510     it = it->next;
0511   }
0512 }
0513 
0514 /**
0515  * Insert into the segment control queue before the specific
0516  * segment control item.
0517  */
0518 static void
0519 rtems_fdisk_segment_queue_insert_before (rtems_fdisk_segment_ctl_queue* queue,
0520                                          rtems_fdisk_segment_ctl*       item,
0521                                          rtems_fdisk_segment_ctl*       sc)
0522 {
0523   if (item)
0524   {
0525     rtems_fdisk_segment_ctl** prev = &queue->head;
0526     rtems_fdisk_segment_ctl*  it = queue->head;
0527 
0528     while (it)
0529     {
0530       if (item == it)
0531       {
0532         sc->next = item;
0533         *prev = sc;
0534         queue->count++;
0535         return;
0536       }
0537 
0538       prev = &it->next;
0539       it = it->next;
0540     }
0541   }
0542 
0543   rtems_fdisk_segment_queue_push_tail (queue, sc);
0544 }
0545 
0546 /**
0547  * Count the number of elements on the list.
0548  */
0549 static uint32_t
0550 rtems_fdisk_segment_queue_count (rtems_fdisk_segment_ctl_queue* queue)
0551 {
0552   return queue->count;
0553 }
0554 
0555 /**
0556  * Count the number of elements on the list.
0557  */
0558 static uint32_t
0559 rtems_fdisk_segment_count_queue (rtems_fdisk_segment_ctl_queue* queue)
0560 {
0561   rtems_fdisk_segment_ctl* sc = queue->head;
0562   uint32_t                 count = 0;
0563 
0564   while (sc)
0565   {
0566     count++;
0567     sc = sc->next;
0568   }
0569 
0570   return count;
0571 }
0572 
0573 /**
0574  * See if a segment control is present on this queue.
0575  */
0576 static bool
0577 rtems_fdisk_segment_queue_present (rtems_fdisk_segment_ctl_queue* queue,
0578                                    rtems_fdisk_segment_ctl*       sc)
0579 {
0580   rtems_fdisk_segment_ctl*  it = queue->head;
0581 
0582   while (it)
0583   {
0584     if (it == sc)
0585       return true;
0586     it = it->next;
0587   }
0588 
0589   return false;
0590 }
0591 
0592 /**
0593  * Format a string with the queue status.
0594  */
0595 static void
0596 rtems_fdisk_queue_status (rtems_flashdisk*         fd,
0597                           rtems_fdisk_segment_ctl* sc,
0598                           char                     queues[5])
0599 {
0600   queues[0] = rtems_fdisk_segment_queue_present (&fd->available, sc) ? 'A' : '-';
0601   queues[1] = rtems_fdisk_segment_queue_present (&fd->used, sc)      ? 'U' : '-';
0602   queues[2] = rtems_fdisk_segment_queue_present (&fd->erase, sc)     ? 'E' : '-';
0603   queues[3] = rtems_fdisk_segment_queue_present (&fd->failed, sc)    ? 'F' : '-';
0604   queues[4] = '\0';
0605 }
0606 
0607 /**
0608  * Check if the page descriptor is erased.
0609  */
0610 static bool
0611 rtems_fdisk_page_desc_erased (const rtems_fdisk_page_desc* pd)
0612 {
0613   return ((pd->crc == 0xffff) &&
0614           (pd->flags == 0xffff) &&
0615           (pd->block == 0xffffffff)) ? true : false;
0616 }
0617 
0618 /**
0619  * Check if the flags are set. The flags are inverted as we can
0620  * only set a flag by changing it from 1 to 0.
0621  */
0622 static bool
0623 rtems_fdisk_page_desc_flags_set (rtems_fdisk_page_desc* pd, uint16_t flags)
0624 {
0625   return (pd->flags & flags) == 0 ? true : false;
0626 }
0627 
0628 /**
0629  * Check if the flags are clear. The flags are inverted as we can
0630  * only set a flag by changing it from 1 to 0.
0631  */
0632 static bool
0633 rtems_fdisk_page_desc_flags_clear (rtems_fdisk_page_desc* pd, uint16_t flags)
0634 {
0635   return (pd->flags & flags) == flags ? true : false;
0636 }
0637 
0638 /**
0639  * Set the flags. Setting means clear the bit to 0.
0640  */
0641 static void
0642 rtems_fdisk_page_desc_set_flags (rtems_fdisk_page_desc* pd, uint16_t flags)
0643 {
0644   pd->flags &= ~flags;
0645 }
0646 
0647 /**
0648  * Get the segment descriptor for a device and segment. There are
0649  * no range checks.
0650  */
0651 static const rtems_fdisk_segment_desc*
0652 rtems_fdisk_seg_descriptor (const rtems_flashdisk* fd,
0653                             uint32_t               device,
0654                             uint32_t               segment)
0655 {
0656   return fd->devices[device].segments[segment].descriptor;
0657 }
0658 
0659 /**
0660  * Count the segments for a device.
0661  */
0662 static uint32_t
0663 rtems_fdisk_count_segments (const rtems_fdisk_device_desc* dd)
0664 {
0665   uint32_t count = 0;
0666   uint32_t segment;
0667   for (segment = 0; segment < dd->segment_count; segment++)
0668     count += dd->segments[segment].count;
0669   return count;
0670 }
0671 
0672 /**
0673  * Calculate the pages in a segment give the segment size and the
0674  * page size.
0675  *
0676  * @param sd The segment descriptor.
0677  * @param page_size The page size in bytes.
0678  */
0679 static uint32_t
0680 rtems_fdisk_pages_in_segment (const rtems_fdisk_segment_desc* sd,
0681                               uint32_t                        page_size)
0682 {
0683   return sd->size / page_size;
0684 }
0685 
0686 /**
0687  * Calculate the number of pages needed to hold the page descriptors.
0688  * The calculation need to round up.
0689  *
0690  * The segment control contains the number of pages used as descriptors
0691  * and should be used rather than this call where possible.
0692  */
0693 static uint32_t
0694 rtems_fdisk_page_desc_pages (const rtems_fdisk_segment_desc* sd,
0695                              uint32_t                        page_size)
0696 {
0697   uint32_t pages = rtems_fdisk_pages_in_segment (sd, page_size);
0698   uint32_t bytes = pages * sizeof (rtems_fdisk_page_desc);
0699   return ((bytes - 1) / page_size) + 1;
0700 }
0701 
0702 /**
0703  * The number of available pages is the total pages less the
0704  * active, used and bad pages.
0705  */
0706 static uint32_t
0707 rtems_fdisk_seg_pages_available (const rtems_fdisk_segment_ctl* sc)
0708 {
0709   return sc->pages - (sc->pages_active + sc->pages_used + sc->pages_bad);
0710 }
0711 /**
0712  * Find the next available page in a segment.
0713  */
0714 static uint32_t
0715 rtems_fdisk_seg_next_available_page (rtems_fdisk_segment_ctl* sc)
0716 {
0717   rtems_fdisk_page_desc* pd = &sc->page_descriptors[0];
0718   uint32_t               page;
0719 
0720   for (page = 0; page < sc->pages; page++, pd++)
0721     if (rtems_fdisk_page_desc_erased (pd))
0722       break;
0723 
0724   return page;
0725 }
0726 
0727 /**
0728  * Find the segment on the queue that has the most free pages.
0729  */
0730 static rtems_fdisk_segment_ctl*
0731 rtems_fdisk_seg_most_available (const rtems_fdisk_segment_ctl_queue* queue)
0732 {
0733   rtems_fdisk_segment_ctl* sc      = queue->head;
0734   rtems_fdisk_segment_ctl* biggest = queue->head;
0735 
0736   while (sc)
0737   {
0738     if (rtems_fdisk_seg_pages_available (sc) >
0739         rtems_fdisk_seg_pages_available (biggest))
0740       biggest = sc;
0741     sc = sc->next;
0742   }
0743 
0744   return biggest;
0745 }
0746 
0747 /**
0748  * Is the segment all used ?
0749  */
0750 #if 0
0751 static bool
0752 rtems_fdisk_seg_pages_all_used (const rtems_fdisk_segment_ctl* sc)
0753 {
0754   return sc->pages == (sc->pages_used + sc->pages_bad) ? true : false;
0755 }
0756 #endif
0757 
0758 /**
0759  * Calculate the blocks in a device. This is the number of
0760  * pages less the pages hold page descriptors. This call be used
0761  * early in the initialisation process and does not rely on
0762  * the system being fully initialised.
0763  *
0764  * @param dd The device descriptor.
0765  * @param page_size The page size in bytes.
0766  */
0767 static uint32_t
0768 rtems_fdisk_blocks_in_device (const rtems_fdisk_device_desc* dd,
0769                               uint32_t                       page_size)
0770 {
0771   uint32_t count = 0;
0772   uint32_t s;
0773   for (s = 0; s < dd->segment_count; s++)
0774   {
0775     const rtems_fdisk_segment_desc* sd = &dd->segments[s];
0776     count +=
0777       (rtems_fdisk_pages_in_segment (sd, page_size) -
0778        rtems_fdisk_page_desc_pages (sd, page_size)) * sd->count;
0779   }
0780   return count;
0781 }
0782 
0783 /**
0784  * Read a block of data from a segment.
0785  */
0786 static int
0787 rtems_fdisk_seg_read (const rtems_flashdisk*         fd,
0788                       const rtems_fdisk_segment_ctl* sc,
0789                       uint32_t                       offset,
0790                       void*                          buffer,
0791                       uint32_t                       size)
0792 {
0793   uint32_t                           device;
0794   uint32_t                           segment;
0795   const rtems_fdisk_segment_desc*    sd;
0796   const rtems_fdisk_driver_handlers* ops;
0797   device = sc->device;
0798   segment = sc->segment;
0799   sd = rtems_fdisk_seg_descriptor (fd, device, segment);
0800   ops = fd->devices[device].descriptor->flash_ops;
0801 #if RTEMS_FDISK_TRACE
0802   rtems_fdisk_printf (fd, "  seg-read: %02d-%03d: o=%08x s=%d",
0803                       device, segment, offset, size);
0804 #endif
0805   return ops->read (sd, device, segment, offset, buffer, size);
0806 }
0807 
0808 /**
0809  * Write a block of data to a segment. It is assumed the
0810  * location in the segment is erased and able to take the
0811  * data.
0812  */
0813 static int
0814 rtems_fdisk_seg_write (const rtems_flashdisk*   fd,
0815                        rtems_fdisk_segment_ctl* sc,
0816                        uint32_t                 offset,
0817                        const void*              buffer,
0818                        uint32_t                 size)
0819 {
0820   int ret;
0821   uint32_t                           device;
0822   uint32_t                           segment;
0823   const rtems_fdisk_segment_desc*    sd;
0824   const rtems_fdisk_driver_handlers* ops;
0825   device = sc->device;
0826   segment = sc->segment;
0827   sd = rtems_fdisk_seg_descriptor (fd, device, segment);
0828   ops = fd->devices[device].descriptor->flash_ops;
0829 #if RTEMS_FDISK_TRACE
0830   rtems_fdisk_printf (fd, "  seg-write: %02d-%03d: o=%08x s=%d",
0831                       device, segment, offset, size);
0832 #endif
0833   ret = ops->write (sd, device, segment, offset, buffer, size);
0834   if (ret)
0835     sc->failed = true;
0836 
0837   return ret;
0838 }
0839 
0840 /**
0841  * Blank check the area of a segment.
0842  */
0843 static int
0844 rtems_fdisk_seg_blank_check (const rtems_flashdisk*   fd,
0845                              rtems_fdisk_segment_ctl* sc,
0846                              uint32_t                 offset,
0847                              uint32_t                 size)
0848 {
0849   uint32_t                           device;
0850   uint32_t                           segment;
0851   const rtems_fdisk_segment_desc*    sd;
0852   const rtems_fdisk_driver_handlers* ops;
0853   device = sc->device;
0854   segment = sc->segment;
0855   sd = rtems_fdisk_seg_descriptor (fd, device, segment);
0856   ops = fd->devices[device].descriptor->flash_ops;
0857 #if RTEMS_FDISK_TRACE
0858   rtems_fdisk_printf (fd, "  seg-blank: %02d-%03d: o=%08x s=%d",
0859                       device, segment, offset, size);
0860 #endif
0861   return ops->blank (sd, device, segment, offset, size);
0862 }
0863 /**
0864  * Verify the data with the data in a segment.
0865  */
0866 static int
0867 rtems_fdisk_seg_verify (const rtems_flashdisk* fd,
0868                         uint32_t               device,
0869                         uint32_t               segment,
0870                         uint32_t               offset,
0871                         const void*            buffer,
0872                         uint32_t               size)
0873 {
0874   const rtems_fdisk_segment_desc*    sd;
0875   const rtems_fdisk_driver_handlers* ops;
0876   sd  = rtems_fdisk_seg_descriptor (fd, device, segment);
0877   ops = fd->devices[device].descriptor->flash_ops;
0878 #if RTEMS_FDISK_TRACE
0879   rtems_fdisk_printf (fd, "  seg-verify: %02d-%03d: o=%08x s=%d",
0880                       device, segment, offset, size);
0881 #endif
0882   return ops->verify (sd, device, segment, offset, buffer, size);
0883 }
0884 
0885 /**
0886  * Blank check a page of data in a segment.
0887  */
0888 static int
0889 rtems_fdisk_seg_blank_check_page (const rtems_flashdisk*   fd,
0890                                   rtems_fdisk_segment_ctl* sc,
0891                                   uint32_t                 page)
0892 {
0893   return rtems_fdisk_seg_blank_check (fd, sc,
0894                                       page * fd->block_size, fd->block_size);
0895 }
0896 
0897 /**
0898  * Read a page of data from a segment.
0899  */
0900 static int
0901 rtems_fdisk_seg_read_page (const rtems_flashdisk*   fd,
0902                            rtems_fdisk_segment_ctl* sc,
0903                            uint32_t                 page,
0904                            void*                    buffer)
0905 {
0906   return rtems_fdisk_seg_read (fd, sc,
0907                                page * fd->block_size, buffer, fd->block_size);
0908 }
0909 
0910 /**
0911  * Write a page of data to a segment.
0912  */
0913 static int
0914 rtems_fdisk_seg_write_page (rtems_flashdisk*         fd,
0915                             rtems_fdisk_segment_ctl* sc,
0916                             uint32_t                 page,
0917                             const void*              buffer)
0918 {
0919   if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
0920   {
0921     int ret = rtems_fdisk_seg_blank_check_page (fd, sc, page);
0922     if (ret)
0923       return ret;
0924   }
0925   --fd->erased_blocks;
0926   return rtems_fdisk_seg_write (fd, sc,
0927                                 page * fd->block_size, buffer, fd->block_size);
0928 }
0929 
0930 /**
0931  * Verify a page of data with the data in the segment.
0932  */
0933 static int
0934 rtems_fdisk_seg_verify_page (const rtems_flashdisk* fd,
0935                              uint32_t               device,
0936                              uint32_t               segment,
0937                              uint32_t               page,
0938                              const void*            buffer)
0939 {
0940   return rtems_fdisk_seg_verify (fd, device, segment,
0941                                  page * fd->block_size, buffer, fd->block_size);
0942 }
0943 
0944 /**
0945  * Copy a page of data from one segment to another segment.
0946  */
0947 static int
0948 rtems_fdisk_seg_copy_page (rtems_flashdisk*         fd,
0949                            rtems_fdisk_segment_ctl* src_sc,
0950                            uint32_t                 src_page,
0951                            rtems_fdisk_segment_ctl* dst_sc,
0952                            uint32_t                 dst_page)
0953 {
0954   int ret;
0955 #if RTEMS_FDISK_TRACE
0956   rtems_fdisk_printf (fd, "  seg-copy-page: %02d-%03d~%03d=>%02d-%03d~%03d",
0957                       src_sc->device, src_sc->segment, src_page,
0958                       dst_sc->device, dst_sc->segment, dst_page);
0959 #endif
0960   ret = rtems_fdisk_seg_read_page (fd, src_sc, src_page,
0961                                    fd->copy_buffer);
0962   if (ret)
0963     return ret;
0964   return rtems_fdisk_seg_write_page (fd, dst_sc, dst_page,
0965                                      fd->copy_buffer);
0966 }
0967 
0968 /**
0969  * Write the page descriptor to a segment. This code assumes the page
0970  * descriptors are located at offset 0 in the segment.
0971  */
0972 static int
0973 rtems_fdisk_seg_write_page_desc (const rtems_flashdisk*       fd,
0974                                  rtems_fdisk_segment_ctl*     sc,
0975                                  uint32_t                     page,
0976                                  const rtems_fdisk_page_desc* page_desc)
0977 {
0978   uint32_t offset = page * sizeof (rtems_fdisk_page_desc);
0979   if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
0980   {
0981     int ret = rtems_fdisk_seg_blank_check (fd, sc,
0982                                            offset,
0983                                            sizeof (rtems_fdisk_page_desc));
0984     if (ret)
0985       return ret;
0986   }
0987   return rtems_fdisk_seg_write (fd, sc, offset,
0988                                 page_desc, sizeof (rtems_fdisk_page_desc));
0989 }
0990 
0991 /**
0992  * Write the page descriptor flags to a segment. This code assumes the page
0993  * descriptors are located at offset 0 in the segment.
0994  */
0995 static int
0996 rtems_fdisk_seg_write_page_desc_flags (const rtems_flashdisk*       fd,
0997                                        rtems_fdisk_segment_ctl*     sc,
0998                                        uint32_t                     page,
0999                                        const rtems_fdisk_page_desc* page_desc)
1000 {
1001   uint32_t offset = ((page * sizeof (rtems_fdisk_page_desc)) +
1002                      ((uint8_t*) &page_desc->flags) - ((uint8_t*) page_desc));
1003   if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
1004   {
1005     uint16_t flash_flags;
1006     int      ret;
1007     ret = rtems_fdisk_seg_read (fd, sc, offset,
1008                                 &flash_flags, sizeof (flash_flags));
1009     if (ret)
1010       return ret;
1011     if ((flash_flags & page_desc->flags) != page_desc->flags)
1012     {
1013       rtems_fdisk_error ("  seg-write-page-flags: %02d-%03d-%03d: "
1014                          "flags not erased: 0x%04x-> 0x%04x",
1015                          sc->device, sc->segment, page,
1016                          flash_flags, page_desc->flags);
1017       return ret;
1018     }
1019   }
1020   return rtems_fdisk_seg_write (fd, sc, offset,
1021                                 &page_desc->flags, sizeof (page_desc->flags));
1022 }
1023 
1024 /**
1025  * Erase a device.
1026  */
1027 static int
1028 rtems_fdisk_device_erase (const rtems_flashdisk* fd, uint32_t device)
1029 {
1030   const rtems_fdisk_driver_handlers* ops;
1031   ops = fd->devices[device].descriptor->flash_ops;
1032 #if RTEMS_FDISK_TRACE
1033   rtems_fdisk_printf (fd, " device-erase: %02d", device);
1034 #endif
1035   return ops->erase_device (fd->devices[device].descriptor, device);
1036 }
1037 
1038 /**
1039  * Erase all flash.
1040  */
1041 static int
1042 rtems_fdisk_erase_flash (const rtems_flashdisk* fd)
1043 {
1044   uint32_t device;
1045   for (device = 0; device < fd->device_count; device++)
1046   {
1047     int ret;
1048 
1049 #if RTEMS_FDISK_TRACE
1050     rtems_fdisk_info (fd, " erase-flash:%02d", device);
1051 #endif
1052 
1053     ret = rtems_fdisk_device_erase (fd, device);
1054 
1055     if (ret != 0)
1056       return ret;
1057   }
1058   return 0;
1059 }
1060 
1061 /**
1062  * Calculate the checksum of a page in a segment.
1063  */
1064 static uint16_t
1065 rtems_fdisk_page_checksum (const uint8_t* buffer, uint32_t page_size)
1066 {
1067   uint16_t cs = 0xffff;
1068   uint32_t i;
1069 
1070   for (i = 0; i < page_size; i++, buffer++)
1071     cs = rtems_fdisk_calc_crc16 (*buffer, cs);
1072 
1073   return cs;
1074 }
1075 
1076 /**
1077  * Erase the segment.
1078  */
1079 static int
1080 rtems_fdisk_erase_segment (rtems_flashdisk* fd, rtems_fdisk_segment_ctl* sc)
1081 {
1082   int                                ret;
1083   uint32_t                           device;
1084   uint32_t                           segment;
1085   const rtems_fdisk_segment_desc*    sd;
1086   const rtems_fdisk_driver_handlers* ops;
1087   device = sc->device;
1088   segment = sc->segment;
1089   sd = rtems_fdisk_seg_descriptor (fd, device, segment);
1090   ops = fd->devices[device].descriptor->flash_ops;
1091   ret = ops->erase (sd, device, segment);
1092   if (ret)
1093   {
1094     rtems_fdisk_error (" erase-segment:%02d-%03d: "      \
1095                        "segment erase failed: %s (%d)",
1096                        sc->device, sc->segment, strerror (ret), ret);
1097     sc->failed = true;
1098     if (!rtems_fdisk_segment_queue_present (&fd->failed, sc))
1099       rtems_fdisk_segment_queue_push_tail (&fd->failed, sc);
1100     return ret;
1101   }
1102 
1103   fd->erased_blocks += sc->pages;
1104   sc->erased++;
1105 
1106   memset (sc->page_descriptors, 0xff, sc->pages_desc * fd->block_size);
1107 
1108   sc->pages_active = 0;
1109   sc->pages_used   = 0;
1110   sc->pages_bad    = 0;
1111 
1112   sc->failed = false;
1113 
1114   /*
1115    * Push to the tail of the available queue. It is a very
1116    * simple type of wear reduction. Every other available
1117    * segment will now get a go.
1118    */
1119   rtems_fdisk_segment_queue_push_tail (&fd->available, sc);
1120 
1121   return 0;
1122 }
1123 
1124 /**
1125  * Erase used segment.
1126  */
1127 static int
1128 rtems_fdisk_erase_used (rtems_flashdisk* fd)
1129 {
1130   rtems_fdisk_segment_ctl* sc;
1131   int                      latched_ret = 0;
1132 
1133   while ((sc = rtems_fdisk_segment_queue_pop_head (&fd->erase)))
1134   {
1135     /*
1136      * The segment will either end up on the available queue or
1137      * the failed queue.
1138      */
1139     int ret = rtems_fdisk_erase_segment (fd, sc);
1140     if (ret && !latched_ret)
1141       latched_ret = ret;
1142   }
1143 
1144   return latched_ret;
1145 }
1146 
1147 /**
1148  * Queue a segment. This is done after some of the stats for the segment
1149  * have been changed and this may effect the order the segment pages have in
1150  * the queue of available pages.
1151  *
1152  * @param fd The flash disk control table.
1153  * @param sc The segment control table to be reallocated
1154  */
1155 static void
1156 rtems_fdisk_queue_segment (rtems_flashdisk* fd, rtems_fdisk_segment_ctl* sc)
1157 {
1158 #if RTEMS_FDISK_TRACE
1159   rtems_fdisk_info (fd, " queue-seg:%02d-%03d: p=%d a=%d u=%d b=%d f=%s n=%s",
1160                     sc->device, sc->segment,
1161                     sc->pages, sc->pages_active, sc->pages_used, sc->pages_bad,
1162                     sc->failed ? "FAILED" : "no", sc->next ? "set" : "null");
1163 #endif
1164 
1165   /*
1166    * If the segment has failed then check the failed queue and append
1167    * if not failed.
1168    */
1169   if (sc->failed)
1170   {
1171     if (!rtems_fdisk_segment_queue_present (&fd->failed, sc))
1172       rtems_fdisk_segment_queue_push_tail (&fd->failed, sc);
1173     return;
1174   }
1175 
1176   /*
1177    * Remove the queue from the available or used queue.
1178    */
1179   rtems_fdisk_segment_queue_remove (&fd->available, sc);
1180   rtems_fdisk_segment_queue_remove (&fd->used, sc);
1181 
1182   /*
1183    * Are all the pages in the segment used ?
1184    * If they are and the driver has been configured to background
1185    * erase place the segment on the used queue. If not configured
1186    * to background erase perform the erase now.
1187    *
1188    */
1189   if (rtems_fdisk_seg_pages_available (sc) == 0)
1190   {
1191     if (sc->pages_active)
1192     {
1193       /*
1194        * Keep the used queue sorted by the most number of used
1195        * pages. When we compact we want to move the pages into
1196        * a new segment and cover more than one segment.
1197        */
1198       rtems_fdisk_segment_ctl* seg = fd->used.head;
1199 
1200       while (seg)
1201       {
1202         if (sc->pages_used > seg->pages_used)
1203           break;
1204         seg = seg->next;
1205       }
1206 
1207       if (seg)
1208         rtems_fdisk_segment_queue_insert_before (&fd->used, seg, sc);
1209       else
1210         rtems_fdisk_segment_queue_push_tail (&fd->used, sc);
1211     }
1212     else
1213     {
1214       if ((fd->flags & RTEMS_FDISK_BACKGROUND_ERASE))
1215         rtems_fdisk_segment_queue_push_tail (&fd->erase, sc);
1216       else
1217         rtems_fdisk_erase_segment (fd, sc);
1218     }
1219   }
1220   else
1221   {
1222     /*
1223      * The segment has pages available so place back onto the
1224      * available list. The list is sorted from the least number
1225      * of available pages to the most. This approach means
1226      * the pages of a partially filled segment will be filled
1227      * before moving onto another emptier segment. This keeps
1228      * empty segments longer aiding compaction.
1229      *
1230      * The down side is the wear effect as a single segment
1231      * could be used more than segment. This will not be
1232      * addressed until wear support is added.
1233      *
1234      * @note Wear support can be added by having counts for
1235      * for the number of times a segment is erased. This
1236      * available list is then sorted on the least number
1237      * of available pages then empty segments are sorted
1238      * on the least number of erases the segment has.
1239      *
1240      * The erase count can be stored in specially flaged
1241      * pages and contain a counter (32bits?) and 32 bits
1242      * for each segment. When a segment is erased a
1243      * bit is cleared for that segment. When 32 erasers
1244      * has occurred the page is re-written to the flash
1245      * with all the counters updated with the number of
1246      * bits cleared and all bits set back to 1.
1247      */
1248     rtems_fdisk_segment_ctl* seg = fd->available.head;
1249 
1250     while (seg)
1251     {
1252       if (rtems_fdisk_seg_pages_available (sc) <
1253           rtems_fdisk_seg_pages_available (seg))
1254         break;
1255       seg = seg->next;
1256     }
1257 
1258     if (seg)
1259       rtems_fdisk_segment_queue_insert_before (&fd->available, seg, sc);
1260     else
1261       rtems_fdisk_segment_queue_push_tail (&fd->available, sc);
1262   }
1263 }
1264 
1265 static int
1266 rtems_fdisk_recycle_segment (rtems_flashdisk*         fd,
1267                                     rtems_fdisk_segment_ctl* ssc,
1268                                     rtems_fdisk_segment_ctl* dsc,
1269                                     uint32_t *pages)
1270 {
1271   int      ret;
1272   uint32_t spage;
1273   uint32_t used = 0;
1274   uint32_t active = 0;
1275 
1276   for (spage = 0; spage < ssc->pages; spage++)
1277   {
1278     rtems_fdisk_page_desc* spd = &ssc->page_descriptors[spage];
1279 
1280     if (rtems_fdisk_page_desc_flags_set (spd, RTEMS_FDISK_PAGE_ACTIVE) &&
1281         !rtems_fdisk_page_desc_flags_set (spd, RTEMS_FDISK_PAGE_USED))
1282     {
1283       uint32_t               dst_pages;
1284       rtems_fdisk_page_desc* dpd;
1285       uint32_t               dpage;
1286 
1287       dpage = rtems_fdisk_seg_next_available_page (dsc);
1288       dpd   = &dsc->page_descriptors[dpage];
1289 
1290       active++;
1291 
1292       if (dpage >= dsc->pages)
1293       {
1294         rtems_fdisk_error ("recycle: %02d-%03d: " \
1295                            "no page desc available: %d",
1296                            dsc->device, dsc->segment,
1297                            rtems_fdisk_seg_pages_available (dsc));
1298         dsc->failed = true;
1299         rtems_fdisk_queue_segment (fd, dsc);
1300         rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
1301         return EIO;
1302       }
1303 
1304 #if RTEMS_FDISK_TRACE
1305       rtems_fdisk_info (fd, "recycle: %02d-%03d-%03d=>%02d-%03d-%03d",
1306                         ssc->device, ssc->segment, spage,
1307                         dsc->device, dsc->segment, dpage);
1308 #endif
1309       ret = rtems_fdisk_seg_copy_page (fd, ssc,
1310                                        spage + ssc->pages_desc,
1311                                        dsc,
1312                                        dpage + dsc->pages_desc);
1313       if (ret)
1314       {
1315         rtems_fdisk_error ("recycle: %02d-%03d-%03d=>" \
1316                            "%02d-%03d-%03d: "             \
1317                            "copy page failed: %s (%d)",
1318                            ssc->device, ssc->segment, spage,
1319                            dsc->device, dsc->segment, dpage,
1320                            strerror (ret), ret);
1321         rtems_fdisk_queue_segment (fd, dsc);
1322         rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
1323         return ret;
1324       }
1325 
1326       *dpd = *spd;
1327 
1328       ret = rtems_fdisk_seg_write_page_desc (fd,
1329                                              dsc,
1330                                              dpage, dpd);
1331 
1332       if (ret)
1333       {
1334         rtems_fdisk_error ("recycle: %02d-%03d-%03d=>"   \
1335                            "%02d-%03d-%03d: copy pd failed: %s (%d)",
1336                            ssc->device, ssc->segment, spage,
1337                            dsc->device, dsc->segment, dpage,
1338                            strerror (ret), ret);
1339         rtems_fdisk_queue_segment (fd, dsc);
1340         rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
1341         return ret;
1342       }
1343 
1344       dsc->pages_active++;
1345 
1346       /*
1347        * No need to set the used bit on the source page as the
1348        * segment will be erased. Power down could be a problem.
1349        * We do the stats to make sure everything is as it should
1350        * be.
1351        */
1352 
1353       ssc->pages_active--;
1354       ssc->pages_used++;
1355 
1356       fd->blocks[spd->block].segment = dsc;
1357       fd->blocks[spd->block].page    = dpage;
1358 
1359       /*
1360        * Place the segment on to the correct queue.
1361        */
1362       rtems_fdisk_queue_segment (fd, dsc);
1363 
1364       /*
1365        * Get new destination segment if necessary.
1366        */
1367       dst_pages = rtems_fdisk_seg_pages_available (dsc);
1368       if (dst_pages == 0)
1369       {
1370         dsc = rtems_fdisk_seg_most_available (&fd->available);
1371         if (!dsc)
1372         {
1373           if (ssc->pages_active == 0)
1374           {
1375             ret = rtems_fdisk_erase_segment (fd, ssc);
1376           }
1377           else
1378           {
1379             rtems_fdisk_error ("recycle: no available dst segment");
1380             ret = EIO;
1381           }
1382 
1383           return ret;
1384         }
1385       }
1386 
1387       (*pages)--;
1388     }
1389     else if (rtems_fdisk_page_desc_erased (spd))
1390     {
1391       --fd->erased_blocks;
1392     }
1393     else
1394     {
1395       used++;
1396     }
1397   }
1398 
1399 #if RTEMS_FDISK_TRACE
1400   rtems_fdisk_printf (fd, "ssc end: %d-%d: p=%ld, a=%ld, u=%ld",
1401                       ssc->device, ssc->segment,
1402                       pages, active, used);
1403 #endif
1404   if (ssc->pages_active != 0)
1405   {
1406     rtems_fdisk_error ("compacting: ssc pages not 0: %d",
1407                        ssc->pages_active);
1408   }
1409 
1410   ret = rtems_fdisk_erase_segment (fd, ssc);
1411 
1412   return ret;
1413 }
1414 
1415 /**
1416  * Compact the used segments to free what is available. Find the segment
1417  * with the most available number of pages and see if we have
1418  * used segments that will fit. The used queue is sorted on the least
1419  * number of active pages.
1420  */
1421 static int
1422 rtems_fdisk_compact (rtems_flashdisk* fd)
1423 {
1424   int ret;
1425   rtems_fdisk_segment_ctl* dsc;
1426   rtems_fdisk_segment_ctl* ssc;
1427   uint32_t compacted_segs = 0;
1428   uint32_t pages;
1429 
1430   if (rtems_fdisk_is_erased_blocks_starvation (fd))
1431   {
1432 #if RTEMS_FDISK_TRACE
1433     rtems_fdisk_printf (fd, " resolve starvation");
1434 #endif
1435 
1436     ssc = rtems_fdisk_segment_queue_pop_head (&fd->used);
1437     if (!ssc)
1438       ssc = rtems_fdisk_segment_queue_pop_head (&fd->available);
1439 
1440     if (ssc)
1441     {
1442       dsc = rtems_fdisk_seg_most_available (&fd->available);
1443       if (dsc)
1444       {
1445         ret = rtems_fdisk_recycle_segment (fd, ssc, dsc, &pages);
1446         if (ret)
1447           return ret;
1448       }
1449       else
1450       {
1451         rtems_fdisk_error ("compacting: starvation");
1452         return EIO;
1453       }
1454     }
1455     else
1456     {
1457       rtems_fdisk_error ("compacting: nothing to recycle");
1458       return EIO;
1459     }
1460   }
1461 
1462   while (fd->used.head)
1463   {
1464     uint32_t                 dst_pages;
1465     uint32_t                 segments;
1466 
1467 #if RTEMS_FDISK_TRACE
1468     rtems_fdisk_printf (fd, " compacting");
1469 #endif
1470 
1471     dsc = rtems_fdisk_seg_most_available (&fd->available);
1472 
1473     if (dsc == 0)
1474     {
1475       rtems_fdisk_error ("compacting: no available segments to compact too");
1476       return EIO;
1477     }
1478 
1479     ssc = fd->used.head;
1480     dst_pages = rtems_fdisk_seg_pages_available (dsc);
1481     segments = 0;
1482     pages = 0;
1483 
1484 #if RTEMS_FDISK_TRACE
1485     rtems_fdisk_printf (fd, " dsc:%02d-%03d: most available",
1486                         dsc->device, dsc->segment);
1487 #endif
1488 
1489     /*
1490      * Count the number of segments that have active pages that fit into
1491      * the destination segment. Also limit the number of segments that
1492      * we handle during one compaction. A lower number means less aggressive
1493      * compaction or less delay when compacting but it may mean the disk
1494      * will fill.
1495      */
1496 
1497     while (ssc &&
1498            ((pages + ssc->pages_active) < dst_pages) &&
1499            ((compacted_segs + segments) < fd->compact_segs))
1500     {
1501       pages += ssc->pages_active;
1502       segments++;
1503       ssc = ssc->next;
1504     }
1505 
1506     /*
1507      * We need a source segment and have pages to copy and
1508      * compacting one segment to another is silly. Compaction needs
1509      * to free at least one more segment.
1510      */
1511 
1512     if (!ssc || (pages == 0) || ((compacted_segs + segments) == 1))
1513     {
1514 #if RTEMS_FDISK_TRACE
1515       rtems_fdisk_printf (fd, " nothing to compact");
1516 #endif
1517       break;
1518     }
1519 
1520 #if RTEMS_FDISK_TRACE
1521     rtems_fdisk_printf (fd, " ssc scan: %d-%d: p=%ld, seg=%ld",
1522                         ssc->device, ssc->segment,
1523                         pages, segments);
1524 #endif
1525 
1526     rtems_fdisk_segment_queue_remove (&fd->available, dsc);
1527 
1528     /*
1529      * We now copy the pages to the new segment.
1530      */
1531 
1532     while (pages)
1533     {
1534       ssc = rtems_fdisk_segment_queue_pop_head (&fd->used);
1535 
1536       if (ssc)
1537       {
1538         ret = rtems_fdisk_recycle_segment (fd, ssc, dsc, &pages);
1539         if (ret)
1540           return ret;
1541       }
1542     }
1543 
1544     compacted_segs += segments;
1545   }
1546 
1547   return 0;
1548 }
1549 
1550 /**
1551  * Recover the block mappings from the devices.
1552  */
1553 static int
1554 rtems_fdisk_recover_block_mappings (rtems_flashdisk* fd)
1555 {
1556   uint32_t device;
1557 
1558   /*
1559    * Clear the queues.
1560    */
1561   rtems_fdisk_segment_queue_init (&fd->available);
1562   rtems_fdisk_segment_queue_init (&fd->used);
1563   rtems_fdisk_segment_queue_init (&fd->erase);
1564   rtems_fdisk_segment_queue_init (&fd->failed);
1565 
1566   /*
1567    * Clear the lock mappings.
1568    */
1569   memset (fd->blocks, 0, fd->block_count * sizeof (rtems_fdisk_block_ctl));
1570 
1571   /*
1572    * Scan each segment or each device recovering the valid pages.
1573    */
1574   fd->erased_blocks = 0;
1575   fd->starvation_threshold = 0;
1576   for (device = 0; device < fd->device_count; device++)
1577   {
1578     uint32_t segment;
1579     for (segment = 0; segment < fd->devices[device].segment_count; segment++)
1580     {
1581       rtems_fdisk_segment_ctl*        sc = &fd->devices[device].segments[segment];
1582       const rtems_fdisk_segment_desc* sd = sc->descriptor;
1583       rtems_fdisk_page_desc*          pd;
1584       uint32_t                        page;
1585       int                             ret;
1586 
1587 #if RTEMS_FDISK_TRACE
1588       rtems_fdisk_info (fd, "recover-block-mappings:%02d-%03d", device, segment);
1589 #endif
1590 
1591       sc->pages_desc = rtems_fdisk_page_desc_pages (sd, fd->block_size);
1592       sc->pages =
1593         rtems_fdisk_pages_in_segment (sd, fd->block_size) - sc->pages_desc;
1594       if (sc->pages > fd->starvation_threshold)
1595         fd->starvation_threshold = sc->pages;
1596 
1597       sc->pages_active = 0;
1598       sc->pages_used   = 0;
1599       sc->pages_bad    = 0;
1600 
1601       sc->failed = false;
1602 
1603       if (!sc->page_descriptors)
1604         sc->page_descriptors = malloc (sc->pages_desc * fd->block_size);
1605 
1606       if (!sc->page_descriptors)
1607         rtems_fdisk_abort ("no memory for page descriptors");
1608 
1609       pd = sc->page_descriptors;
1610 
1611       /*
1612        * The page descriptors are always at the start of the segment. Read
1613        * the descriptors off the device into the segment control page
1614        * descriptors.
1615        *
1616        * @todo It may be better to ask the driver to get these value
1617        *       so NAND flash could be better supported.
1618        */
1619       ret = rtems_fdisk_seg_read (fd, sc, 0, (void*) pd,
1620                                   sc->pages_desc * fd->block_size);
1621 
1622       if (ret)
1623       {
1624         rtems_fdisk_error ("recover-block-mappings:%02d-%03d: " \
1625                            "read page desc failed: %s (%d)",
1626                            device, segment, strerror (ret), ret);
1627         return ret;
1628       }
1629 
1630       /*
1631        * Check each page in the segement for valid pages.
1632        * Update the stats for the segment so we know how many pages
1633        * are active and how many are used.
1634        *
1635        * If the page is active see if the block is with-in range and
1636        * if the block is a duplicate.
1637        */
1638       for (page = 0; page < sc->pages; page++, pd++)
1639       {
1640         if (rtems_fdisk_page_desc_erased (pd))
1641         {
1642           /*
1643            * Is the page erased ?
1644            */
1645           ret = rtems_fdisk_seg_blank_check_page (fd, sc,
1646                                                   page + sc->pages_desc);
1647 
1648           if (ret == 0)
1649           {
1650             ++fd->erased_blocks;
1651           }
1652           else
1653           {
1654 #if RTEMS_FDISK_TRACE
1655             rtems_fdisk_warning (fd, "page not blank: %d-%d-%d",
1656                                  device, segment, page, pd->block);
1657 #endif
1658             rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_USED);
1659 
1660             ret = rtems_fdisk_seg_write_page_desc (fd, sc,
1661                                                    page, pd);
1662 
1663             if (ret)
1664             {
1665               rtems_fdisk_error ("forcing page to used failed: %d-%d-%d",
1666                                  device, segment, page);
1667             }
1668 
1669             sc->pages_used++;
1670           }
1671         }
1672         else
1673         {
1674           if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_USED))
1675           {
1676             sc->pages_used++;
1677           }
1678           else if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_ACTIVE))
1679           {
1680             if (pd->block >= fd->block_count)
1681             {
1682 #if RTEMS_FDISK_TRACE
1683               rtems_fdisk_warning (fd,
1684                                    "invalid block number: %d-%d-%d: block: %d",
1685                                    device, segment, page, pd->block);
1686 #endif
1687               sc->pages_bad++;
1688             }
1689             else if (fd->blocks[pd->block].segment)
1690             {
1691               /**
1692                * @todo
1693                * This may need more work later. Maybe a counter is stored with
1694                * each block so we can tell which is the later block when
1695                * duplicates appear. A power down with a failed wirte could cause
1696                * a duplicate.
1697                */
1698               const rtems_fdisk_segment_ctl* bsc = fd->blocks[pd->block].segment;
1699               rtems_fdisk_error ("duplicate block: %d-%d-%d: " \
1700                                  "duplicate: %d-%d-%d",
1701                                  bsc->device, bsc->segment,
1702                                  fd->blocks[pd->block].page,
1703                                  device, segment, page);
1704               sc->pages_bad++;
1705             }
1706             else
1707             {
1708               /**
1709                * @todo
1710                * Add start up crc checks here.
1711                */
1712               fd->blocks[pd->block].segment = sc;
1713               fd->blocks[pd->block].page    = page;
1714 
1715               /*
1716                * The page is active.
1717                */
1718               sc->pages_active++;
1719             }
1720           }
1721           else
1722             sc->pages_bad++;
1723         }
1724       }
1725 
1726       /*
1727        * Place the segment on to the correct queue.
1728        */
1729       rtems_fdisk_queue_segment (fd, sc);
1730     }
1731   }
1732 
1733   return 0;
1734 }
1735 
1736 /**
1737  * Read a block. The block is checked to see if the page referenced
1738  * is valid and the page has a valid crc.
1739  *
1740  * @param fd The rtems_flashdisk control table.
1741  * @param block The block number to read.
1742  * @param buffer The buffer to write the data into.
1743  * @return 0 No error.
1744  * @return EIO Invalid block size, block number, segment pointer, crc,
1745  *             page flags.
1746  */
1747 static bool
1748 rtems_fdisk_read_block (rtems_flashdisk* fd,
1749                         uint32_t         block,
1750                         uint8_t*         buffer)
1751 {
1752   rtems_fdisk_block_ctl*   bc;
1753   rtems_fdisk_segment_ctl* sc;
1754   rtems_fdisk_page_desc*   pd;
1755 
1756 #if RTEMS_FDISK_TRACE
1757   rtems_fdisk_info (fd, "read-block:%d", block);
1758 #endif
1759 
1760   /*
1761    * Broken out to allow info messages when testing.
1762    */
1763 
1764   if (block >= (fd->block_count - fd->unavail_blocks))
1765   {
1766     rtems_fdisk_error ("read-block: block out of range: %d", block);
1767     return EIO;
1768   }
1769 
1770   bc = &fd->blocks[block];
1771 
1772   if (!bc->segment)
1773   {
1774 #if RTEMS_FDISK_TRACE
1775     rtems_fdisk_info (fd, "read-block: no segment mapping: %d", block);
1776 #endif
1777     memset (buffer, 0xff, fd->block_size);
1778     return 0;
1779   }
1780 
1781   sc = fd->blocks[block].segment;
1782   pd = &sc->page_descriptors[bc->page];
1783 
1784 #if RTEMS_FDISK_TRACE
1785   rtems_fdisk_info (fd,
1786                     " read:%d=>%02d-%03d-%03d: p=%d a=%d u=%d b=%d n=%s: " \
1787                     "f=%04x c=%04x b=%d",
1788                     block, sc->device, sc->segment, bc->page,
1789                     sc->pages, sc->pages_active, sc->pages_used, sc->pages_bad,
1790                     sc->next ? "set" : "null",
1791                     pd->flags, pd->crc, pd->block);
1792 #endif
1793 
1794   if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_ACTIVE))
1795   {
1796     if (rtems_fdisk_page_desc_flags_clear (pd, RTEMS_FDISK_PAGE_USED))
1797     {
1798       uint16_t cs;
1799 
1800       /*
1801        * We use the segment page offset not the page number used in the
1802        * driver. This skips the page descriptors.
1803        */
1804       int ret = rtems_fdisk_seg_read_page (fd, sc,
1805                                            bc->page + sc->pages_desc, buffer);
1806 
1807       if (ret)
1808       {
1809 #if RTEMS_FDISK_TRACE
1810         rtems_fdisk_info (fd,
1811                           "read-block:%02d-%03d-%03d: read page failed: %s (%d)",
1812                           sc->device, sc->segment, bc->page,
1813                           strerror (ret), ret);
1814 #endif
1815         return ret;
1816       }
1817 
1818       cs = rtems_fdisk_page_checksum (buffer, fd->block_size);
1819 
1820       if (cs == pd->crc)
1821         return 0;
1822 
1823       rtems_fdisk_error ("read-block: crc failure: %d: buffer:%04x page:%04x",
1824                          block, cs, pd->crc);
1825     }
1826     else
1827     {
1828       rtems_fdisk_error ("read-block: block points to used page: %d: %d-%d-%d",
1829                          block, sc->device, sc->segment, bc->page);
1830     }
1831   }
1832   else
1833   {
1834     rtems_fdisk_error ("read-block: block page not active: %d: %d-%d-%d",
1835                        block, sc->device, sc->segment, bc->page);
1836   }
1837 
1838   return EIO;
1839 }
1840 
1841 /**
1842  * Write a block. The block:
1843  *
1844  *  # May never have existed in flash before this write.
1845  *  # Exists and needs to be moved to a new page.
1846  *
1847  * If the block does not exist in flash we need to get the next
1848  * segment available to place the page into. The segments with
1849  * available pages are held on the avaliable list sorted on least
1850  * number of available pages as the primary key. Currently there
1851  * is no secondary key. Empty segments are at the end of the list.
1852  *
1853  * If the block already exists we need to set the USED bit in the
1854  * current page's flags. This is a single byte which changes a 1 to
1855  * a 0 and can be done with a single 16 bit write. The driver for
1856  * 8 bit devices should only attempt the write on the changed bit.
1857  *
1858  * @param fd The rtems_flashdisk control table.
1859  * @param block The block number to read.
1860  * @param block_size The size of the block. Must match what we have.
1861  * @param buffer The buffer to write the data into.
1862  * @return 0 No error.
1863  * @return EIO Invalid block size, block number, segment pointer, crc,
1864  *             page flags.
1865  */
1866 static int
1867 rtems_fdisk_write_block (rtems_flashdisk* fd,
1868                          uint32_t         block,
1869                          const uint8_t*   buffer)
1870 {
1871   rtems_fdisk_block_ctl*   bc;
1872   rtems_fdisk_segment_ctl* sc;
1873   rtems_fdisk_page_desc*   pd;
1874   uint32_t                 page;
1875   int                      ret;
1876 
1877 #if RTEMS_FDISK_TRACE
1878   rtems_fdisk_info (fd, "write-block:%d", block);
1879 #endif
1880 
1881   /*
1882    * Broken out to allow info messages when testing.
1883    */
1884 
1885   if (block >= (fd->block_count - fd->unavail_blocks))
1886   {
1887     rtems_fdisk_error ("write-block: block out of range: %d", block);
1888     return EIO;
1889   }
1890 
1891   bc = &fd->blocks[block];
1892 
1893   /*
1894    * Does the page exist in flash ?
1895    */
1896   if (bc->segment)
1897   {
1898     sc = bc->segment;
1899     pd = &sc->page_descriptors[bc->page];
1900 
1901 #if RTEMS_FDISK_TRACE
1902     rtems_fdisk_info (fd, " write:%02d-%03d-%03d: flag used",
1903                       sc->device, sc->segment, bc->page);
1904 #endif
1905 
1906     /*
1907      * The page exists in flash so see if the page has been changed.
1908      */
1909     if (rtems_fdisk_seg_verify_page (fd, sc->device, sc->segment,
1910                                      bc->page + sc->pages_desc, buffer) == 0)
1911     {
1912 #if RTEMS_FDISK_TRACE
1913       rtems_fdisk_info (fd, "write-block:%d=>%02d-%03d-%03d: page verified",
1914                         block, sc->device, sc->segment, bc->page);
1915 #endif
1916       return 0;
1917     }
1918 
1919     /*
1920      * The page exists in flash so we need to set the used flag
1921      * in the page descriptor. The descriptor is in memory with the
1922      * segment control block. We can assume this memory copy
1923      * matches the flash device.
1924      */
1925 
1926     rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_USED);
1927 
1928     ret = rtems_fdisk_seg_write_page_desc_flags (fd, sc, bc->page, pd);
1929 
1930     if (ret)
1931     {
1932 #if RTEMS_FDISK_TRACE
1933       rtems_fdisk_info (fd, " write:%02d-%03d-%03d: "      \
1934                         "write used page desc failed: %s (%d)",
1935                         sc->device, sc->segment, bc->page,
1936                         strerror (ret), ret);
1937 #endif
1938     }
1939     else
1940     {
1941       sc->pages_active--;
1942       sc->pages_used++;
1943     }
1944 
1945     /*
1946      * If possible reuse this segment. This will mean the segment
1947      * needs to be removed from the available list and placed
1948      * back if space is still available.
1949      */
1950     rtems_fdisk_queue_segment (fd, sc);
1951 
1952     /*
1953      * If no background compacting then compact in the forground.
1954      * If we compact we ignore the error as there is little we
1955      * can do from here. The write may will work.
1956      */
1957     if ((fd->flags & RTEMS_FDISK_BACKGROUND_COMPACT) == 0)
1958       rtems_fdisk_compact (fd);
1959   }
1960 
1961   /*
1962    * Is it time to compact the disk ?
1963    *
1964    * We override the background compaction configruation.
1965    */
1966   if (rtems_fdisk_segment_count_queue (&fd->available) <=
1967       fd->avail_compact_segs)
1968     rtems_fdisk_compact (fd);
1969 
1970   /*
1971    * Get the next avaliable segment.
1972    */
1973   sc = rtems_fdisk_segment_queue_pop_head (&fd->available);
1974 
1975   /*
1976    * Is the flash disk full ?
1977    */
1978   if (!sc)
1979   {
1980     /*
1981      * If compacting is configured for the background do it now
1982      * to see if we can get some space back.
1983      */
1984     if ((fd->flags & RTEMS_FDISK_BACKGROUND_COMPACT))
1985       rtems_fdisk_compact (fd);
1986 
1987     /*
1988      * Try again for some free space.
1989      */
1990     sc = rtems_fdisk_segment_queue_pop_head (&fd->available);
1991 
1992     if (!sc)
1993     {
1994       rtems_fdisk_error ("write-block: no available pages");
1995       return ENOSPC;
1996     }
1997   }
1998 
1999 #if RTEMS_FDISK_TRACE
2000   if (fd->info_level >= 3)
2001   {
2002     char queues[5];
2003     rtems_fdisk_queue_status (fd, sc, queues);
2004     rtems_fdisk_info (fd, " write:%d=>%02d-%03d: queue check: %s",
2005                       block, sc->device, sc->segment, queues);
2006   }
2007 #endif
2008 
2009   /*
2010    * Find the next avaliable page in the segment.
2011    */
2012 
2013   pd = sc->page_descriptors;
2014 
2015   for (page = 0; page < sc->pages; page++, pd++)
2016   {
2017     if (rtems_fdisk_page_desc_erased (pd))
2018     {
2019       pd->crc   = rtems_fdisk_page_checksum (buffer, fd->block_size);
2020       pd->block = block;
2021 
2022       bc->segment = sc;
2023       bc->page    = page;
2024 
2025       rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_ACTIVE);
2026 
2027 #if RTEMS_FDISK_TRACE
2028       rtems_fdisk_info (fd, " write:%d=>%02d-%03d-%03d: write: " \
2029                         "p=%d a=%d u=%d b=%d n=%s: f=%04x c=%04x b=%d",
2030                         block, sc->device, sc->segment, page,
2031                         sc->pages, sc->pages_active, sc->pages_used,
2032                         sc->pages_bad, sc->next ? "set" : "null",
2033                         pd->flags, pd->crc, pd->block);
2034 #endif
2035 
2036       /*
2037        * We use the segment page offset not the page number used in the
2038        * driver. This skips the page descriptors.
2039        */
2040       ret = rtems_fdisk_seg_write_page (fd, sc, page + sc->pages_desc, buffer);
2041       if (ret)
2042       {
2043 #if RTEMS_FDISK_TRACE
2044         rtems_fdisk_info (fd, "write-block:%02d-%03d-%03d: write page failed: " \
2045                           "%s (%d)", sc->device, sc->segment, page,
2046                           strerror (ret), ret);
2047 #endif
2048       }
2049       else
2050       {
2051         ret = rtems_fdisk_seg_write_page_desc (fd, sc, page, pd);
2052         if (ret)
2053         {
2054 #if RTEMS_FDISK_TRACE
2055           rtems_fdisk_info (fd, "write-block:%02d-%03d-%03d: "  \
2056                             "write page desc failed: %s (%d)",
2057                             sc->device, sc->segment, bc->page,
2058                             strerror (ret), ret);
2059 #endif
2060         }
2061         else
2062         {
2063           sc->pages_active++;
2064         }
2065       }
2066 
2067       rtems_fdisk_queue_segment (fd, sc);
2068 
2069       if (rtems_fdisk_is_erased_blocks_starvation (fd))
2070         rtems_fdisk_compact (fd);
2071 
2072       return ret;
2073     }
2074   }
2075 
2076   rtems_fdisk_error ("write-block: no erased page descs in segment: %d-%d",
2077                      sc->device, sc->segment);
2078 
2079   sc->failed = true;
2080   rtems_fdisk_queue_segment (fd, sc);
2081 
2082   return EIO;
2083 }
2084 
2085 /**
2086  * Disk READ request handler. This primitive copies data from the
2087  * flash disk to the supplied buffer and invoke the callout function
2088  * to inform upper layer that reading is completed.
2089  *
2090  * @param req Pointer to the READ block device request info.
2091  * @retval 0 Always.  The request done callback contains the status.
2092  */
2093 static int
2094 rtems_fdisk_read (rtems_flashdisk* fd, rtems_blkdev_request* req)
2095 {
2096   rtems_blkdev_sg_buffer* sg = req->bufs;
2097   uint32_t                buf;
2098   int                     ret = 0;
2099 
2100   for (buf = 0; (ret == 0) && (buf < req->bufnum); buf++, sg++)
2101   {
2102     uint8_t* data;
2103     uint32_t fb;
2104     uint32_t b;
2105     fb = sg->length / fd->block_size;
2106     data = sg->buffer;
2107     for (b = 0; b < fb; b++, data += fd->block_size)
2108     {
2109       ret = rtems_fdisk_read_block (fd, sg->block + b, data);
2110       if (ret)
2111         break;
2112     }
2113   }
2114 
2115   rtems_blkdev_request_done (req, ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL);
2116 
2117   return 0;
2118 }
2119 
2120 /**
2121  * Flash disk WRITE request handler. This primitive copies data from
2122  * supplied buffer to flash disk and invoke the callout function to inform
2123  * upper layer that writing is completed.
2124  *
2125  * @param req Pointers to the WRITE block device request info.
2126  * @retval 0 Always.  The request done callback contains the status.
2127  */
2128 static int
2129 rtems_fdisk_write (rtems_flashdisk* fd, rtems_blkdev_request* req)
2130 {
2131   rtems_blkdev_sg_buffer* sg = req->bufs;
2132   uint32_t                buf;
2133   int                     ret = 0;
2134 
2135   for (buf = 0; (ret == 0) && (buf < req->bufnum); buf++, sg++)
2136   {
2137     uint8_t* data;
2138     uint32_t fb;
2139     uint32_t b;
2140     fb = sg->length / fd->block_size;
2141     data = sg->buffer;
2142     for (b = 0; b < fb; b++, data += fd->block_size)
2143     {
2144       ret = rtems_fdisk_write_block (fd, sg->block + b, data);
2145       if (ret)
2146         break;
2147     }
2148   }
2149 
2150   rtems_blkdev_request_done (req, ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL);
2151 
2152   return 0;
2153 }
2154 
2155 /**
2156  * Flash disk erase disk.
2157  *
2158  * @param fd The flashdisk data.
2159  * @retval int The ioctl return value.
2160  */
2161 static int
2162 rtems_fdisk_erase_disk (rtems_flashdisk* fd)
2163 {
2164   uint32_t device;
2165   int      ret;
2166 
2167 #if RTEMS_FDISK_TRACE
2168   rtems_fdisk_info (fd, "erase-disk");
2169 #endif
2170 
2171   ret = rtems_fdisk_erase_flash (fd);
2172 
2173   if (ret == 0)
2174   {
2175     for (device = 0; device < fd->device_count; device++)
2176     {
2177       if (!fd->devices[device].segments)
2178         return ENOMEM;
2179 
2180       ret = rtems_fdisk_recover_block_mappings (fd);
2181       if (ret)
2182         break;
2183     }
2184   }
2185 
2186   return ret;
2187 }
2188 
2189 /**
2190  * Flash Disk Monitoring data is return in the monitoring data
2191  * structure.
2192  */
2193 static int
2194 rtems_fdisk_monitoring_data (rtems_flashdisk*          fd,
2195                              rtems_fdisk_monitor_data* data)
2196 {
2197   uint32_t i;
2198   uint32_t j;
2199 
2200   data->block_size     = fd->block_size;
2201   data->block_count    = fd->block_count;
2202   data->unavail_blocks = fd->unavail_blocks;
2203   data->device_count   = fd->device_count;
2204 
2205   data->blocks_used = 0;
2206   for (i = 0; i < fd->block_count; i++)
2207     if (fd->blocks[i].segment)
2208       data->blocks_used++;
2209 
2210   data->segs_available = rtems_fdisk_segment_count_queue (&fd->available);
2211   data->segs_used      = rtems_fdisk_segment_count_queue (&fd->used);
2212   data->segs_failed    = rtems_fdisk_segment_count_queue (&fd->failed);
2213 
2214   data->segment_count = 0;
2215   data->page_count    = 0;
2216   data->pages_desc    = 0;
2217   data->pages_active  = 0;
2218   data->pages_used    = 0;
2219   data->pages_bad     = 0;
2220   data->seg_erases    = 0;
2221 
2222   for (i = 0; i < fd->device_count; i++)
2223   {
2224     data->segment_count += fd->devices[i].segment_count;
2225 
2226     for (j = 0; j < fd->devices[i].segment_count; j++)
2227     {
2228       rtems_fdisk_segment_ctl* sc = &fd->devices[i].segments[j];
2229 
2230       data->page_count   += sc->pages;
2231       data->pages_desc   += sc->pages_desc;
2232       data->pages_active += sc->pages_active;
2233       data->pages_used   += sc->pages_used;
2234       data->pages_bad    += sc->pages_bad;
2235       data->seg_erases   += sc->erased;
2236     }
2237   }
2238 
2239   data->info_level = fd->info_level;
2240   return 0;
2241 }
2242 
2243 /**
2244  * Print to stdout the status of the driver. This is a debugging aid.
2245  */
2246 static int
2247 rtems_fdisk_print_status (rtems_flashdisk* fd)
2248 {
2249 #if RTEMS_FDISK_TRACE
2250   uint32_t current_info_level = fd->info_level;
2251   uint32_t total;
2252   uint32_t count;
2253   uint32_t device;
2254 
2255   fd->info_level = 3;
2256 
2257   rtems_fdisk_printf (fd,
2258                       "Flash Disk Driver Status : %d.%d", fd->major, fd->minor);
2259 
2260   rtems_fdisk_printf (fd, "Block count\t%d", fd->block_count);
2261   rtems_fdisk_printf (fd, "Unavail blocks\t%d", fd->unavail_blocks);
2262   rtems_fdisk_printf (fd, "Starvation threshold\t%d", fd->starvation_threshold);
2263   rtems_fdisk_printf (fd, "Starvations\t%d", fd->starvations);
2264   count = rtems_fdisk_segment_count_queue (&fd->available);
2265   total = count;
2266   rtems_fdisk_printf (fd, "Available queue\t%ld (%ld)",
2267                       count, rtems_fdisk_segment_queue_count (&fd->available));
2268   count = rtems_fdisk_segment_count_queue (&fd->used);
2269   total += count;
2270   rtems_fdisk_printf (fd, "Used queue\t%ld (%ld)",
2271                       count, rtems_fdisk_segment_queue_count (&fd->used));
2272   count = rtems_fdisk_segment_count_queue (&fd->erase);
2273   total += count;
2274   rtems_fdisk_printf (fd, "Erase queue\t%ld (%ld)",
2275                       count, rtems_fdisk_segment_queue_count (&fd->erase));
2276   count = rtems_fdisk_segment_count_queue (&fd->failed);
2277   total += count;
2278   rtems_fdisk_printf (fd, "Failed queue\t%ld (%ld)",
2279                       count, rtems_fdisk_segment_queue_count (&fd->failed));
2280 
2281   count = 0;
2282   for (device = 0; device < fd->device_count; device++)
2283     count += fd->devices[device].segment_count;
2284 
2285   rtems_fdisk_printf (fd, "Queue total\t%ld of %ld, %s", total, count,
2286                       total == count ? "ok" : "MISSING");
2287 
2288   rtems_fdisk_printf (fd, "Device count\t%d", fd->device_count);
2289 
2290   for (device = 0; device < fd->device_count; device++)
2291   {
2292     uint32_t block;
2293     uint32_t seg;
2294 
2295     rtems_fdisk_printf (fd, " Device\t\t%ld", device);
2296     rtems_fdisk_printf (fd, "  Segment count\t%ld",
2297                         fd->devices[device].segment_count);
2298 
2299     for (seg = 0; seg < fd->devices[device].segment_count; seg++)
2300     {
2301       rtems_fdisk_segment_ctl* sc = &fd->devices[device].segments[seg];
2302       uint32_t                 page;
2303       uint32_t                 erased = 0;
2304       uint32_t                 active = 0;
2305       uint32_t                 used = 0;
2306       bool                     is_active = false;
2307       char                     queues[5];
2308 
2309       rtems_fdisk_queue_status (fd, sc, queues);
2310 
2311       for (page = 0; page < sc->pages; page++)
2312       {
2313         if (rtems_fdisk_page_desc_erased (&sc->page_descriptors[page]))
2314           erased++;
2315         else if (rtems_fdisk_page_desc_flags_set (&sc->page_descriptors[page],
2316                                                   RTEMS_FDISK_PAGE_ACTIVE))
2317         {
2318           if (rtems_fdisk_page_desc_flags_set (&sc->page_descriptors[page],
2319                                                RTEMS_FDISK_PAGE_USED))
2320             used++;
2321           else
2322           {
2323             active++;
2324             is_active = true;
2325           }
2326         }
2327 
2328         for (block = 0; block < fd->block_count; block++)
2329         {
2330           if ((fd->blocks[block].segment == sc) &&
2331               (fd->blocks[block].page == page) && !is_active)
2332             rtems_fdisk_printf (fd,
2333                                 "    %ld\t not active when mapped by block %ld",
2334                                 page, block);
2335         }
2336       }
2337 
2338       count = 0;
2339       for (block = 0; block < fd->block_count; block++)
2340       {
2341         if (fd->blocks[block].segment == sc)
2342           count++;
2343       }
2344 
2345       rtems_fdisk_printf (fd, "  %3ld %s p:%3ld a:%3ld/%3ld" \
2346                           " u:%3ld/%3ld e:%3ld/%3ld br:%ld",
2347                           seg, queues,
2348                           sc->pages, sc->pages_active, active,
2349                           sc->pages_used, used, erased,
2350                           sc->pages - (sc->pages_active +
2351                                        sc->pages_used + sc->pages_bad),
2352                           count);
2353     }
2354   }
2355 
2356   {
2357     rtems_fdisk_segment_ctl* sc = fd->used.head;
2358     int count = 0;
2359     rtems_fdisk_printf (fd, "Used List:");
2360     while (sc)
2361     {
2362       rtems_fdisk_printf (fd, "  %3d %02d:%03d u:%3ld",
2363                           count, sc->device, sc->segment, sc->pages_used);
2364       sc = sc->next;
2365       count++;
2366     }
2367   }
2368   fd->info_level = current_info_level;
2369 
2370   return 0;
2371 #else
2372   return ENOSYS;
2373 #endif
2374 }
2375 
2376 /**
2377  * Flash disk IOCTL handler.
2378  *
2379  * @param dd Disk device.
2380  * @param req IOCTL request code.
2381  * @param argp IOCTL argument.
2382  * @retval The IOCTL return value
2383  */
2384 static int
2385 rtems_fdisk_ioctl (rtems_disk_device *dd, uint32_t req, void* argp)
2386 {
2387   rtems_flashdisk*      fd = rtems_disk_get_driver_data (dd);
2388   rtems_blkdev_request* r = argp;
2389 
2390   errno = 0;
2391 
2392   rtems_mutex_lock (&fd->lock);
2393 
2394   switch (req)
2395   {
2396     case RTEMS_BLKIO_REQUEST:
2397       if (fd->device_count == 0)
2398       {
2399         errno = ENODEV;
2400       }
2401       else
2402       {
2403         switch (r->req)
2404         {
2405           case RTEMS_BLKDEV_REQ_READ:
2406             errno = rtems_fdisk_read (fd, r);
2407             break;
2408 
2409           case RTEMS_BLKDEV_REQ_WRITE:
2410             errno = rtems_fdisk_write (fd, r);
2411             break;
2412 
2413           default:
2414             errno = EINVAL;
2415             break;
2416         }
2417       }
2418       break;
2419 
2420     case RTEMS_FDISK_IOCTL_ERASE_DISK:
2421       errno = rtems_fdisk_erase_disk (fd);
2422       break;
2423 
2424     case RTEMS_FDISK_IOCTL_COMPACT:
2425       errno = rtems_fdisk_compact (fd);
2426       break;
2427 
2428     case RTEMS_FDISK_IOCTL_ERASE_USED:
2429       errno = rtems_fdisk_erase_used (fd);
2430       break;
2431 
2432     case RTEMS_FDISK_IOCTL_MONITORING:
2433       errno = rtems_fdisk_monitoring_data (fd,
2434                                            (rtems_fdisk_monitor_data*) argp);
2435       break;
2436 
2437     case RTEMS_FDISK_IOCTL_INFO_LEVEL:
2438       fd->info_level = (uintptr_t) argp;
2439       break;
2440 
2441     case RTEMS_FDISK_IOCTL_PRINT_STATUS:
2442       errno = rtems_fdisk_print_status (fd);
2443       break;
2444 
2445     default:
2446       rtems_blkdev_ioctl (dd, req, argp);
2447       break;
2448   }
2449 
2450   rtems_mutex_unlock (&fd->lock);
2451 
2452   return errno == 0 ? 0 : -1;
2453 }
2454 
2455 /**
2456  * Flash disk device driver initialization.
2457  *
2458  * @todo Memory clean up on error is really badly handled.
2459  *
2460  * @param major Flash disk major device number.
2461  * @param minor Minor device number, not applicable.
2462  * @param arg Initialization argument, not applicable.
2463  */
2464 rtems_device_driver
2465 rtems_fdisk_initialize (rtems_device_major_number major,
2466                         rtems_device_minor_number minor,
2467                         void*                     arg RTEMS_UNUSED)
2468 {
2469   const rtems_flashdisk_config* c = rtems_flashdisk_configuration;
2470   rtems_flashdisk*              fd;
2471   rtems_status_code             sc;
2472 
2473   sc = rtems_fdisk_crc16_gen_factors (0x8408);
2474   if (sc != RTEMS_SUCCESSFUL)
2475       return sc;
2476 
2477   fd = calloc (rtems_flashdisk_configuration_size, sizeof (*fd));
2478   if (!fd)
2479     return RTEMS_NO_MEMORY;
2480 
2481   for (minor = 0; minor < rtems_flashdisk_configuration_size; minor++, c++, fd++)
2482   {
2483     char     name[] = RTEMS_FLASHDISK_DEVICE_BASE_NAME "a";
2484     uint32_t device;
2485     uint32_t blocks = 0;
2486     int      ret;
2487 
2488     name [sizeof(RTEMS_FLASHDISK_DEVICE_BASE_NAME)] += minor;
2489 
2490     fd->major              = major;
2491     fd->minor              = minor;
2492     fd->flags              = c->flags;
2493     fd->compact_segs       = c->compact_segs;
2494     fd->avail_compact_segs = c->avail_compact_segs;
2495     fd->block_size         = c->block_size;
2496     fd->unavail_blocks     = c->unavail_blocks;
2497     fd->info_level         = c->info_level;
2498 
2499     for (device = 0; device < c->device_count; device++)
2500       blocks += rtems_fdisk_blocks_in_device (&c->devices[device],
2501                                               c->block_size);
2502 
2503     /*
2504      * One copy buffer of a page size.
2505      */
2506     fd->copy_buffer = malloc (c->block_size);
2507     if (!fd->copy_buffer)
2508       return RTEMS_NO_MEMORY;
2509 
2510     fd->blocks = calloc (blocks, sizeof (rtems_fdisk_block_ctl));
2511     if (!fd->blocks)
2512       return RTEMS_NO_MEMORY;
2513 
2514     fd->block_count = blocks;
2515 
2516     fd->devices = calloc (c->device_count, sizeof (rtems_fdisk_device_ctl));
2517     if (!fd->devices)
2518       return RTEMS_NO_MEMORY;
2519 
2520     rtems_mutex_init (&fd->lock, "Flash Disk");
2521 
2522     sc = rtems_blkdev_create(name, c->block_size, blocks - fd->unavail_blocks,
2523                              rtems_fdisk_ioctl, fd);
2524     if (sc != RTEMS_SUCCESSFUL)
2525     {
2526       rtems_mutex_destroy (&fd->lock);
2527       free (fd->copy_buffer);
2528       free (fd->blocks);
2529       free (fd->devices);
2530       rtems_fdisk_error ("disk create phy failed");
2531       return sc;
2532     }
2533 
2534     for (device = 0; device < c->device_count; device++)
2535     {
2536       rtems_fdisk_segment_ctl* sc;
2537       uint32_t                 segment_count;
2538       uint32_t                 segment;
2539 
2540       segment_count = rtems_fdisk_count_segments (&c->devices[device]);
2541 
2542       fd->devices[device].segments = calloc (segment_count,
2543                                              sizeof (rtems_fdisk_segment_ctl));
2544       if (!fd->devices[device].segments)
2545       {
2546         unlink (name);
2547         rtems_mutex_destroy (&fd->lock);
2548         free (fd->copy_buffer);
2549         free (fd->blocks);
2550         free (fd->devices);
2551         return RTEMS_NO_MEMORY;
2552       }
2553 
2554       sc = fd->devices[device].segments;
2555 
2556       for (segment = 0; segment < c->devices[device].segment_count; segment++)
2557       {
2558         const rtems_fdisk_segment_desc* sd;
2559         uint32_t                        seg_segment;
2560 
2561         sd = &c->devices[device].segments[segment];
2562 
2563         for (seg_segment = 0; seg_segment < sd->count; seg_segment++, sc++)
2564         {
2565           sc->descriptor = sd;
2566           sc->device     = device;
2567           sc->segment    = seg_segment;
2568           sc->erased     = 0;
2569         }
2570       }
2571 
2572       fd->devices[device].segment_count = segment_count;
2573       fd->devices[device].descriptor    = &c->devices[device];
2574     }
2575 
2576     fd->device_count = c->device_count;
2577 
2578     ret = rtems_fdisk_recover_block_mappings (fd);
2579     if (ret)
2580     {
2581       unlink (name);
2582       rtems_mutex_destroy (&fd->lock);
2583       free (fd->copy_buffer);
2584       free (fd->blocks);
2585       free (fd->devices);
2586       rtems_fdisk_error ("recovery of disk failed: %s (%d)",
2587                          strerror (ret), ret);
2588       return ret;
2589     }
2590 
2591     ret = rtems_fdisk_compact (fd);
2592     if (ret)
2593     {
2594       unlink (name);
2595       rtems_mutex_destroy (&fd->lock);
2596       free (fd->copy_buffer);
2597       free (fd->blocks);
2598       free (fd->devices);
2599       rtems_fdisk_error ("compacting of disk failed: %s (%d)",
2600                          strerror (ret), ret);
2601       return ret;
2602     }
2603   }
2604 
2605   return RTEMS_SUCCESSFUL;
2606 }