source: rtems/cpukit/libblock/src/flashdisk.c @ 0d15414e

4.104.115
Last change on this file since 0d15414e was 918a0d8, checked in by Joel Sherrill <joel.sherrill@…>, on Jun 12, 2009 at 5:38:53 PM

2009-06-12 Joel Sherrill <joel.sherrill@…>

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