source: rtems/cpukit/libblock/src/flashdisk.c @ 5c587596

4.104.115
Last change on this file since 5c587596 was 5c587596, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 01/19/10 at 09:10:03

libblock API update

  • Property mode set to 100644
File size: 74.4 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                buf;
2057  int                     ret = 0;
2058
2059  for (buf = 0; (ret == 0) && (buf < req->bufnum); buf++, sg++)
2060  {
2061    uint8_t* data;
2062    uint32_t fb;
2063    uint32_t b;
2064    fb = sg->length / fd->block_size;
2065    data = sg->buffer;
2066    for (b = 0; b < fb; b++, data += fd->block_size)
2067    {
2068      ret = rtems_fdisk_read_block (fd, sg->block + b, data);
2069      if (ret)
2070        break;
2071    }
2072  }
2073
2074  req->req_done (req->done_arg, ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR);
2075
2076  return ret;
2077}
2078
2079/**
2080 * Flash disk WRITE request handler. This primitive copies data from
2081 * supplied buffer to flash disk and invoke the callout function to inform
2082 * upper layer that writing is completed.
2083 *
2084 * @param req Pointers to the WRITE block device request info.
2085 * @retval int The ioctl return value.
2086 */
2087static int
2088rtems_fdisk_write (rtems_flashdisk* fd, rtems_blkdev_request* req)
2089{
2090  rtems_blkdev_sg_buffer* sg = req->bufs;
2091  uint32_t                buf;
2092  int                     ret = 0;
2093
2094  for (buf = 0; (ret == 0) && (buf < req->bufnum); buf++, sg++)
2095  {
2096    uint8_t* data;
2097    uint32_t fb;
2098    uint32_t b;
2099    fb = sg->length / fd->block_size;
2100    data = sg->buffer;
2101    for (b = 0; b < fb; b++, data += fd->block_size)
2102    {
2103      ret = rtems_fdisk_write_block (fd, sg->block + b, data);
2104      if (ret)
2105        break;
2106    }
2107  }
2108
2109  req->req_done (req->done_arg, ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR);
2110
2111  return 0;
2112}
2113
2114/**
2115 * Flash disk erase disk.
2116 *
2117 * @param fd The flashdisk data.
2118 * @retval int The ioctl return value.
2119 */
2120static int
2121rtems_fdisk_erase_disk (rtems_flashdisk* fd)
2122{
2123  uint32_t device;
2124  int      ret;
2125
2126#if RTEMS_FDISK_TRACE
2127  rtems_fdisk_info (fd, "erase-disk");
2128#endif
2129
2130  ret = rtems_fdisk_erase_flash (fd);
2131
2132  if (ret == 0)
2133  {
2134    for (device = 0; device < fd->device_count; device++)
2135    {
2136      if (!fd->devices[device].segments)
2137        return ENOMEM;
2138
2139      ret = rtems_fdisk_recover_block_mappings (fd);
2140      if (ret)
2141        break;
2142    }
2143  }
2144
2145  return ret;
2146}
2147
2148/**
2149 * Flash Disk Monitoring data is return in the monitoring data
2150 * structure.
2151 */
2152static int
2153rtems_fdisk_monitoring_data (rtems_flashdisk*          fd,
2154                             rtems_fdisk_monitor_data* data)
2155{
2156  uint32_t i;
2157  uint32_t j;
2158
2159  data->block_size     = fd->block_size;
2160  data->block_count    = fd->block_count;
2161  data->unavail_blocks = fd->unavail_blocks;
2162  data->device_count   = fd->device_count;
2163
2164  data->blocks_used = 0;
2165  for (i = 0; i < fd->block_count; i++)
2166    if (fd->blocks[i].segment)
2167      data->blocks_used++;
2168
2169  data->segs_available = rtems_fdisk_segment_count_queue (&fd->available);
2170  data->segs_used      = rtems_fdisk_segment_count_queue (&fd->used);
2171  data->segs_failed    = rtems_fdisk_segment_count_queue (&fd->failed);
2172
2173  data->segment_count = 0;
2174  data->page_count    = 0;
2175  data->pages_desc    = 0;
2176  data->pages_active  = 0;
2177  data->pages_used    = 0;
2178  data->pages_bad     = 0;
2179  data->seg_erases    = 0;
2180
2181  for (i = 0; i < fd->device_count; i++)
2182  {
2183    data->segment_count += fd->devices[i].segment_count;
2184
2185    for (j = 0; j < fd->devices[i].segment_count; j++)
2186    {
2187      rtems_fdisk_segment_ctl* sc = &fd->devices[i].segments[j];
2188
2189      data->page_count   += sc->pages;
2190      data->pages_desc   += sc->pages_desc;
2191      data->pages_active += sc->pages_active;
2192      data->pages_used   += sc->pages_used;
2193      data->pages_bad    += sc->pages_bad;
2194      data->seg_erases   += sc->erased;
2195    }
2196  }
2197
2198  data->info_level = fd->info_level;
2199  return 0;
2200}
2201
2202/**
2203 * Print to stdout the status of the driver. This is a debugging aid.
2204 */
2205static int
2206rtems_fdisk_print_status (rtems_flashdisk* fd)
2207{
2208#if RTEMS_FDISK_TRACE
2209  uint32_t current_info_level = fd->info_level;
2210  uint32_t total;
2211  uint32_t count;
2212  uint32_t device;
2213
2214  fd->info_level = 3;
2215
2216  rtems_fdisk_printf (fd,
2217                      "Flash Disk Driver Status : %d.%d", fd->major, fd->minor);
2218
2219  rtems_fdisk_printf (fd, "Block count\t%d", fd->block_count);
2220  rtems_fdisk_printf (fd, "Unavail blocks\t%d", fd->unavail_blocks);
2221  count = rtems_fdisk_segment_count_queue (&fd->available);
2222  total = count;
2223  rtems_fdisk_printf (fd, "Available queue\t%ld (%ld)",
2224                      count, rtems_fdisk_segment_queue_count (&fd->available));
2225  count = rtems_fdisk_segment_count_queue (&fd->used);
2226  total += count;
2227  rtems_fdisk_printf (fd, "Used queue\t%ld (%ld)",
2228                      count, rtems_fdisk_segment_queue_count (&fd->used));
2229  count = rtems_fdisk_segment_count_queue (&fd->erase);
2230  total += count;
2231  rtems_fdisk_printf (fd, "Erase queue\t%ld (%ld)",
2232                      count, rtems_fdisk_segment_queue_count (&fd->erase));
2233  count = rtems_fdisk_segment_count_queue (&fd->failed);
2234  total += count;
2235  rtems_fdisk_printf (fd, "Failed queue\t%ld (%ld)",
2236                      count, rtems_fdisk_segment_queue_count (&fd->failed));
2237
2238  count = 0;
2239  for (device = 0; device < fd->device_count; device++)
2240    count += fd->devices[device].segment_count;
2241
2242  rtems_fdisk_printf (fd, "Queue total\t%ld of %ld, %s", total, count,
2243                      total == count ? "ok" : "MISSING");
2244
2245  rtems_fdisk_printf (fd, "Device count\t%d", fd->device_count);
2246
2247  for (device = 0; device < fd->device_count; device++)
2248  {
2249    uint32_t block;
2250    uint32_t seg;
2251
2252    rtems_fdisk_printf (fd, " Device\t\t%ld", device);
2253    rtems_fdisk_printf (fd, "  Segment count\t%ld",
2254                        fd->devices[device].segment_count);
2255
2256    for (seg = 0; seg < fd->devices[device].segment_count; seg++)
2257    {
2258      rtems_fdisk_segment_ctl* sc = &fd->devices[device].segments[seg];
2259      uint32_t                 page;
2260      uint32_t                 erased = 0;
2261      uint32_t                 active = 0;
2262      uint32_t                 used = 0;
2263      bool                     is_active = false;
2264      char                     queues[5];
2265
2266      rtems_fdisk_queue_status (fd, sc, queues);
2267
2268      for (page = 0; page < sc->pages; page++)
2269      {
2270        if (rtems_fdisk_page_desc_erased (&sc->page_descriptors[page]))
2271          erased++;
2272        else if (rtems_fdisk_page_desc_flags_set (&sc->page_descriptors[page],
2273                                                  RTEMS_FDISK_PAGE_ACTIVE))
2274        {
2275          if (rtems_fdisk_page_desc_flags_set (&sc->page_descriptors[page],
2276                                               RTEMS_FDISK_PAGE_USED))
2277            used++;
2278          else
2279          {
2280            active++;
2281            is_active = true;
2282          }
2283        }
2284
2285        for (block = 0; block < fd->block_count; block++)
2286        {
2287          if ((fd->blocks[block].segment == sc) &&
2288              (fd->blocks[block].page == page) && !is_active)
2289            rtems_fdisk_printf (fd,
2290                                "    %ld\t not active when mapped by block %ld",
2291                                page, block);
2292        }
2293      }
2294
2295      count = 0;
2296      for (block = 0; block < fd->block_count; block++)
2297      {
2298        if (fd->blocks[block].segment == sc)
2299          count++;
2300      }
2301
2302      rtems_fdisk_printf (fd, "  %3ld %s p:%3ld a:%3ld/%3ld" \
2303                          " u:%3ld/%3ld e:%3ld/%3ld br:%ld",
2304                          seg, queues,
2305                          sc->pages, sc->pages_active, active,
2306                          sc->pages_used, used, erased,
2307                          sc->pages - (sc->pages_active +
2308                                       sc->pages_used + sc->pages_bad),
2309                          count);
2310    }
2311  }
2312
2313  {
2314    rtems_fdisk_segment_ctl* sc = fd->used.head;
2315    int count = 0;
2316    rtems_fdisk_printf (fd, "Used List:");
2317    while (sc)
2318    {
2319      rtems_fdisk_printf (fd, "  %3d %02d:%03d u:%3ld",
2320                          count, sc->device, sc->segment, sc->pages_used);
2321      sc = sc->next;
2322      count++;
2323    }
2324  }
2325  fd->info_level = current_info_level;
2326
2327  return 0;
2328#else
2329  return ENOSYS;
2330#endif
2331}
2332
2333/**
2334 * Flash disk IOCTL handler.
2335 *
2336 * @param dd Disk device.
2337 * @param req IOCTL request code.
2338 * @param argp IOCTL argument.
2339 * @retval The IOCTL return value
2340 */
2341static int
2342rtems_fdisk_ioctl (rtems_disk_device *dd, uint32_t req, void* argp)
2343{
2344  dev_t                     dev = rtems_disk_get_device_identifier (dd);
2345  rtems_device_minor_number minor = rtems_filesystem_dev_minor_t (dev);
2346  rtems_blkdev_request*     r = argp;
2347  rtems_status_code         sc;
2348
2349  errno = 0;
2350
2351  sc = rtems_semaphore_obtain (rtems_flashdisks[minor].lock, RTEMS_WAIT, 0);
2352  if (sc != RTEMS_SUCCESSFUL)
2353    errno = EIO;
2354  else
2355  {
2356    errno = 0;
2357    switch (req)
2358    {
2359      case RTEMS_BLKIO_REQUEST:
2360        if ((minor >= rtems_flashdisk_count) ||
2361            (rtems_flashdisks[minor].device_count == 0))
2362        {
2363          errno = ENODEV;
2364        }
2365        else
2366        {
2367          switch (r->req)
2368          {
2369            case RTEMS_BLKDEV_REQ_READ:
2370              errno = rtems_fdisk_read (&rtems_flashdisks[minor], r);
2371              break;
2372
2373            case RTEMS_BLKDEV_REQ_WRITE:
2374              errno = rtems_fdisk_write (&rtems_flashdisks[minor], r);
2375              break;
2376
2377            default:
2378              errno = EINVAL;
2379              break;
2380          }
2381        }
2382        break;
2383
2384      case RTEMS_FDISK_IOCTL_ERASE_DISK:
2385        errno = rtems_fdisk_erase_disk (&rtems_flashdisks[minor]);
2386        break;
2387
2388      case RTEMS_FDISK_IOCTL_COMPACT:
2389        errno = rtems_fdisk_compact (&rtems_flashdisks[minor]);
2390        break;
2391
2392      case RTEMS_FDISK_IOCTL_ERASE_USED:
2393        errno = rtems_fdisk_erase_used (&rtems_flashdisks[minor]);
2394        break;
2395
2396      case RTEMS_FDISK_IOCTL_MONITORING:
2397        errno = rtems_fdisk_monitoring_data (&rtems_flashdisks[minor],
2398                                             (rtems_fdisk_monitor_data*) argp);
2399        break;
2400
2401      case RTEMS_FDISK_IOCTL_INFO_LEVEL:
2402        rtems_flashdisks[minor].info_level = (uint32_t) argp;
2403        break;
2404
2405      case RTEMS_FDISK_IOCTL_PRINT_STATUS:
2406        errno = rtems_fdisk_print_status (&rtems_flashdisks[minor]);
2407        break;
2408
2409      default:
2410        rtems_blkdev_ioctl (dd, req, argp);
2411        break;
2412    }
2413
2414    sc = rtems_semaphore_release (rtems_flashdisks[minor].lock);
2415    if (sc != RTEMS_SUCCESSFUL)
2416      errno = EIO;
2417  }
2418
2419  return errno == 0 ? 0 : -1;
2420}
2421
2422/**
2423 * Flash disk device driver initialization.
2424 *
2425 * @todo Memory clean up on error is really badly handled.
2426 *
2427 * @param major Flash disk major device number.
2428 * @param minor Minor device number, not applicable.
2429 * @param arg Initialization argument, not applicable.
2430 */
2431rtems_device_driver
2432rtems_fdisk_initialize (rtems_device_major_number major,
2433                        rtems_device_minor_number minor,
2434                        void*                     arg __attribute__((unused)))
2435{
2436  const rtems_flashdisk_config* c = rtems_flashdisk_configuration;
2437  rtems_flashdisk*              fd;
2438  rtems_status_code             sc;
2439
2440  sc = rtems_disk_io_initialize ();
2441  if (sc != RTEMS_SUCCESSFUL)
2442    return sc;
2443
2444  sc = rtems_fdisk_crc16_gen_factors (0x8408);
2445  if (sc != RTEMS_SUCCESSFUL)
2446      return sc;
2447
2448  rtems_flashdisks = calloc (rtems_flashdisk_configuration_size,
2449                             sizeof (rtems_flashdisk));
2450
2451  if (!rtems_flashdisks)
2452    return RTEMS_NO_MEMORY;
2453
2454  for (minor = 0; minor < rtems_flashdisk_configuration_size; minor++, c++)
2455  {
2456    char     name[] = RTEMS_FLASHDISK_DEVICE_BASE_NAME "a";
2457    dev_t    dev = rtems_filesystem_make_dev_t (major, minor);
2458    uint32_t device;
2459    uint32_t blocks = 0;
2460    int      ret;
2461
2462    fd = &rtems_flashdisks[minor];
2463
2464    name [sizeof(RTEMS_FLASHDISK_DEVICE_BASE_NAME)] += minor;
2465
2466    fd->major              = major;
2467    fd->minor              = minor;
2468    fd->flags              = c->flags;
2469    fd->compact_segs       = c->compact_segs;
2470    fd->avail_compact_segs = c->avail_compact_segs;
2471    fd->block_size         = c->block_size;
2472    fd->unavail_blocks     = c->unavail_blocks;
2473    fd->info_level         = c->info_level;
2474
2475    for (device = 0; device < c->device_count; device++)
2476      blocks += rtems_fdisk_blocks_in_device (&c->devices[device],
2477                                              c->block_size);
2478
2479    sc = rtems_disk_create_phys(dev, c->block_size,
2480                                blocks - fd->unavail_blocks,
2481                                rtems_fdisk_ioctl, NULL, name);
2482    if (sc != RTEMS_SUCCESSFUL)
2483    {
2484      rtems_fdisk_error ("disk create phy failed");
2485      return sc;
2486    }
2487
2488    sc = rtems_semaphore_create (rtems_build_name ('F', 'D', 'S', 'K'), 1,
2489                                 RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE |
2490                                 RTEMS_INHERIT_PRIORITY, 0, &fd->lock);
2491    if (sc != RTEMS_SUCCESSFUL)
2492    {
2493      rtems_fdisk_error ("disk lock create failed");
2494      return sc;
2495    }
2496
2497    /*
2498     * One copy buffer of a page size.
2499     */
2500    fd->copy_buffer = malloc (c->block_size);
2501    if (!fd->copy_buffer)
2502      return RTEMS_NO_MEMORY;
2503
2504    fd->blocks = calloc (blocks, sizeof (rtems_fdisk_block_ctl));
2505    if (!fd->blocks)
2506      return RTEMS_NO_MEMORY;
2507
2508    fd->block_count = blocks;
2509
2510    fd->devices = calloc (c->device_count, sizeof (rtems_fdisk_device_ctl));
2511    if (!fd->devices)
2512      return RTEMS_NO_MEMORY;
2513
2514    for (device = 0; device < c->device_count; device++)
2515    {
2516      rtems_fdisk_segment_ctl* sc;
2517      uint32_t                 segment_count;
2518      uint32_t                 segment;
2519
2520      segment_count = rtems_fdisk_count_segments (&c->devices[device]);
2521
2522      fd->devices[device].segments = calloc (segment_count,
2523                                             sizeof (rtems_fdisk_segment_ctl));
2524      if (!fd->devices[device].segments)
2525        return RTEMS_NO_MEMORY;
2526
2527      sc = fd->devices[device].segments;
2528
2529      for (segment = 0; segment < c->devices[device].segment_count; segment++)
2530      {
2531        const rtems_fdisk_segment_desc* sd;
2532        uint32_t                        seg_segment;
2533
2534        sd = &c->devices[device].segments[segment];
2535
2536        for (seg_segment = 0; seg_segment < sd->count; seg_segment++, sc++)
2537        {
2538          sc->descriptor = sd;
2539          sc->device     = device;
2540          sc->segment    = seg_segment;
2541          sc->erased     = 0;
2542        }
2543      }
2544
2545      fd->devices[device].segment_count = segment_count;
2546      fd->devices[device].descriptor    = &c->devices[device];
2547    }
2548
2549    fd->device_count = c->device_count;
2550
2551    ret = rtems_fdisk_recover_block_mappings (fd);
2552    if (ret)
2553      rtems_fdisk_error ("recovery of disk failed: %s (%d)",
2554                         strerror (ret), ret);
2555
2556    ret = rtems_fdisk_compact (fd);
2557    if (ret)
2558      rtems_fdisk_error ("compacting of disk failed: %s (%d)",
2559                         strerror (ret), ret);
2560  }
2561
2562  rtems_flashdisk_count = rtems_flashdisk_configuration_size;
2563
2564  return RTEMS_SUCCESSFUL;
2565}
Note: See TracBrowser for help on using the repository browser.