source: rtems/cpukit/libblock/src/flashdisk.c @ 9f808d1f

4.115
Last change on this file since 9f808d1f was 9f808d1f, checked in by Sebastian Huber <sebastian.huber@…>, on 03/14/12 at 09:45:36

PR2040: libblock: Flash disk starvations statistic

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