source: rtems/cpukit/libblock/src/flashdisk.c @ ac50df2

4.115
Last change on this file since ac50df2 was ac50df2, checked in by Sebastian Huber <sebastian.huber@…>, on 03/28/12 at 09:30:59

libblock: PR2040: Add starvation threshold

Do not use the unavailable block count as the erased blocks starvation
threshold. Use instead the block count of the largest segment. This
improves the starvation resolution gain of available blocks.

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