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

4.115
Last change on this file since 9de9b7d2 was f6c7bcfe, checked in by Mathew Kallada <matkallada@…>, on 12/21/12 at 17:42:39

libblock: Doxygen Enhancement Task #1

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