source: rtems/cpukit/libblock/src/flashdisk.c @ f9027ccf

Last change on this file since f9027ccf was f9027ccf, checked in by Sebastian Huber <sebastian.huber@…>, on Jan 2, 2018 at 3:34:54 PM

libblock: Use self-contained mutex for flashdisk

Update #2843.

  • Property mode set to 100644
File size: 72.2 KB
Line 
1/**
2 * @file
3 *
4 * @brief Flash Disk Block Device Implementation
5 * @ingroup libblock
6 */
7
8/*
9 * Copyright (C) 2007 Chris Johns
10 *
11 * The license and distribution terms for this file may be
12 * found in the file LICENSE in this distribution or at
13 * http://www.rtems.org/license/LICENSE.
14 */
15
16/*
17 * The use of pages can vary. The rtems_fdisk_seg_*_page set
18 * routines use an absolute page number relative to the segment
19 * while all other page numbers are relative to the number of
20 * page descriptor pages a segment has. You need to add the
21 * number of page descriptor pages (pages_desc) to the page number
22 * when call the rtems_fdisk_seg_*_page functions.
23 *
24 * You must always show the page number as relative in any trace
25 * or error message as device-segment-page and if you have to
26 * the page number as absolute use device-segment~page. This
27 * can be seen in the page copy routine.
28 *
29 * The code is like this to avoid needing the pass the pages_desc
30 * value around. It is only used in selected places and so the
31 * extra parameter was avoided.
32 */
33
34#if HAVE_CONFIG_H
35#include "config.h"
36#endif
37
38#include <rtems.h>
39#include <rtems/libio.h>
40#include <errno.h>
41#include <stdlib.h>
42#include <stdio.h>
43#include <string.h>
44#include <inttypes.h>
45
46#include <rtems/blkdev.h>
47#include <rtems/diskdevs.h>
48#include <rtems/flashdisk.h>
49#include <rtems/thread.h>
50
51/**
52 * Control tracing. It can be compiled out of the code for small
53 * footprint targets. Leave in by default.
54 */
55#if !defined (RTEMS_FDISK_TRACE)
56#define RTEMS_FDISK_TRACE 1
57#endif
58
59/**
60 * The start of a segment has a segment control table. This hold the CRC and
61 * block number for the page.
62 *
63 * @todo A sequence number for the block could be added. This would
64 *       mean a larger descriptor size. Need to make the sequence
65 *       large like 20+ bits so a large file system would not have
66 *       more blocks available than the sequence number.
67 */
68typedef struct rtems_fdisk_page_desc
69{
70  uint16_t crc;       /**< The page's checksum. */
71  uint16_t flags;     /**< The flags for the page. */
72  uint32_t block;     /**< The block number. */
73} rtems_fdisk_page_desc;
74
75/**
76 * Flag the page as active.
77 */
78#define RTEMS_FDISK_PAGE_ACTIVE (1 << 0)
79
80/**
81 * Flag the page as used.
82 */
83#define RTEMS_FDISK_PAGE_USED (1 << 1)
84
85/**
86 * Flash Segment Control holds the pointer to the segment, number of
87 * pages, various page stats and the memory copy of the page descriptors.
88 */
89typedef struct rtems_fdisk_segment_ctl
90{
91  /**
92   * Segments with available pages are maintained as a linked list.
93   */
94  struct rtems_fdisk_segment_ctl* next;
95
96  /**
97   * The descriptor provided by the low-level driver.
98   */
99  const rtems_fdisk_segment_desc* descriptor;
100
101  /**
102   * The device this segment resides on.
103   */
104  uint32_t device;
105
106  /**
107   * The segment in the device. This must be within the
108   * segment descriptor.
109   */
110  uint32_t segment;
111
112  /**
113   * The in-memory ocpy of the page descriptors found at
114   * the start of the segment in the flash device.
115   */
116  rtems_fdisk_page_desc* page_descriptors;
117
118  /*
119   * Page stats.
120   *
121   * A bad page does not checksum or is not erased or has invalid flags.
122   */
123  uint32_t pages;         /**< Total number of pages in the segment. */
124  uint32_t pages_desc;    /**< Number of pages used for page descriptors. */
125  uint32_t pages_active;  /**< Number of pages flagged as active. */
126  uint32_t pages_used;    /**< Number of pages flagged as used. */
127  uint32_t pages_bad;     /**< Number of pages detected as bad. */
128
129  uint32_t failed;        /**< The segment has failed. */
130
131  uint32_t erased;        /**< Counter to debugging. Wear support would
132                               remove this. */
133} rtems_fdisk_segment_ctl;
134
135/**
136 * Segment control table queue.
137 */
138typedef struct rtems_fdisk_segment_ctl_queue
139{
140  rtems_fdisk_segment_ctl* head;
141  rtems_fdisk_segment_ctl* tail;
142  uint32_t                 count;
143} rtems_fdisk_segment_ctl_queue;
144
145/**
146 * Flash Device Control holds the segment controls
147 */
148typedef struct rtems_fdisk_device_ctl
149{
150  rtems_fdisk_segment_ctl*       segments;      /**< Segment controls. */
151  uint32_t                       segment_count; /**< Segment control count. */
152  const rtems_fdisk_device_desc* descriptor;    /**< Device descriptor. */
153} rtems_fdisk_device_ctl;
154
155/**
156 * The Block control holds the segment and page with the data.
157 */
158typedef struct rtems_fdisk_block_ctl
159{
160  rtems_fdisk_segment_ctl* segment; /**< The segment with the block. */
161  uint32_t                 page;    /**< The page in the segment. */
162} rtems_fdisk_block_ctl;
163
164/**
165 * The virtual block table holds the mapping for blocks as seen by the disk
166 * drivers to the device, segment and page numbers of the physical device.
167 */
168typedef struct rtems_flashdisk
169{
170  rtems_device_major_number major;         /**< The driver's major number. */
171  rtems_device_minor_number minor;         /**< The driver's minor number. */
172
173  uint32_t flags;                          /**< configuration flags. */
174
175  uint32_t compact_segs;                   /**< Max segs to compact at once. */
176  uint32_t avail_compact_segs;             /**< The number of segments when
177                                                compaction occurs when writing. */
178
179  uint32_t               block_size;       /**< The block size for this disk. */
180  rtems_fdisk_block_ctl* blocks;           /**< The block to segment-page
181                                                mappings. */
182  uint32_t block_count;                    /**< The number of avail. blocks. */
183  uint32_t unavail_blocks;                 /**< The number of unavail blocks. */
184  uint32_t starvation_threshold;           /**< Erased blocks starvation threshold. */
185  uint32_t erased_blocks;                  /**< The number of erased blocks. */
186
187  rtems_fdisk_device_ctl* devices;         /**< The flash devices for this
188                                                disk. */
189  uint32_t                device_count;    /**< The number of flash devices. */
190
191  rtems_fdisk_segment_ctl_queue available; /**< The queue of segments with
192                                                available pages. */
193  rtems_fdisk_segment_ctl_queue used;      /**< The list of segments with all
194                                                pages used. */
195  rtems_fdisk_segment_ctl_queue erase;     /**< The list of segments to be
196                                                erased. */
197  rtems_fdisk_segment_ctl_queue failed;    /**< The list of segments that failed
198                                                when being erased. */
199  rtems_mutex lock;                        /**< Mutex for threading protection.*/
200
201  uint8_t* copy_buffer;                    /**< Copy buf used during compacting */
202
203  uint32_t info_level;                     /**< The info trace level. */
204
205  uint32_t starvations;                    /**< Erased blocks starvations counter. */
206} rtems_flashdisk;
207
208/**
209 * The array of flash disks we support.
210 */
211static rtems_flashdisk* rtems_flashdisks;
212
213/**
214 * The number of flash disks we have.
215 */
216static uint32_t rtems_flashdisk_count;
217
218/**
219 * The CRC16 factor table. Created during initialisation.
220 */
221static uint16_t* rtems_fdisk_crc16_factor;
222
223/**
224 * Calculate the CRC16 checksum.
225 *
226 * @param _b The byte to checksum.
227 * @param _c The current checksum.
228 */
229#define rtems_fdisk_calc_crc16(_b, _c) \
230  rtems_fdisk_crc16_factor[((_b) ^ ((_c) & 0xff)) & 0xff] ^ (((_c) >> 8) & 0xff)
231
232/**
233 * Generate the CRC table.
234 *
235 * @param pattern The seed pattern for the table of factors.
236 * @relval RTEMS_SUCCESSFUL The table was generated.
237 * @retval RTEMS_NO_MEMORY The table could not be allocated from the heap.
238 */
239static rtems_status_code
240rtems_fdisk_crc16_gen_factors (uint16_t pattern)
241{
242  uint32_t b;
243
244  rtems_fdisk_crc16_factor = malloc (sizeof (uint16_t) * 256);
245  if (!rtems_fdisk_crc16_factor)
246    return RTEMS_NO_MEMORY;
247
248  for (b = 0; b < 256; b++)
249  {
250    uint32_t i;
251    uint16_t v = b;
252    for (i = 8; i--;)
253      v = v & 1 ? (v >> 1) ^ pattern : v >> 1;
254    rtems_fdisk_crc16_factor[b] = v & 0xffff;
255  }
256  return RTEMS_SUCCESSFUL;
257}
258
259#if RTEMS_FDISK_TRACE
260/**
261 * Print a message to the flash disk output and flush it.
262 *
263 * @param fd The flashdisk control structure.
264 * @param format The format string. See printf for details.
265 * @param ... The arguments for the format text.
266 * @return int The number of bytes written to the output.
267 */
268static int
269rtems_fdisk_printf (const rtems_flashdisk* fd, const char *format, ...)
270{
271  int ret = 0;
272  if (fd->info_level >= 3)
273  {
274    va_list args;
275    va_start (args, format);
276    fprintf (stdout, "fdisk:");
277    ret =  vfprintf (stdout, format, args);
278    fprintf (stdout, "\n");
279    fflush (stdout);
280    va_end (args);
281  }
282  return ret;
283}
284
285static bool
286rtems_fdisk_is_erased_blocks_starvation (rtems_flashdisk* fd)
287{
288  bool starvation = fd->erased_blocks < fd->starvation_threshold;
289
290  if (starvation)
291    fd->starvations++;
292
293  return starvation;
294}
295
296/**
297 * Print a info message to the flash disk output and flush it.
298 *
299 * @param fd The flashdisk control structure.
300 * @param format The format string. See printf for details.
301 * @param ... The arguments for the format text.
302 * @return int The number of bytes written to the output.
303 */
304static int
305rtems_fdisk_info (const rtems_flashdisk* fd, const char *format, ...)
306{
307  int ret = 0;
308  if (fd->info_level >= 2)
309  {
310    va_list args;
311    va_start (args, format);
312    fprintf (stdout, "fdisk:");
313    ret =  vfprintf (stdout, format, args);
314    fprintf (stdout, "\n");
315    fflush (stdout);
316    va_end (args);
317  }
318  return ret;
319}
320
321/**
322 * Print a warning to the flash disk output and flush it.
323 *
324 * @param fd The flashdisk control structure.
325 * @param format The format string. See printf for details.
326 * @param ... The arguments for the format text.
327 * @return int The number of bytes written to the output.
328 */
329static int
330rtems_fdisk_warning (const rtems_flashdisk* fd, const char *format, ...)
331{
332  int ret = 0;
333  if (fd->info_level >= 1)
334  {
335    va_list args;
336    va_start (args, format);
337    fprintf (stdout, "fdisk:warning:");
338    ret =  vfprintf (stdout, format, args);
339    fprintf (stdout, "\n");
340    fflush (stdout);
341    va_end (args);
342  }
343  return ret;
344}
345#endif
346
347/**
348 * Print an error to the flash disk output and flush it.
349 *
350 * @param format The format string. See printf for details.
351 * @param ... The arguments for the format text.
352 * @return int The number of bytes written to the output.
353 */
354static int
355rtems_fdisk_error (const char *format, ...)
356{
357  int ret;
358  va_list args;
359  va_start (args, format);
360  fprintf (stderr, "fdisk:error:");
361  ret =  vfprintf (stderr, format, args);
362  fprintf (stderr, "\n");
363  fflush (stderr);
364  va_end (args);
365  return ret;
366}
367
368/**
369 * Print an abort message, flush it then abort the program.
370 *
371 * @param format The format string. See printf for details.
372 * @param ... The arguments for the format text.
373 */
374static void
375rtems_fdisk_abort (const char *format, ...)
376{
377  va_list args;
378  va_start (args, format);
379  fprintf (stderr, "fdisk:abort:");
380  vfprintf (stderr, format, args);
381  fprintf (stderr, "\n");
382  fflush (stderr);
383  va_end (args);
384  exit (1);
385}
386
387/**
388 * Initialise the segment control queue.
389 */
390static void
391rtems_fdisk_segment_queue_init (rtems_fdisk_segment_ctl_queue* queue)
392{
393  queue->head = queue->tail = 0;
394  queue->count = 0;
395}
396
397/**
398 * Push to the head of the segment control queue.
399 */
400static void
401rtems_fdisk_segment_queue_push_head (rtems_fdisk_segment_ctl_queue* queue,
402                                     rtems_fdisk_segment_ctl*       sc)
403{
404  if (sc)
405  {
406    sc->next = queue->head;
407    queue->head = sc;
408
409    if (queue->tail == 0)
410      queue->tail = sc;
411    queue->count++;
412  }
413}
414
415/**
416 * Pop the head of the segment control queue.
417 */
418static rtems_fdisk_segment_ctl*
419rtems_fdisk_segment_queue_pop_head (rtems_fdisk_segment_ctl_queue* queue)
420{
421  if (queue->head)
422  {
423    rtems_fdisk_segment_ctl* sc = queue->head;
424
425    queue->head = sc->next;
426    if (!queue->head)
427      queue->tail = 0;
428
429    queue->count--;
430
431    sc->next = 0;
432
433    return sc;
434  }
435
436  return 0;
437}
438
439/**
440 * Push to the tail of the segment control queue.
441 */
442static void
443rtems_fdisk_segment_queue_push_tail (rtems_fdisk_segment_ctl_queue* queue,
444                                     rtems_fdisk_segment_ctl*       sc)
445{
446  if (sc)
447  {
448    sc->next = 0;
449
450    if (queue->head)
451    {
452      queue->tail->next = sc;
453      queue->tail       = sc;
454    }
455    else
456    {
457      queue->head = queue->tail = sc;
458    }
459
460    queue->count++;
461  }
462}
463
464/**
465 * Remove from the segment control queue.
466 */
467static void
468rtems_fdisk_segment_queue_remove (rtems_fdisk_segment_ctl_queue* queue,
469                                  rtems_fdisk_segment_ctl*       sc)
470{
471  rtems_fdisk_segment_ctl* prev = 0;
472  rtems_fdisk_segment_ctl* it = queue->head;
473
474  /*
475   * Do not change sc->next as sc could be on another queue.
476   */
477
478  while (it)
479  {
480    if (sc == it)
481    {
482      if (prev == 0)
483      {
484        queue->head = sc->next;
485        if (queue->head == 0)
486          queue->tail = 0;
487      }
488      else
489      {
490        prev->next = sc->next;
491        if (queue->tail == sc)
492          queue->tail = prev;
493      }
494      sc->next = 0;
495      queue->count--;
496      break;
497    }
498
499    prev = it;
500    it = it->next;
501  }
502}
503
504/**
505 * Insert into the segment control queue before the specific
506 * segment control item.
507 */
508static void
509rtems_fdisk_segment_queue_insert_before (rtems_fdisk_segment_ctl_queue* queue,
510                                         rtems_fdisk_segment_ctl*       item,
511                                         rtems_fdisk_segment_ctl*       sc)
512{
513  if (item)
514  {
515    rtems_fdisk_segment_ctl** prev = &queue->head;
516    rtems_fdisk_segment_ctl*  it = queue->head;
517
518    while (it)
519    {
520      if (item == it)
521      {
522        sc->next = item;
523        *prev = sc;
524        queue->count++;
525        return;
526      }
527
528      prev = &it->next;
529      it = it->next;
530    }
531  }
532
533  rtems_fdisk_segment_queue_push_tail (queue, sc);
534}
535
536/**
537 * Count the number of elements on the list.
538 */
539static uint32_t
540rtems_fdisk_segment_queue_count (rtems_fdisk_segment_ctl_queue* queue)
541{
542  return queue->count;
543}
544
545/**
546 * Count the number of elements on the list.
547 */
548static uint32_t
549rtems_fdisk_segment_count_queue (rtems_fdisk_segment_ctl_queue* queue)
550{
551  rtems_fdisk_segment_ctl* sc = queue->head;
552  uint32_t                 count = 0;
553
554  while (sc)
555  {
556    count++;
557    sc = sc->next;
558  }
559
560  return count;
561}
562
563/**
564 * See if a segment control is present on this queue.
565 */
566static bool
567rtems_fdisk_segment_queue_present (rtems_fdisk_segment_ctl_queue* queue,
568                                   rtems_fdisk_segment_ctl*       sc)
569{
570  rtems_fdisk_segment_ctl*  it = queue->head;
571
572  while (it)
573  {
574    if (it == sc)
575      return true;
576    it = it->next;
577  }
578
579  return false;
580}
581
582/**
583 * Format a string with the queue status.
584 */
585static void
586rtems_fdisk_queue_status (rtems_flashdisk*         fd,
587                          rtems_fdisk_segment_ctl* sc,
588                          char                     queues[5])
589{
590  queues[0] = rtems_fdisk_segment_queue_present (&fd->available, sc) ? 'A' : '-';
591  queues[1] = rtems_fdisk_segment_queue_present (&fd->used, sc)      ? 'U' : '-';
592  queues[2] = rtems_fdisk_segment_queue_present (&fd->erase, sc)     ? 'E' : '-';
593  queues[3] = rtems_fdisk_segment_queue_present (&fd->failed, sc)    ? 'F' : '-';
594  queues[4] = '\0';
595}
596
597/**
598 * Check if the page descriptor is erased.
599 */
600static bool
601rtems_fdisk_page_desc_erased (const rtems_fdisk_page_desc* pd)
602{
603  return ((pd->crc == 0xffff) &&
604          (pd->flags == 0xffff) &&
605          (pd->block == 0xffffffff)) ? true : false;
606}
607
608/**
609 * Check if the flags are set. The flags are inverted as we can
610 * only set a flag by changing it from 1 to 0.
611 */
612static bool
613rtems_fdisk_page_desc_flags_set (rtems_fdisk_page_desc* pd, uint16_t flags)
614{
615  return (pd->flags & flags) == 0 ? true : false;
616}
617
618/**
619 * Check if the flags are clear. The flags are inverted as we can
620 * only set a flag by changing it from 1 to 0.
621 */
622static bool
623rtems_fdisk_page_desc_flags_clear (rtems_fdisk_page_desc* pd, uint16_t flags)
624{
625  return (pd->flags & flags) == flags ? true : false;
626}
627
628/**
629 * Set the flags. Setting means clear the bit to 0.
630 */
631static void
632rtems_fdisk_page_desc_set_flags (rtems_fdisk_page_desc* pd, uint16_t flags)
633{
634  pd->flags &= ~flags;
635}
636
637/**
638 * Get the segment descriptor for a device and segment. There are
639 * no range checks.
640 */
641static const rtems_fdisk_segment_desc*
642rtems_fdisk_seg_descriptor (const rtems_flashdisk* fd,
643                            uint32_t               device,
644                            uint32_t               segment)
645{
646  return fd->devices[device].segments[segment].descriptor;
647}
648
649/**
650 * Count the segments for a device.
651 */
652static uint32_t
653rtems_fdisk_count_segments (const rtems_fdisk_device_desc* dd)
654{
655  uint32_t count = 0;
656  uint32_t segment;
657  for (segment = 0; segment < dd->segment_count; segment++)
658    count += dd->segments[segment].count;
659  return count;
660}
661
662/**
663 * Calculate the pages in a segment give the segment size and the
664 * page size.
665 *
666 * @param sd The segment descriptor.
667 * @param page_size The page size in bytes.
668 */
669static uint32_t
670rtems_fdisk_pages_in_segment (const rtems_fdisk_segment_desc* sd,
671                              uint32_t                        page_size)
672{
673  return sd->size / page_size;
674}
675
676/**
677 * Calculate the number of pages needed to hold the page descriptors.
678 * The calculation need to round up.
679 *
680 * The segment control contains the number of pages used as descriptors
681 * and should be used rather than this call where possible.
682 */
683static uint32_t
684rtems_fdisk_page_desc_pages (const rtems_fdisk_segment_desc* sd,
685                             uint32_t                        page_size)
686{
687  uint32_t pages = rtems_fdisk_pages_in_segment (sd, page_size);
688  uint32_t bytes = pages * sizeof (rtems_fdisk_page_desc);
689  return ((bytes - 1) / page_size) + 1;
690}
691
692/**
693 * The number of available pages is the total pages less the
694 * active, used and bad pages.
695 */
696static uint32_t
697rtems_fdisk_seg_pages_available (const rtems_fdisk_segment_ctl* sc)
698{
699  return sc->pages - (sc->pages_active + sc->pages_used + sc->pages_bad);
700}
701/**
702 * Find the next available page in a segment.
703 */
704static uint32_t
705rtems_fdisk_seg_next_available_page (rtems_fdisk_segment_ctl* sc)
706{
707  rtems_fdisk_page_desc* pd = &sc->page_descriptors[0];
708  uint32_t               page;
709
710  for (page = 0; page < sc->pages; page++, pd++)
711    if (rtems_fdisk_page_desc_erased (pd))
712      break;
713
714  return page;
715}
716
717/**
718 * Find the segment on the queue that has the most free pages.
719 */
720static rtems_fdisk_segment_ctl*
721rtems_fdisk_seg_most_available (const rtems_fdisk_segment_ctl_queue* queue)
722{
723  rtems_fdisk_segment_ctl* sc      = queue->head;
724  rtems_fdisk_segment_ctl* biggest = queue->head;
725
726  while (sc)
727  {
728    if (rtems_fdisk_seg_pages_available (sc) >
729        rtems_fdisk_seg_pages_available (biggest))
730      biggest = sc;
731    sc = sc->next;
732  }
733
734  return biggest;
735}
736
737/**
738 * Is the segment all used ?
739 */
740#if 0
741static bool
742rtems_fdisk_seg_pages_all_used (const rtems_fdisk_segment_ctl* sc)
743{
744  return sc->pages == (sc->pages_used + sc->pages_bad) ? true : false;
745}
746#endif
747
748/**
749 * Calculate the blocks in a device. This is the number of
750 * pages less the pages hold page descriptors. This call be used
751 * early in the initialisation process and does not rely on
752 * the system being fully initialised.
753 *
754 * @param dd The device descriptor.
755 * @param page_size The page size in bytes.
756 */
757static uint32_t
758rtems_fdisk_blocks_in_device (const rtems_fdisk_device_desc* dd,
759                              uint32_t                       page_size)
760{
761  uint32_t count = 0;
762  uint32_t s;
763  for (s = 0; s < dd->segment_count; s++)
764  {
765    const rtems_fdisk_segment_desc* sd = &dd->segments[s];
766    count +=
767      (rtems_fdisk_pages_in_segment (sd, page_size) -
768       rtems_fdisk_page_desc_pages (sd, page_size)) * sd->count;
769  }
770  return count;
771}
772
773/**
774 * Read a block of data from a segment.
775 */
776static int
777rtems_fdisk_seg_read (const rtems_flashdisk*         fd,
778                      const rtems_fdisk_segment_ctl* sc,
779                      uint32_t                       offset,
780                      void*                          buffer,
781                      uint32_t                       size)
782{
783  uint32_t                           device;
784  uint32_t                           segment;
785  const rtems_fdisk_segment_desc*    sd;
786  const rtems_fdisk_driver_handlers* ops;
787  device = sc->device;
788  segment = sc->segment;
789  sd = rtems_fdisk_seg_descriptor (fd, device, segment);
790  ops = fd->devices[device].descriptor->flash_ops;
791#if RTEMS_FDISK_TRACE
792  rtems_fdisk_printf (fd, "  seg-read: %02d-%03d: o=%08x s=%d",
793                      device, segment, offset, size);
794#endif
795  return ops->read (sd, device, segment, offset, buffer, size);
796}
797
798/**
799 * Write a block of data to a segment. It is assumed the
800 * location in the segment is erased and able to take the
801 * data.
802 */
803static int
804rtems_fdisk_seg_write (const rtems_flashdisk*   fd,
805                       rtems_fdisk_segment_ctl* sc,
806                       uint32_t                 offset,
807                       const void*              buffer,
808                       uint32_t                 size)
809{
810  int ret;
811  uint32_t                           device;
812  uint32_t                           segment;
813  const rtems_fdisk_segment_desc*    sd;
814  const rtems_fdisk_driver_handlers* ops;
815  device = sc->device;
816  segment = sc->segment;
817  sd = rtems_fdisk_seg_descriptor (fd, device, segment);
818  ops = fd->devices[device].descriptor->flash_ops;
819#if RTEMS_FDISK_TRACE
820  rtems_fdisk_printf (fd, "  seg-write: %02d-%03d: o=%08x s=%d",
821                      device, segment, offset, size);
822#endif
823  ret = ops->write (sd, device, segment, offset, buffer, size);
824  if (ret)
825    sc->failed = true;
826
827  return ret;
828}
829
830/**
831 * Blank check the area of a segment.
832 */
833static int
834rtems_fdisk_seg_blank_check (const rtems_flashdisk*   fd,
835                             rtems_fdisk_segment_ctl* sc,
836                             uint32_t                 offset,
837                             uint32_t                 size)
838{
839  uint32_t                           device;
840  uint32_t                           segment;
841  const rtems_fdisk_segment_desc*    sd;
842  const rtems_fdisk_driver_handlers* ops;
843  device = sc->device;
844  segment = sc->segment;
845  sd = rtems_fdisk_seg_descriptor (fd, device, segment);
846  ops = fd->devices[device].descriptor->flash_ops;
847#if RTEMS_FDISK_TRACE
848  rtems_fdisk_printf (fd, "  seg-blank: %02d-%03d: o=%08x s=%d",
849                      device, segment, offset, size);
850#endif
851  return ops->blank (sd, device, segment, offset, size);
852}
853/**
854 * Verify the data with the data in a segment.
855 */
856static int
857rtems_fdisk_seg_verify (const rtems_flashdisk* fd,
858                        uint32_t               device,
859                        uint32_t               segment,
860                        uint32_t               offset,
861                        const void*            buffer,
862                        uint32_t               size)
863{
864  const rtems_fdisk_segment_desc*    sd;
865  const rtems_fdisk_driver_handlers* ops;
866  sd  = rtems_fdisk_seg_descriptor (fd, device, segment);
867  ops = fd->devices[device].descriptor->flash_ops;
868#if RTEMS_FDISK_TRACE
869  rtems_fdisk_printf (fd, "  seg-verify: %02d-%03d: o=%08x s=%d",
870                      device, segment, offset, size);
871#endif
872  return ops->verify (sd, device, segment, offset, buffer, size);
873}
874
875/**
876 * Blank check a page of data in a segment.
877 */
878static int
879rtems_fdisk_seg_blank_check_page (const rtems_flashdisk*   fd,
880                                  rtems_fdisk_segment_ctl* sc,
881                                  uint32_t                 page)
882{
883  return rtems_fdisk_seg_blank_check (fd, sc,
884                                      page * fd->block_size, fd->block_size);
885}
886
887/**
888 * Read a page of data from a segment.
889 */
890static int
891rtems_fdisk_seg_read_page (const rtems_flashdisk*   fd,
892                           rtems_fdisk_segment_ctl* sc,
893                           uint32_t                 page,
894                           void*                    buffer)
895{
896  return rtems_fdisk_seg_read (fd, sc,
897                               page * fd->block_size, buffer, fd->block_size);
898}
899
900/**
901 * Write a page of data to a segment.
902 */
903static int
904rtems_fdisk_seg_write_page (rtems_flashdisk*         fd,
905                            rtems_fdisk_segment_ctl* sc,
906                            uint32_t                 page,
907                            const void*              buffer)
908{
909  if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
910  {
911    int ret = rtems_fdisk_seg_blank_check_page (fd, sc, page);
912    if (ret)
913      return ret;
914  }
915  --fd->erased_blocks;
916  return rtems_fdisk_seg_write (fd, sc,
917                                page * fd->block_size, buffer, fd->block_size);
918}
919
920/**
921 * Verify a page of data with the data in the segment.
922 */
923static int
924rtems_fdisk_seg_verify_page (const rtems_flashdisk* fd,
925                             uint32_t               device,
926                             uint32_t               segment,
927                             uint32_t               page,
928                             const void*            buffer)
929{
930  return rtems_fdisk_seg_verify (fd, device, segment,
931                                 page * fd->block_size, buffer, fd->block_size);
932}
933
934/**
935 * Copy a page of data from one segment to another segment.
936 */
937static int
938rtems_fdisk_seg_copy_page (rtems_flashdisk*         fd,
939                           rtems_fdisk_segment_ctl* src_sc,
940                           uint32_t                 src_page,
941                           rtems_fdisk_segment_ctl* dst_sc,
942                           uint32_t                 dst_page)
943{
944  int ret;
945#if RTEMS_FDISK_TRACE
946  rtems_fdisk_printf (fd, "  seg-copy-page: %02d-%03d~%03d=>%02d-%03d~%03d",
947                      src_sc->device, src_sc->segment, src_page,
948                      dst_sc->device, dst_sc->segment, dst_page);
949#endif
950  ret = rtems_fdisk_seg_read_page (fd, src_sc, src_page,
951                                   fd->copy_buffer);
952  if (ret)
953    return ret;
954  return rtems_fdisk_seg_write_page (fd, dst_sc, dst_page,
955                                     fd->copy_buffer);
956}
957
958/**
959 * Write the page descriptor to a segment. This code assumes the page
960 * descriptors are located at offset 0 in the segment.
961 */
962static int
963rtems_fdisk_seg_write_page_desc (const rtems_flashdisk*       fd,
964                                 rtems_fdisk_segment_ctl*     sc,
965                                 uint32_t                     page,
966                                 const rtems_fdisk_page_desc* page_desc)
967{
968  uint32_t offset = page * sizeof (rtems_fdisk_page_desc);
969  if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
970  {
971    int ret = rtems_fdisk_seg_blank_check (fd, sc,
972                                           offset,
973                                           sizeof (rtems_fdisk_page_desc));
974    if (ret)
975      return ret;
976  }
977  return rtems_fdisk_seg_write (fd, sc, offset,
978                                page_desc, sizeof (rtems_fdisk_page_desc));
979}
980
981/**
982 * Write the page descriptor flags to a segment. This code assumes the page
983 * descriptors are located at offset 0 in the segment.
984 */
985static int
986rtems_fdisk_seg_write_page_desc_flags (const rtems_flashdisk*       fd,
987                                       rtems_fdisk_segment_ctl*     sc,
988                                       uint32_t                     page,
989                                       const rtems_fdisk_page_desc* page_desc)
990{
991  uint32_t offset = ((page * sizeof (rtems_fdisk_page_desc)) +
992                     ((uint8_t*) &page_desc->flags) - ((uint8_t*) page_desc));
993  if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
994  {
995    uint16_t flash_flags;
996    int      ret;
997    ret = rtems_fdisk_seg_read (fd, sc, offset,
998                                &flash_flags, sizeof (flash_flags));
999    if (ret)
1000      return ret;
1001    if ((flash_flags & page_desc->flags) != page_desc->flags)
1002    {
1003      rtems_fdisk_error ("  seg-write-page-flags: %02d-%03d-%03d: "
1004                         "flags not erased: 0x%04 -> 0x%04x",
1005                         sc->device, sc->segment, page,
1006                         flash_flags, page_desc->flags);
1007      return ret;
1008    }
1009  }
1010  return rtems_fdisk_seg_write (fd, sc, offset,
1011                                &page_desc->flags, sizeof (page_desc->flags));
1012}
1013
1014/**
1015 * Erase a device.
1016 */
1017static int
1018rtems_fdisk_device_erase (const rtems_flashdisk* fd, uint32_t device)
1019{
1020  const rtems_fdisk_driver_handlers* ops;
1021  ops = fd->devices[device].descriptor->flash_ops;
1022#if RTEMS_FDISK_TRACE
1023  rtems_fdisk_printf (fd, " device-erase: %02d", device);
1024#endif
1025  return ops->erase_device (fd->devices[device].descriptor, device);
1026}
1027
1028/**
1029 * Erase all flash.
1030 */
1031static int
1032rtems_fdisk_erase_flash (const rtems_flashdisk* fd)
1033{
1034  uint32_t device;
1035  for (device = 0; device < fd->device_count; device++)
1036  {
1037    int ret;
1038
1039#if RTEMS_FDISK_TRACE
1040    rtems_fdisk_info (fd, " erase-flash:%02d", device);
1041#endif
1042
1043    ret = rtems_fdisk_device_erase (fd, device);
1044
1045    if (ret != 0)
1046      return ret;
1047  }
1048  return 0;
1049}
1050
1051/**
1052 * Calculate the checksum of a page in a segment.
1053 */
1054static uint16_t
1055rtems_fdisk_page_checksum (const uint8_t* buffer, uint32_t page_size)
1056{
1057  uint16_t cs = 0xffff;
1058  uint32_t i;
1059
1060  for (i = 0; i < page_size; i++, buffer++)
1061    cs = rtems_fdisk_calc_crc16 (*buffer, cs);
1062
1063  return cs;
1064}
1065
1066/**
1067 * Erase the segment.
1068 */
1069static int
1070rtems_fdisk_erase_segment (rtems_flashdisk* fd, rtems_fdisk_segment_ctl* sc)
1071{
1072  int                                ret;
1073  uint32_t                           device;
1074  uint32_t                           segment;
1075  const rtems_fdisk_segment_desc*    sd;
1076  const rtems_fdisk_driver_handlers* ops;
1077  device = sc->device;
1078  segment = sc->segment;
1079  sd = rtems_fdisk_seg_descriptor (fd, device, segment);
1080  ops = fd->devices[device].descriptor->flash_ops;
1081  ret = ops->erase (sd, device, segment);
1082  if (ret)
1083  {
1084    rtems_fdisk_error (" erase-segment:%02d-%03d: "      \
1085                       "segment erase failed: %s (%d)",
1086                       sc->device, sc->segment, strerror (ret), ret);
1087    sc->failed = true;
1088    if (!rtems_fdisk_segment_queue_present (&fd->failed, sc))
1089      rtems_fdisk_segment_queue_push_tail (&fd->failed, sc);
1090    return ret;
1091  }
1092
1093  fd->erased_blocks += sc->pages;
1094  sc->erased++;
1095
1096  memset (sc->page_descriptors, 0xff, sc->pages_desc * fd->block_size);
1097
1098  sc->pages_active = 0;
1099  sc->pages_used   = 0;
1100  sc->pages_bad    = 0;
1101
1102  sc->failed = false;
1103
1104  /*
1105   * Push to the tail of the available queue. It is a very
1106   * simple type of wear reduction. Every other available
1107   * segment will now get a go.
1108   */
1109  rtems_fdisk_segment_queue_push_tail (&fd->available, sc);
1110
1111  return 0;
1112}
1113
1114/**
1115 * Erase used segment.
1116 */
1117static int
1118rtems_fdisk_erase_used (rtems_flashdisk* fd)
1119{
1120  rtems_fdisk_segment_ctl* sc;
1121  int                      latched_ret = 0;
1122
1123  while ((sc = rtems_fdisk_segment_queue_pop_head (&fd->erase)))
1124  {
1125    /*
1126     * The segment will either end up on the available queue or
1127     * the failed queue.
1128     */
1129    int ret = rtems_fdisk_erase_segment (fd, sc);
1130    if (ret && !latched_ret)
1131      latched_ret = ret;
1132  }
1133
1134  return latched_ret;
1135}
1136
1137/**
1138 * Queue a segment. This is done after some of the stats for the segment
1139 * have been changed and this may effect the order the segment pages have in
1140 * the queue of available pages.
1141 *
1142 * @param fd The flash disk control table.
1143 * @param sc The segment control table to be reallocated
1144 */
1145static void
1146rtems_fdisk_queue_segment (rtems_flashdisk* fd, rtems_fdisk_segment_ctl* sc)
1147{
1148#if RTEMS_FDISK_TRACE
1149  rtems_fdisk_info (fd, " queue-seg:%02d-%03d: p=%d a=%d u=%d b=%d f=%s n=%s",
1150                    sc->device, sc->segment,
1151                    sc->pages, sc->pages_active, sc->pages_used, sc->pages_bad,
1152                    sc->failed ? "FAILED" : "no", sc->next ? "set" : "null");
1153#endif
1154
1155  /*
1156   * If the segment has failed then check the failed queue and append
1157   * if not failed.
1158   */
1159  if (sc->failed)
1160  {
1161    if (!rtems_fdisk_segment_queue_present (&fd->failed, sc))
1162      rtems_fdisk_segment_queue_push_tail (&fd->failed, sc);
1163    return;
1164  }
1165
1166  /*
1167   * Remove the queue from the available or used queue.
1168   */
1169  rtems_fdisk_segment_queue_remove (&fd->available, sc);
1170  rtems_fdisk_segment_queue_remove (&fd->used, sc);
1171
1172  /*
1173   * Are all the pages in the segment used ?
1174   * If they are and the driver has been configured to background
1175   * erase place the segment on the used queue. If not configured
1176   * to background erase perform the erase now.
1177   *
1178   */
1179  if (rtems_fdisk_seg_pages_available (sc) == 0)
1180  {
1181    if (sc->pages_active)
1182    {
1183      /*
1184       * Keep the used queue sorted by the most number of used
1185       * pages. When we compact we want to move the pages into
1186       * a new segment and cover more than one segment.
1187       */
1188      rtems_fdisk_segment_ctl* seg = fd->used.head;
1189
1190      while (seg)
1191      {
1192        if (sc->pages_used > seg->pages_used)
1193          break;
1194        seg = seg->next;
1195      }
1196
1197      if (seg)
1198        rtems_fdisk_segment_queue_insert_before (&fd->used, seg, sc);
1199      else
1200        rtems_fdisk_segment_queue_push_tail (&fd->used, sc);
1201    }
1202    else
1203    {
1204      if ((fd->flags & RTEMS_FDISK_BACKGROUND_ERASE))
1205        rtems_fdisk_segment_queue_push_tail (&fd->erase, sc);
1206      else
1207        rtems_fdisk_erase_segment (fd, sc);
1208    }
1209  }
1210  else
1211  {
1212    /*
1213     * The segment has pages available so place back onto the
1214     * available list. The list is sorted from the least number
1215     * of available pages to the most. This approach means
1216     * the pages of a partially filled segment will be filled
1217     * before moving onto another emptier segment. This keeps
1218     * empty segments longer aiding compaction.
1219     *
1220     * The down side is the wear effect as a single segment
1221     * could be used more than segment. This will not be
1222     * addressed until wear support is added.
1223     *
1224     * @note Wear support can be added by having counts for
1225     * for the number of times a segment is erased. This
1226     * available list is then sorted on the least number
1227     * of available pages then empty segments are sorted
1228     * on the least number of erases the segment has.
1229     *
1230     * The erase count can be stored in specially flaged
1231     * pages and contain a counter (32bits?) and 32 bits
1232     * for each segment. When a segment is erased a
1233     * bit is cleared for that segment. When 32 erasers
1234     * has occurred the page is re-written to the flash
1235     * with all the counters updated with the number of
1236     * bits cleared and all bits set back to 1.
1237     */
1238    rtems_fdisk_segment_ctl* seg = fd->available.head;
1239
1240    while (seg)
1241    {
1242      if (rtems_fdisk_seg_pages_available (sc) <
1243          rtems_fdisk_seg_pages_available (seg))
1244        break;
1245      seg = seg->next;
1246    }
1247
1248    if (seg)
1249      rtems_fdisk_segment_queue_insert_before (&fd->available, seg, sc);
1250    else
1251      rtems_fdisk_segment_queue_push_tail (&fd->available, sc);
1252  }
1253}
1254
1255static int
1256rtems_fdisk_recycle_segment (rtems_flashdisk*         fd,
1257                                    rtems_fdisk_segment_ctl* ssc,
1258                                    rtems_fdisk_segment_ctl* dsc,
1259                                    uint32_t *pages)
1260{
1261  int      ret;
1262  uint32_t spage;
1263  uint32_t used = 0;
1264  uint32_t active = 0;
1265
1266  for (spage = 0; spage < ssc->pages; spage++)
1267  {
1268    rtems_fdisk_page_desc* spd = &ssc->page_descriptors[spage];
1269
1270    if (rtems_fdisk_page_desc_flags_set (spd, RTEMS_FDISK_PAGE_ACTIVE) &&
1271        !rtems_fdisk_page_desc_flags_set (spd, RTEMS_FDISK_PAGE_USED))
1272    {
1273      uint32_t               dst_pages;
1274      rtems_fdisk_page_desc* dpd;
1275      uint32_t               dpage;
1276
1277      dpage = rtems_fdisk_seg_next_available_page (dsc);
1278      dpd   = &dsc->page_descriptors[dpage];
1279
1280      active++;
1281
1282      if (dpage >= dsc->pages)
1283      {
1284        rtems_fdisk_error ("recycle: %02d-%03d: " \
1285                           "no page desc available: %d",
1286                           dsc->device, dsc->segment,
1287                           rtems_fdisk_seg_pages_available (dsc));
1288        dsc->failed = true;
1289        rtems_fdisk_queue_segment (fd, dsc);
1290        rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
1291        return EIO;
1292      }
1293
1294#if RTEMS_FDISK_TRACE
1295      rtems_fdisk_info (fd, "recycle: %02d-%03d-%03d=>%02d-%03d-%03d",
1296                        ssc->device, ssc->segment, spage,
1297                        dsc->device, dsc->segment, dpage);
1298#endif
1299      ret = rtems_fdisk_seg_copy_page (fd, ssc,
1300                                       spage + ssc->pages_desc,
1301                                       dsc,
1302                                       dpage + dsc->pages_desc);
1303      if (ret)
1304      {
1305        rtems_fdisk_error ("recycle: %02d-%03d-%03d=>" \
1306                           "%02d-%03d-%03d: "             \
1307                           "copy page failed: %s (%d)",
1308                           ssc->device, ssc->segment, spage,
1309                           dsc->device, dsc->segment, dpage,
1310                           strerror (ret), ret);
1311        rtems_fdisk_queue_segment (fd, dsc);
1312        rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
1313        return ret;
1314      }
1315
1316      *dpd = *spd;
1317
1318      ret = rtems_fdisk_seg_write_page_desc (fd,
1319                                             dsc,
1320                                             dpage, dpd);
1321
1322      if (ret)
1323      {
1324        rtems_fdisk_error ("recycle: %02d-%03d-%03d=>"   \
1325                           "%02d-%03d-%03d: copy pd failed: %s (%d)",
1326                           ssc->device, ssc->segment, spage,
1327                           dsc->device, dsc->segment, dpage,
1328                           strerror (ret), ret);
1329        rtems_fdisk_queue_segment (fd, dsc);
1330        rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
1331        return ret;
1332      }
1333
1334      dsc->pages_active++;
1335
1336      /*
1337       * No need to set the used bit on the source page as the
1338       * segment will be erased. Power down could be a problem.
1339       * We do the stats to make sure everything is as it should
1340       * be.
1341       */
1342
1343      ssc->pages_active--;
1344      ssc->pages_used++;
1345
1346      fd->blocks[spd->block].segment = dsc;
1347      fd->blocks[spd->block].page    = dpage;
1348
1349      /*
1350       * Place the segment on to the correct queue.
1351       */
1352      rtems_fdisk_queue_segment (fd, dsc);
1353
1354      /*
1355       * Get new destination segment if necessary.
1356       */
1357      dst_pages = rtems_fdisk_seg_pages_available (dsc);
1358      if (dst_pages == 0)
1359      {
1360        dsc = rtems_fdisk_seg_most_available (&fd->available);
1361        if (!dsc)
1362        {
1363          if (ssc->pages_active == 0)
1364          {
1365            ret = rtems_fdisk_erase_segment (fd, ssc);
1366          }
1367          else
1368          {
1369            rtems_fdisk_error ("recycle: no available dst segment");
1370            ret = EIO;
1371          }
1372
1373          return ret;
1374        }
1375      }
1376
1377      (*pages)--;
1378    }
1379    else if (rtems_fdisk_page_desc_erased (spd))
1380    {
1381      --fd->erased_blocks;
1382    }
1383    else
1384    {
1385      used++;
1386    }
1387  }
1388
1389#if RTEMS_FDISK_TRACE
1390  rtems_fdisk_printf (fd, "ssc end: %d-%d: p=%ld, a=%ld, u=%ld",
1391                      ssc->device, ssc->segment,
1392                      pages, active, used);
1393#endif
1394  if (ssc->pages_active != 0)
1395  {
1396    rtems_fdisk_error ("compacting: ssc pages not 0: %d",
1397                       ssc->pages_active);
1398  }
1399
1400  ret = rtems_fdisk_erase_segment (fd, ssc);
1401
1402  return ret;
1403}
1404
1405/**
1406 * Compact the used segments to free what is available. Find the segment
1407 * with the most available number of pages and see if we have
1408 * used segments that will fit. The used queue is sorted on the least
1409 * number of active pages.
1410 */
1411static int
1412rtems_fdisk_compact (rtems_flashdisk* fd)
1413{
1414  int ret;
1415  rtems_fdisk_segment_ctl* dsc;
1416  rtems_fdisk_segment_ctl* ssc;
1417  uint32_t compacted_segs = 0;
1418  uint32_t pages;
1419
1420  if (rtems_fdisk_is_erased_blocks_starvation (fd))
1421  {
1422#if RTEMS_FDISK_TRACE
1423    rtems_fdisk_printf (fd, " resolve starvation");
1424#endif
1425
1426    ssc = rtems_fdisk_segment_queue_pop_head (&fd->used);
1427    if (!ssc)
1428      ssc = rtems_fdisk_segment_queue_pop_head (&fd->available);
1429
1430    if (ssc)
1431    {
1432      dsc = rtems_fdisk_seg_most_available (&fd->available);
1433      if (dsc)
1434      {
1435        ret = rtems_fdisk_recycle_segment (fd, ssc, dsc, &pages);
1436        if (ret)
1437          return ret;
1438      }
1439      else
1440      {
1441        rtems_fdisk_error ("compacting: starvation");
1442        return EIO;
1443      }
1444    }
1445    else
1446    {
1447      rtems_fdisk_error ("compacting: nothing to recycle");
1448      return EIO;
1449    }
1450  }
1451
1452  while (fd->used.head)
1453  {
1454    uint32_t                 dst_pages;
1455    uint32_t                 segments;
1456
1457#if RTEMS_FDISK_TRACE
1458    rtems_fdisk_printf (fd, " compacting");
1459#endif
1460
1461    dsc = rtems_fdisk_seg_most_available (&fd->available);
1462
1463    if (dsc == 0)
1464    {
1465      rtems_fdisk_error ("compacting: no available segments to compact too");
1466      return EIO;
1467    }
1468
1469    ssc = fd->used.head;
1470    dst_pages = rtems_fdisk_seg_pages_available (dsc);
1471    segments = 0;
1472    pages = 0;
1473
1474#if RTEMS_FDISK_TRACE
1475    rtems_fdisk_printf (fd, " dsc:%02d-%03d: most available",
1476                        dsc->device, dsc->segment);
1477#endif
1478
1479    /*
1480     * Count the number of segments that have active pages that fit into
1481     * the destination segment. Also limit the number of segments that
1482     * we handle during one compaction. A lower number means less aggressive
1483     * compaction or less delay when compacting but it may mean the disk
1484     * will fill.
1485     */
1486
1487    while (ssc &&
1488           ((pages + ssc->pages_active) < dst_pages) &&
1489           ((compacted_segs + segments) < fd->compact_segs))
1490    {
1491      pages += ssc->pages_active;
1492      segments++;
1493      ssc = ssc->next;
1494    }
1495
1496    /*
1497     * We need a source segment and have pages to copy and
1498     * compacting one segment to another is silly. Compaction needs
1499     * to free at least one more segment.
1500     */
1501
1502    if (!ssc || (pages == 0) || ((compacted_segs + segments) == 1))
1503    {
1504#if RTEMS_FDISK_TRACE
1505      rtems_fdisk_printf (fd, " nothing to compact");
1506#endif
1507      break;
1508    }
1509
1510#if RTEMS_FDISK_TRACE
1511    rtems_fdisk_printf (fd, " ssc scan: %d-%d: p=%ld, seg=%ld",
1512                        ssc->device, ssc->segment,
1513                        pages, segments);
1514#endif
1515
1516    rtems_fdisk_segment_queue_remove (&fd->available, dsc);
1517
1518    /*
1519     * We now copy the pages to the new segment.
1520     */
1521
1522    while (pages)
1523    {
1524      ssc = rtems_fdisk_segment_queue_pop_head (&fd->used);
1525
1526      if (ssc)
1527      {
1528        ret = rtems_fdisk_recycle_segment (fd, ssc, dsc, &pages);
1529        if (ret)
1530          return ret;
1531      }
1532    }
1533
1534    compacted_segs += segments;
1535  }
1536
1537  return 0;
1538}
1539
1540/**
1541 * Recover the block mappings from the devices.
1542 */
1543static int
1544rtems_fdisk_recover_block_mappings (rtems_flashdisk* fd)
1545{
1546  uint32_t device;
1547
1548  /*
1549   * Clear the queues.
1550   */
1551  rtems_fdisk_segment_queue_init (&fd->available);
1552  rtems_fdisk_segment_queue_init (&fd->used);
1553  rtems_fdisk_segment_queue_init (&fd->erase);
1554  rtems_fdisk_segment_queue_init (&fd->failed);
1555
1556  /*
1557   * Clear the lock mappings.
1558   */
1559  memset (fd->blocks, 0, fd->block_count * sizeof (rtems_fdisk_block_ctl));
1560
1561  /*
1562   * Scan each segment or each device recovering the valid pages.
1563   */
1564  fd->erased_blocks = 0;
1565  fd->starvation_threshold = 0;
1566  for (device = 0; device < fd->device_count; device++)
1567  {
1568    uint32_t segment;
1569    for (segment = 0; segment < fd->devices[device].segment_count; segment++)
1570    {
1571      rtems_fdisk_segment_ctl*        sc = &fd->devices[device].segments[segment];
1572      const rtems_fdisk_segment_desc* sd = sc->descriptor;
1573      rtems_fdisk_page_desc*          pd;
1574      uint32_t                        page;
1575      int                             ret;
1576
1577#if RTEMS_FDISK_TRACE
1578      rtems_fdisk_info (fd, "recover-block-mappings:%02d-%03d", device, segment);
1579#endif
1580
1581      sc->pages_desc = rtems_fdisk_page_desc_pages (sd, fd->block_size);
1582      sc->pages =
1583        rtems_fdisk_pages_in_segment (sd, fd->block_size) - sc->pages_desc;
1584      if (sc->pages > fd->starvation_threshold)
1585        fd->starvation_threshold = sc->pages;
1586
1587      sc->pages_active = 0;
1588      sc->pages_used   = 0;
1589      sc->pages_bad    = 0;
1590
1591      sc->failed = false;
1592
1593      if (!sc->page_descriptors)
1594        sc->page_descriptors = malloc (sc->pages_desc * fd->block_size);
1595
1596      if (!sc->page_descriptors)
1597        rtems_fdisk_abort ("no memory for page descriptors");
1598
1599      pd = sc->page_descriptors;
1600
1601      /*
1602       * The page descriptors are always at the start of the segment. Read
1603       * the descriptors off the device into the segment control page
1604       * descriptors.
1605       *
1606       * @todo It may be better to ask the driver to get these value
1607       *       so NAND flash could be better supported.
1608       */
1609      ret = rtems_fdisk_seg_read (fd, sc, 0, (void*) pd,
1610                                  sc->pages_desc * fd->block_size);
1611
1612      if (ret)
1613      {
1614        rtems_fdisk_error ("recover-block-mappings:%02d-%03d: " \
1615                           "read page desc failed: %s (%d)",
1616                           device, segment, strerror (ret), ret);
1617        return ret;
1618      }
1619
1620      /*
1621       * Check each page in the segement for valid pages.
1622       * Update the stats for the segment so we know how many pages
1623       * are active and how many are used.
1624       *
1625       * If the page is active see if the block is with-in range and
1626       * if the block is a duplicate.
1627       */
1628      for (page = 0; page < sc->pages; page++, pd++)
1629      {
1630        if (rtems_fdisk_page_desc_erased (pd))
1631        {
1632          /*
1633           * Is the page erased ?
1634           */
1635          ret = rtems_fdisk_seg_blank_check_page (fd, sc,
1636                                                  page + sc->pages_desc);
1637
1638          if (ret == 0)
1639          {
1640            ++fd->erased_blocks;
1641          }
1642          else
1643          {
1644#if RTEMS_FDISK_TRACE
1645            rtems_fdisk_warning (fd, "page not blank: %d-%d-%d",
1646                                 device, segment, page, pd->block);
1647#endif
1648            rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_USED);
1649
1650            ret = rtems_fdisk_seg_write_page_desc (fd, sc,
1651                                                   page, pd);
1652
1653            if (ret)
1654            {
1655              rtems_fdisk_error ("forcing page to used failed: %d-%d-%d",
1656                                 device, segment, page);
1657            }
1658
1659            sc->pages_used++;
1660          }
1661        }
1662        else
1663        {
1664          if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_USED))
1665          {
1666            sc->pages_used++;
1667          }
1668          else if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_ACTIVE))
1669          {
1670            if (pd->block >= fd->block_count)
1671            {
1672#if RTEMS_FDISK_TRACE
1673              rtems_fdisk_warning (fd,
1674                                   "invalid block number: %d-%d-%d: block: %d",
1675                                   device, segment, page, pd->block);
1676#endif
1677              sc->pages_bad++;
1678            }
1679            else if (fd->blocks[pd->block].segment)
1680            {
1681              /**
1682               * @todo
1683               * This may need more work later. Maybe a counter is stored with
1684               * each block so we can tell which is the later block when
1685               * duplicates appear. A power down with a failed wirte could cause
1686               * a duplicate.
1687               */
1688              const rtems_fdisk_segment_ctl* bsc = fd->blocks[pd->block].segment;
1689              rtems_fdisk_error ("duplicate block: %d-%d-%d: " \
1690                                 "duplicate: %d-%d-%d",
1691                                 bsc->device, bsc->segment,
1692                                 fd->blocks[pd->block].page,
1693                                 device, segment, page);
1694              sc->pages_bad++;
1695            }
1696            else
1697            {
1698              /**
1699               * @todo
1700               * Add start up crc checks here.
1701               */
1702              fd->blocks[pd->block].segment = sc;
1703              fd->blocks[pd->block].page    = page;
1704
1705              /*
1706               * The page is active.
1707               */
1708              sc->pages_active++;
1709            }
1710          }
1711          else
1712            sc->pages_bad++;
1713        }
1714      }
1715
1716      /*
1717       * Place the segment on to the correct queue.
1718       */
1719      rtems_fdisk_queue_segment (fd, sc);
1720    }
1721  }
1722
1723  return 0;
1724}
1725
1726/**
1727 * Read a block. The block is checked to see if the page referenced
1728 * is valid and the page has a valid crc.
1729 *
1730 * @param fd The rtems_flashdisk control table.
1731 * @param block The block number to read.
1732 * @param buffer The buffer to write the data into.
1733 * @return 0 No error.
1734 * @return EIO Invalid block size, block number, segment pointer, crc,
1735 *             page flags.
1736 */
1737static bool
1738rtems_fdisk_read_block (rtems_flashdisk* fd,
1739                        uint32_t         block,
1740                        uint8_t*         buffer)
1741{
1742  rtems_fdisk_block_ctl*   bc;
1743  rtems_fdisk_segment_ctl* sc;
1744  rtems_fdisk_page_desc*   pd;
1745
1746#if RTEMS_FDISK_TRACE
1747  rtems_fdisk_info (fd, "read-block:%d", block);
1748#endif
1749
1750  /*
1751   * Broken out to allow info messages when testing.
1752   */
1753
1754  if (block >= (fd->block_count - fd->unavail_blocks))
1755  {
1756    rtems_fdisk_error ("read-block: block out of range: %d", block);
1757    return EIO;
1758  }
1759
1760  bc = &fd->blocks[block];
1761
1762  if (!bc->segment)
1763  {
1764#if RTEMS_FDISK_TRACE
1765    rtems_fdisk_info (fd, "read-block: no segment mapping: %d", block);
1766#endif
1767    memset (buffer, 0xff, fd->block_size);
1768    return 0;
1769  }
1770
1771  sc = fd->blocks[block].segment;
1772  pd = &sc->page_descriptors[bc->page];
1773
1774#if RTEMS_FDISK_TRACE
1775  rtems_fdisk_info (fd,
1776                    " read:%d=>%02d-%03d-%03d: p=%d a=%d u=%d b=%d n=%s: " \
1777                    "f=%04x c=%04x b=%d",
1778                    block, sc->device, sc->segment, bc->page,
1779                    sc->pages, sc->pages_active, sc->pages_used, sc->pages_bad,
1780                    sc->next ? "set" : "null",
1781                    pd->flags, pd->crc, pd->block);
1782#endif
1783
1784  if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_ACTIVE))
1785  {
1786    if (rtems_fdisk_page_desc_flags_clear (pd, RTEMS_FDISK_PAGE_USED))
1787    {
1788      uint16_t cs;
1789
1790      /*
1791       * We use the segment page offset not the page number used in the
1792       * driver. This skips the page descriptors.
1793       */
1794      int ret = rtems_fdisk_seg_read_page (fd, sc,
1795                                           bc->page + sc->pages_desc, buffer);
1796
1797      if (ret)
1798      {
1799#if RTEMS_FDISK_TRACE
1800        rtems_fdisk_info (fd,
1801                          "read-block:%02d-%03d-%03d: read page failed: %s (%d)",
1802                          sc->device, sc->segment, bc->page,
1803                          strerror (ret), ret);
1804#endif
1805        return ret;
1806      }
1807
1808      cs = rtems_fdisk_page_checksum (buffer, fd->block_size);
1809
1810      if (cs == pd->crc)
1811        return 0;
1812
1813      rtems_fdisk_error ("read-block: crc failure: %d: buffer:%04x page:%04x",
1814                         block, cs, pd->crc);
1815    }
1816    else
1817    {
1818      rtems_fdisk_error ("read-block: block points to used page: %d: %d-%d-%d",
1819                         block, sc->device, sc->segment, bc->page);
1820    }
1821  }
1822  else
1823  {
1824    rtems_fdisk_error ("read-block: block page not active: %d: %d-%d-%d",
1825                       block, sc->device, sc->segment, bc->page);
1826  }
1827
1828  return EIO;
1829}
1830
1831/**
1832 * Write a block. The block:
1833 *
1834 *  # May never have existed in flash before this write.
1835 *  # Exists and needs to be moved to a new page.
1836 *
1837 * If the block does not exist in flash we need to get the next
1838 * segment available to place the page into. The segments with
1839 * available pages are held on the avaliable list sorted on least
1840 * number of available pages as the primary key. Currently there
1841 * is no secondary key. Empty segments are at the end of the list.
1842 *
1843 * If the block already exists we need to set the USED bit in the
1844 * current page's flags. This is a single byte which changes a 1 to
1845 * a 0 and can be done with a single 16 bit write. The driver for
1846 * 8 bit devices should only attempt the write on the changed bit.
1847 *
1848 * @param fd The rtems_flashdisk control table.
1849 * @param block The block number to read.
1850 * @param block_size The size of the block. Must match what we have.
1851 * @param buffer The buffer to write the data into.
1852 * @return 0 No error.
1853 * @return EIO Invalid block size, block number, segment pointer, crc,
1854 *             page flags.
1855 */
1856static int
1857rtems_fdisk_write_block (rtems_flashdisk* fd,
1858                         uint32_t         block,
1859                         const uint8_t*   buffer)
1860{
1861  rtems_fdisk_block_ctl*   bc;
1862  rtems_fdisk_segment_ctl* sc;
1863  rtems_fdisk_page_desc*   pd;
1864  uint32_t                 page;
1865  int                      ret;
1866
1867#if RTEMS_FDISK_TRACE
1868  rtems_fdisk_info (fd, "write-block:%d", block);
1869#endif
1870
1871  /*
1872   * Broken out to allow info messages when testing.
1873   */
1874
1875  if (block >= (fd->block_count - fd->unavail_blocks))
1876  {
1877    rtems_fdisk_error ("write-block: block out of range: %d", block);
1878    return EIO;
1879  }
1880
1881  bc = &fd->blocks[block];
1882
1883  /*
1884   * Does the page exist in flash ?
1885   */
1886  if (bc->segment)
1887  {
1888    sc = bc->segment;
1889    pd = &sc->page_descriptors[bc->page];
1890
1891#if RTEMS_FDISK_TRACE
1892    rtems_fdisk_info (fd, " write:%02d-%03d-%03d: flag used",
1893                      sc->device, sc->segment, bc->page);
1894#endif
1895
1896    /*
1897     * The page exists in flash so see if the page has been changed.
1898     */
1899    if (rtems_fdisk_seg_verify_page (fd, sc->device, sc->segment,
1900                                     bc->page + sc->pages_desc, buffer) == 0)
1901    {
1902#if RTEMS_FDISK_TRACE
1903      rtems_fdisk_info (fd, "write-block:%d=>%02d-%03d-%03d: page verified",
1904                        block, sc->device, sc->segment, bc->page);
1905#endif
1906      return 0;
1907    }
1908
1909    /*
1910     * The page exists in flash so we need to set the used flag
1911     * in the page descriptor. The descriptor is in memory with the
1912     * segment control block. We can assume this memory copy
1913     * matches the flash device.
1914     */
1915
1916    rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_USED);
1917
1918    ret = rtems_fdisk_seg_write_page_desc_flags (fd, sc, bc->page, pd);
1919
1920    if (ret)
1921    {
1922#if RTEMS_FDISK_TRACE
1923      rtems_fdisk_info (fd, " write:%02d-%03d-%03d: "      \
1924                        "write used page desc failed: %s (%d)",
1925                        sc->device, sc->segment, bc->page,
1926                        strerror (ret), ret);
1927#endif
1928    }
1929    else
1930    {
1931      sc->pages_active--;
1932      sc->pages_used++;
1933    }
1934
1935    /*
1936     * If possible reuse this segment. This will mean the segment
1937     * needs to be removed from the available list and placed
1938     * back if space is still available.
1939     */
1940    rtems_fdisk_queue_segment (fd, sc);
1941
1942    /*
1943     * If no background compacting then compact in the forground.
1944     * If we compact we ignore the error as there is little we
1945     * can do from here. The write may will work.
1946     */
1947    if ((fd->flags & RTEMS_FDISK_BACKGROUND_COMPACT) == 0)
1948      rtems_fdisk_compact (fd);
1949  }
1950
1951  /*
1952   * Is it time to compact the disk ?
1953   *
1954   * We override the background compaction configruation.
1955   */
1956  if (rtems_fdisk_segment_count_queue (&fd->available) <=
1957      fd->avail_compact_segs)
1958    rtems_fdisk_compact (fd);
1959
1960  /*
1961   * Get the next avaliable segment.
1962   */
1963  sc = rtems_fdisk_segment_queue_pop_head (&fd->available);
1964
1965  /*
1966   * Is the flash disk full ?
1967   */
1968  if (!sc)
1969  {
1970    /*
1971     * If compacting is configured for the background do it now
1972     * to see if we can get some space back.
1973     */
1974    if ((fd->flags & RTEMS_FDISK_BACKGROUND_COMPACT))
1975      rtems_fdisk_compact (fd);
1976
1977    /*
1978     * Try again for some free space.
1979     */
1980    sc = rtems_fdisk_segment_queue_pop_head (&fd->available);
1981
1982    if (!sc)
1983    {
1984      rtems_fdisk_error ("write-block: no available pages");
1985      return ENOSPC;
1986    }
1987  }
1988
1989#if RTEMS_FDISK_TRACE
1990  if (fd->info_level >= 3)
1991  {
1992    char queues[5];
1993    rtems_fdisk_queue_status (fd, sc, queues);
1994    rtems_fdisk_info (fd, " write:%d=>%02d-%03d: queue check: %s",
1995                      block, sc->device, sc->segment, queues);
1996  }
1997#endif
1998
1999  /*
2000   * Find the next avaliable page in the segment.
2001   */
2002
2003  pd = sc->page_descriptors;
2004
2005  for (page = 0; page < sc->pages; page++, pd++)
2006  {
2007    if (rtems_fdisk_page_desc_erased (pd))
2008    {
2009      pd->crc   = rtems_fdisk_page_checksum (buffer, fd->block_size);
2010      pd->block = block;
2011
2012      bc->segment = sc;
2013      bc->page    = page;
2014
2015      rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_ACTIVE);
2016
2017#if RTEMS_FDISK_TRACE
2018      rtems_fdisk_info (fd, " write:%d=>%02d-%03d-%03d: write: " \
2019                        "p=%d a=%d u=%d b=%d n=%s: f=%04x c=%04x b=%d",
2020                        block, sc->device, sc->segment, page,
2021                        sc->pages, sc->pages_active, sc->pages_used,
2022                        sc->pages_bad, sc->next ? "set" : "null",
2023                        pd->flags, pd->crc, pd->block);
2024#endif
2025
2026      /*
2027       * We use the segment page offset not the page number used in the
2028       * driver. This skips the page descriptors.
2029       */
2030      ret = rtems_fdisk_seg_write_page (fd, sc, page + sc->pages_desc, buffer);
2031      if (ret)
2032      {
2033#if RTEMS_FDISK_TRACE
2034        rtems_fdisk_info (fd, "write-block:%02d-%03d-%03d: write page failed: " \
2035                          "%s (%d)", sc->device, sc->segment, page,
2036                          strerror (ret), ret);
2037#endif
2038      }
2039      else
2040      {
2041        ret = rtems_fdisk_seg_write_page_desc (fd, sc, page, pd);
2042        if (ret)
2043        {
2044#if RTEMS_FDISK_TRACE
2045          rtems_fdisk_info (fd, "write-block:%02d-%03d-%03d: "  \
2046                            "write page desc failed: %s (%d)",
2047                            sc->device, sc->segment, bc->page,
2048                            strerror (ret), ret);
2049#endif
2050        }
2051        else
2052        {
2053          sc->pages_active++;
2054        }
2055      }
2056
2057      rtems_fdisk_queue_segment (fd, sc);
2058
2059      if (rtems_fdisk_is_erased_blocks_starvation (fd))
2060        rtems_fdisk_compact (fd);
2061
2062      return ret;
2063    }
2064  }
2065
2066  rtems_fdisk_error ("write-block: no erased page descs in segment: %d-%d",
2067                     sc->device, sc->segment);
2068
2069  sc->failed = true;
2070  rtems_fdisk_queue_segment (fd, sc);
2071
2072  return EIO;
2073}
2074
2075/**
2076 * Disk READ request handler. This primitive copies data from the
2077 * flash disk to the supplied buffer and invoke the callout function
2078 * to inform upper layer that reading is completed.
2079 *
2080 * @param req Pointer to the READ block device request info.
2081 * @retval 0 Always.  The request done callback contains the status.
2082 */
2083static int
2084rtems_fdisk_read (rtems_flashdisk* fd, rtems_blkdev_request* req)
2085{
2086  rtems_blkdev_sg_buffer* sg = req->bufs;
2087  uint32_t                buf;
2088  int                     ret = 0;
2089
2090  for (buf = 0; (ret == 0) && (buf < req->bufnum); buf++, sg++)
2091  {
2092    uint8_t* data;
2093    uint32_t fb;
2094    uint32_t b;
2095    fb = sg->length / fd->block_size;
2096    data = sg->buffer;
2097    for (b = 0; b < fb; b++, data += fd->block_size)
2098    {
2099      ret = rtems_fdisk_read_block (fd, sg->block + b, data);
2100      if (ret)
2101        break;
2102    }
2103  }
2104
2105  rtems_blkdev_request_done (req, ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL);
2106
2107  return 0;
2108}
2109
2110/**
2111 * Flash disk WRITE request handler. This primitive copies data from
2112 * supplied buffer to flash disk and invoke the callout function to inform
2113 * upper layer that writing is completed.
2114 *
2115 * @param req Pointers to the WRITE block device request info.
2116 * @retval 0 Always.  The request done callback contains the status.
2117 */
2118static int
2119rtems_fdisk_write (rtems_flashdisk* fd, rtems_blkdev_request* req)
2120{
2121  rtems_blkdev_sg_buffer* sg = req->bufs;
2122  uint32_t                buf;
2123  int                     ret = 0;
2124
2125  for (buf = 0; (ret == 0) && (buf < req->bufnum); buf++, sg++)
2126  {
2127    uint8_t* data;
2128    uint32_t fb;
2129    uint32_t b;
2130    fb = sg->length / fd->block_size;
2131    data = sg->buffer;
2132    for (b = 0; b < fb; b++, data += fd->block_size)
2133    {
2134      ret = rtems_fdisk_write_block (fd, sg->block + b, data);
2135      if (ret)
2136        break;
2137    }
2138  }
2139
2140  rtems_blkdev_request_done (req, ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL);
2141
2142  return 0;
2143}
2144
2145/**
2146 * Flash disk erase disk.
2147 *
2148 * @param fd The flashdisk data.
2149 * @retval int The ioctl return value.
2150 */
2151static int
2152rtems_fdisk_erase_disk (rtems_flashdisk* fd)
2153{
2154  uint32_t device;
2155  int      ret;
2156
2157#if RTEMS_FDISK_TRACE
2158  rtems_fdisk_info (fd, "erase-disk");
2159#endif
2160
2161  ret = rtems_fdisk_erase_flash (fd);
2162
2163  if (ret == 0)
2164  {
2165    for (device = 0; device < fd->device_count; device++)
2166    {
2167      if (!fd->devices[device].segments)
2168        return ENOMEM;
2169
2170      ret = rtems_fdisk_recover_block_mappings (fd);
2171      if (ret)
2172        break;
2173    }
2174  }
2175
2176  return ret;
2177}
2178
2179/**
2180 * Flash Disk Monitoring data is return in the monitoring data
2181 * structure.
2182 */
2183static int
2184rtems_fdisk_monitoring_data (rtems_flashdisk*          fd,
2185                             rtems_fdisk_monitor_data* data)
2186{
2187  uint32_t i;
2188  uint32_t j;
2189
2190  data->block_size     = fd->block_size;
2191  data->block_count    = fd->block_count;
2192  data->unavail_blocks = fd->unavail_blocks;
2193  data->device_count   = fd->device_count;
2194
2195  data->blocks_used = 0;
2196  for (i = 0; i < fd->block_count; i++)
2197    if (fd->blocks[i].segment)
2198      data->blocks_used++;
2199
2200  data->segs_available = rtems_fdisk_segment_count_queue (&fd->available);
2201  data->segs_used      = rtems_fdisk_segment_count_queue (&fd->used);
2202  data->segs_failed    = rtems_fdisk_segment_count_queue (&fd->failed);
2203
2204  data->segment_count = 0;
2205  data->page_count    = 0;
2206  data->pages_desc    = 0;
2207  data->pages_active  = 0;
2208  data->pages_used    = 0;
2209  data->pages_bad     = 0;
2210  data->seg_erases    = 0;
2211
2212  for (i = 0; i < fd->device_count; i++)
2213  {
2214    data->segment_count += fd->devices[i].segment_count;
2215
2216    for (j = 0; j < fd->devices[i].segment_count; j++)
2217    {
2218      rtems_fdisk_segment_ctl* sc = &fd->devices[i].segments[j];
2219
2220      data->page_count   += sc->pages;
2221      data->pages_desc   += sc->pages_desc;
2222      data->pages_active += sc->pages_active;
2223      data->pages_used   += sc->pages_used;
2224      data->pages_bad    += sc->pages_bad;
2225      data->seg_erases   += sc->erased;
2226    }
2227  }
2228
2229  data->info_level = fd->info_level;
2230  return 0;
2231}
2232
2233/**
2234 * Print to stdout the status of the driver. This is a debugging aid.
2235 */
2236static int
2237rtems_fdisk_print_status (rtems_flashdisk* fd)
2238{
2239#if RTEMS_FDISK_TRACE
2240  uint32_t current_info_level = fd->info_level;
2241  uint32_t total;
2242  uint32_t count;
2243  uint32_t device;
2244
2245  fd->info_level = 3;
2246
2247  rtems_fdisk_printf (fd,
2248                      "Flash Disk Driver Status : %d.%d", fd->major, fd->minor);
2249
2250  rtems_fdisk_printf (fd, "Block count\t%d", fd->block_count);
2251  rtems_fdisk_printf (fd, "Unavail blocks\t%d", fd->unavail_blocks);
2252  rtems_fdisk_printf (fd, "Starvation threshold\t%d", fd->starvation_threshold);
2253  rtems_fdisk_printf (fd, "Starvations\t%d", fd->starvations);
2254  count = rtems_fdisk_segment_count_queue (&fd->available);
2255  total = count;
2256  rtems_fdisk_printf (fd, "Available queue\t%ld (%ld)",
2257                      count, rtems_fdisk_segment_queue_count (&fd->available));
2258  count = rtems_fdisk_segment_count_queue (&fd->used);
2259  total += count;
2260  rtems_fdisk_printf (fd, "Used queue\t%ld (%ld)",
2261                      count, rtems_fdisk_segment_queue_count (&fd->used));
2262  count = rtems_fdisk_segment_count_queue (&fd->erase);
2263  total += count;
2264  rtems_fdisk_printf (fd, "Erase queue\t%ld (%ld)",
2265                      count, rtems_fdisk_segment_queue_count (&fd->erase));
2266  count = rtems_fdisk_segment_count_queue (&fd->failed);
2267  total += count;
2268  rtems_fdisk_printf (fd, "Failed queue\t%ld (%ld)",
2269                      count, rtems_fdisk_segment_queue_count (&fd->failed));
2270
2271  count = 0;
2272  for (device = 0; device < fd->device_count; device++)
2273    count += fd->devices[device].segment_count;
2274
2275  rtems_fdisk_printf (fd, "Queue total\t%ld of %ld, %s", total, count,
2276                      total == count ? "ok" : "MISSING");
2277
2278  rtems_fdisk_printf (fd, "Device count\t%d", fd->device_count);
2279
2280  for (device = 0; device < fd->device_count; device++)
2281  {
2282    uint32_t block;
2283    uint32_t seg;
2284
2285    rtems_fdisk_printf (fd, " Device\t\t%ld", device);
2286    rtems_fdisk_printf (fd, "  Segment count\t%ld",
2287                        fd->devices[device].segment_count);
2288
2289    for (seg = 0; seg < fd->devices[device].segment_count; seg++)
2290    {
2291      rtems_fdisk_segment_ctl* sc = &fd->devices[device].segments[seg];
2292      uint32_t                 page;
2293      uint32_t                 erased = 0;
2294      uint32_t                 active = 0;
2295      uint32_t                 used = 0;
2296      bool                     is_active = false;
2297      char                     queues[5];
2298
2299      rtems_fdisk_queue_status (fd, sc, queues);
2300
2301      for (page = 0; page < sc->pages; page++)
2302      {
2303        if (rtems_fdisk_page_desc_erased (&sc->page_descriptors[page]))
2304          erased++;
2305        else if (rtems_fdisk_page_desc_flags_set (&sc->page_descriptors[page],
2306                                                  RTEMS_FDISK_PAGE_ACTIVE))
2307        {
2308          if (rtems_fdisk_page_desc_flags_set (&sc->page_descriptors[page],
2309                                               RTEMS_FDISK_PAGE_USED))
2310            used++;
2311          else
2312          {
2313            active++;
2314            is_active = true;
2315          }
2316        }
2317
2318        for (block = 0; block < fd->block_count; block++)
2319        {
2320          if ((fd->blocks[block].segment == sc) &&
2321              (fd->blocks[block].page == page) && !is_active)
2322            rtems_fdisk_printf (fd,
2323                                "    %ld\t not active when mapped by block %ld",
2324                                page, block);
2325        }
2326      }
2327
2328      count = 0;
2329      for (block = 0; block < fd->block_count; block++)
2330      {
2331        if (fd->blocks[block].segment == sc)
2332          count++;
2333      }
2334
2335      rtems_fdisk_printf (fd, "  %3ld %s p:%3ld a:%3ld/%3ld" \
2336                          " u:%3ld/%3ld e:%3ld/%3ld br:%ld",
2337                          seg, queues,
2338                          sc->pages, sc->pages_active, active,
2339                          sc->pages_used, used, erased,
2340                          sc->pages - (sc->pages_active +
2341                                       sc->pages_used + sc->pages_bad),
2342                          count);
2343    }
2344  }
2345
2346  {
2347    rtems_fdisk_segment_ctl* sc = fd->used.head;
2348    int count = 0;
2349    rtems_fdisk_printf (fd, "Used List:");
2350    while (sc)
2351    {
2352      rtems_fdisk_printf (fd, "  %3d %02d:%03d u:%3ld",
2353                          count, sc->device, sc->segment, sc->pages_used);
2354      sc = sc->next;
2355      count++;
2356    }
2357  }
2358  fd->info_level = current_info_level;
2359
2360  return 0;
2361#else
2362  return ENOSYS;
2363#endif
2364}
2365
2366/**
2367 * Flash disk IOCTL handler.
2368 *
2369 * @param dd Disk device.
2370 * @param req IOCTL request code.
2371 * @param argp IOCTL argument.
2372 * @retval The IOCTL return value
2373 */
2374static int
2375rtems_fdisk_ioctl (rtems_disk_device *dd, uint32_t req, void* argp)
2376{
2377  dev_t                     dev = rtems_disk_get_device_identifier (dd);
2378  rtems_device_minor_number minor = rtems_filesystem_dev_minor_t (dev);
2379  rtems_blkdev_request*     r = argp;
2380
2381  errno = 0;
2382
2383  rtems_mutex_lock (&rtems_flashdisks[minor].lock);
2384
2385  switch (req)
2386  {
2387    case RTEMS_BLKIO_REQUEST:
2388      if ((minor >= rtems_flashdisk_count) ||
2389          (rtems_flashdisks[minor].device_count == 0))
2390      {
2391        errno = ENODEV;
2392      }
2393      else
2394      {
2395        switch (r->req)
2396        {
2397          case RTEMS_BLKDEV_REQ_READ:
2398            errno = rtems_fdisk_read (&rtems_flashdisks[minor], r);
2399            break;
2400
2401          case RTEMS_BLKDEV_REQ_WRITE:
2402            errno = rtems_fdisk_write (&rtems_flashdisks[minor], r);
2403            break;
2404
2405          default:
2406            errno = EINVAL;
2407            break;
2408        }
2409      }
2410      break;
2411
2412    case RTEMS_FDISK_IOCTL_ERASE_DISK:
2413      errno = rtems_fdisk_erase_disk (&rtems_flashdisks[minor]);
2414      break;
2415
2416    case RTEMS_FDISK_IOCTL_COMPACT:
2417      errno = rtems_fdisk_compact (&rtems_flashdisks[minor]);
2418      break;
2419
2420    case RTEMS_FDISK_IOCTL_ERASE_USED:
2421      errno = rtems_fdisk_erase_used (&rtems_flashdisks[minor]);
2422      break;
2423
2424    case RTEMS_FDISK_IOCTL_MONITORING:
2425      errno = rtems_fdisk_monitoring_data (&rtems_flashdisks[minor],
2426                                           (rtems_fdisk_monitor_data*) argp);
2427      break;
2428
2429    case RTEMS_FDISK_IOCTL_INFO_LEVEL:
2430      rtems_flashdisks[minor].info_level = (uintptr_t) argp;
2431      break;
2432
2433    case RTEMS_FDISK_IOCTL_PRINT_STATUS:
2434      errno = rtems_fdisk_print_status (&rtems_flashdisks[minor]);
2435      break;
2436
2437    default:
2438      rtems_blkdev_ioctl (dd, req, argp);
2439      break;
2440  }
2441
2442  rtems_mutex_unlock (&rtems_flashdisks[minor].lock);
2443
2444  return errno == 0 ? 0 : -1;
2445}
2446
2447/**
2448 * Flash disk device driver initialization.
2449 *
2450 * @todo Memory clean up on error is really badly handled.
2451 *
2452 * @param major Flash disk major device number.
2453 * @param minor Minor device number, not applicable.
2454 * @param arg Initialization argument, not applicable.
2455 */
2456rtems_device_driver
2457rtems_fdisk_initialize (rtems_device_major_number major,
2458                        rtems_device_minor_number minor,
2459                        void*                     arg RTEMS_UNUSED)
2460{
2461  const rtems_flashdisk_config* c = rtems_flashdisk_configuration;
2462  rtems_flashdisk*              fd;
2463  rtems_status_code             sc;
2464
2465  sc = rtems_disk_io_initialize ();
2466  if (sc != RTEMS_SUCCESSFUL)
2467    return sc;
2468
2469  sc = rtems_fdisk_crc16_gen_factors (0x8408);
2470  if (sc != RTEMS_SUCCESSFUL)
2471      return sc;
2472
2473  rtems_flashdisks = calloc (rtems_flashdisk_configuration_size,
2474                             sizeof (rtems_flashdisk));
2475
2476  if (!rtems_flashdisks)
2477    return RTEMS_NO_MEMORY;
2478
2479  for (minor = 0; minor < rtems_flashdisk_configuration_size; minor++, c++)
2480  {
2481    char     name[] = RTEMS_FLASHDISK_DEVICE_BASE_NAME "a";
2482    dev_t    dev = rtems_filesystem_make_dev_t (major, minor);
2483    uint32_t device;
2484    uint32_t blocks = 0;
2485    int      ret;
2486
2487    fd = &rtems_flashdisks[minor];
2488
2489    name [sizeof(RTEMS_FLASHDISK_DEVICE_BASE_NAME)] += minor;
2490
2491    fd->major              = major;
2492    fd->minor              = minor;
2493    fd->flags              = c->flags;
2494    fd->compact_segs       = c->compact_segs;
2495    fd->avail_compact_segs = c->avail_compact_segs;
2496    fd->block_size         = c->block_size;
2497    fd->unavail_blocks     = c->unavail_blocks;
2498    fd->info_level         = c->info_level;
2499
2500    for (device = 0; device < c->device_count; device++)
2501      blocks += rtems_fdisk_blocks_in_device (&c->devices[device],
2502                                              c->block_size);
2503
2504    /*
2505     * One copy buffer of a page size.
2506     */
2507    fd->copy_buffer = malloc (c->block_size);
2508    if (!fd->copy_buffer)
2509      return RTEMS_NO_MEMORY;
2510
2511    fd->blocks = calloc (blocks, sizeof (rtems_fdisk_block_ctl));
2512    if (!fd->blocks)
2513      return RTEMS_NO_MEMORY;
2514
2515    fd->block_count = blocks;
2516
2517    fd->devices = calloc (c->device_count, sizeof (rtems_fdisk_device_ctl));
2518    if (!fd->devices)
2519      return RTEMS_NO_MEMORY;
2520
2521    rtems_mutex_init (&fd->lock, "Flash Disk");
2522
2523    sc = rtems_disk_create_phys(dev, c->block_size,
2524                                blocks - fd->unavail_blocks,
2525                                rtems_fdisk_ioctl, NULL, name);
2526    if (sc != RTEMS_SUCCESSFUL)
2527    {
2528      rtems_mutex_destroy (&fd->lock);
2529      rtems_disk_delete (dev);
2530      free (fd->copy_buffer);
2531      free (fd->blocks);
2532      free (fd->devices);
2533      rtems_fdisk_error ("disk create phy failed");
2534      return sc;
2535    }
2536
2537    for (device = 0; device < c->device_count; device++)
2538    {
2539      rtems_fdisk_segment_ctl* sc;
2540      uint32_t                 segment_count;
2541      uint32_t                 segment;
2542
2543      segment_count = rtems_fdisk_count_segments (&c->devices[device]);
2544
2545      fd->devices[device].segments = calloc (segment_count,
2546                                             sizeof (rtems_fdisk_segment_ctl));
2547      if (!fd->devices[device].segments)
2548      {
2549        rtems_disk_delete (dev);
2550        rtems_mutex_destroy (&fd->lock);
2551        free (fd->copy_buffer);
2552        free (fd->blocks);
2553        free (fd->devices);
2554        return RTEMS_NO_MEMORY;
2555      }
2556
2557      sc = fd->devices[device].segments;
2558
2559      for (segment = 0; segment < c->devices[device].segment_count; segment++)
2560      {
2561        const rtems_fdisk_segment_desc* sd;
2562        uint32_t                        seg_segment;
2563
2564        sd = &c->devices[device].segments[segment];
2565
2566        for (seg_segment = 0; seg_segment < sd->count; seg_segment++, sc++)
2567        {
2568          sc->descriptor = sd;
2569          sc->device     = device;
2570          sc->segment    = seg_segment;
2571          sc->erased     = 0;
2572        }
2573      }
2574
2575      fd->devices[device].segment_count = segment_count;
2576      fd->devices[device].descriptor    = &c->devices[device];
2577    }
2578
2579    fd->device_count = c->device_count;
2580
2581    ret = rtems_fdisk_recover_block_mappings (fd);
2582    if (ret)
2583    {
2584      rtems_disk_delete (dev);
2585      rtems_mutex_destroy (&fd->lock);
2586      free (fd->copy_buffer);
2587      free (fd->blocks);
2588      free (fd->devices);
2589      rtems_fdisk_error ("recovery of disk failed: %s (%d)",
2590                         strerror (ret), ret);
2591      return ret;
2592    }
2593
2594    ret = rtems_fdisk_compact (fd);
2595    if (ret)
2596    {
2597      rtems_disk_delete (dev);
2598      rtems_mutex_destroy (&fd->lock);
2599      free (fd->copy_buffer);
2600      free (fd->blocks);
2601      free (fd->devices);
2602      rtems_fdisk_error ("compacting of disk failed: %s (%d)",
2603                         strerror (ret), ret);
2604      return ret;
2605    }
2606  }
2607
2608  rtems_flashdisk_count = rtems_flashdisk_configuration_size;
2609
2610  return RTEMS_SUCCESSFUL;
2611}
Note: See TracBrowser for help on using the repository browser.