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

5
Last change on this file since 1dec54f9 was 0fe7133, checked in by Sebastian Huber <sebastian.huber@…>, on 07/31/18 at 14:01:17

flashdisk: Use rtems_blkdev_create()

Update #3358.

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