source: rtems/cpukit/libblock/src/flashdisk.c @ 3899a537

4.104.114.95
Last change on this file since 3899a537 was 3899a537, checked in by Chris Johns <chrisj@…>, on 07/29/08 at 02:21:15

2008-07-29 Chris Johns <chrisj@…>

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