source: rtems/cpukit/libblock/src/flashdisk.c @ 1b68747

Last change on this file since 1b68747 was 1b68747, checked in by Sebastian Huber <sebastian.huber@…>, on 03/30/12 at 09:00:37

PR2040: libblock: Fix recycle destination update

Check the availablity of a recycle destination segment only when it is
necessary to avoid missing resycle source segment erasures.

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