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

4.104.114.95
Last change on this file since 1ff9922 was 1ff9922, checked in by Chris Johns <chrisj@…>, on 12/22/07 at 08:27:18

2007-12-22 Chris Johns <chrisj@…>

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