summaryrefslogtreecommitdiffstats
path: root/cpukit/libblock/include/rtems/flashdisk.h
blob: 28e21bbb5e14a3fb6b300ad3848e60040de3e60e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
/**
 * @file
 *
 * @ingroup RTEMSFDisk
 *
 * This file defines the interface to a flash disk block device.
 */

/*
 * Copyright (C) 2007 Chris Johns
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.com/license/LICENSE.
 *
 * $Id$
 */

#if !defined (_RTEMS_FLASHDISK_H_)
#define _RTEMS_FLASHDISK_H_

#include <stdint.h>
#include <sys/ioctl.h>

#include <rtems.h>

/**
 * @defgroup RTEMSFDisk Flash Disk Device
 *
 * @ingroup rtems_blkdev
 *
 * Flash disk driver for RTEMS provides support for block based
 * file systems on flash devices. The driver is not a flash file
 * system nor does it try to compete with flash file systems. It
 * currently does not journal how-ever block sequence numbering
 * could be added to allow recovery of a past positions if
 * a power down occurred while being updated.
 *
 * This flash driver provides block device support for most flash
 * devices. The driver has been tested on NOR type devices such
 * as the AMLV160 or M28W160. Support for NAND type devices may
 * require driver changes to allow speedy recover of the block
 * mapping data and to also handle the current use of word programming.
 * Currently the page descriptors are stored in the first few pages
 * of each segment.
 *
 * The driver supports devices, segments and pages. You provide
 * to the driver the device descriptions as a table of device
 * descriptors. Each device descriptor contain a table of
 * segment descriptions or segment descriptors. The driver uses
 * this information to manage the devices.
 *
 * A device is made up of segments. These are also called
 * sectors or blocks. It is the smallest erasable part of a device.
 * A device can have differing size segments at different
 * offsets in the device. The segment descriptors support repeating
 * segments that are continuous in the device. The driver breaks the
 * segments up into pages. The first pages of a segment contain
 * the page descriptors. A page descriptor hold the page flags,
 * a CRC for the page of data and the block number the page
 * holds. The block can appear in any order in the devices. A
 * page is active if it hold a current block of data. If the
 * used bit is set the page is counted as used. A page moves
 * from erased to active to used then back to erased. If a block
 * is written that is already in a page, the block is written to
 * a new page the old page is flagged as used.
 *
 * At initialization time each segment's page descriptors are
 * read into memory and scanned to determine the active pages,
 * the used pages and the bad pages. If a segment has any erased
 * pages it is queue on the available queue. If the segment has
 * no erased pages it is queue on the used queue.
 *
 * The available queue is sorted from the least number available
 * to the most number of available pages. A segment that has just
 * been erased will placed at the end of the queue. A segment that
 * has only a few available pages will be used sooner and once
 * there are no available pages it is queued on the used queue.
 * The used queue hold segments that have no available pages and
 * is sorted from the least number of active pages to the most
 * number of active pages.
 *
 * The driver is required to compact segments. Compacting takes
 * the segment with the most number of available pages from the
 * available queue then takes segments with the least number of
 * active pages from the used queue until it has enough pages
 * to fill the empty segment. As the active pages are moved
 * they flagged as used and once the segment has only used pages
 * it is erased.
 *
 * A flash block driver like this never knows if a page is not
 * being used by the file-system. A typical file system is not
 * design with the idea of erasing a block on a disk once it is
 * not being used. The file-system will normally use a flag
 * or a location as a marker to say that part of the disk is
 * no longer in use. This means a number of blocks could be
 * held in active pages but are no in use by the file system.
 * The file system may also read blocks that have never been
 * written to disk. This complicates the driver and may make
 * the wear, usage and erase patterns harsher than a flash
 * file system. The driver may also suffer from problems if
 * power is lost.
 *
 * There are some flash disk specific IO control request types.
 * To use open the device and issue the ioctl() call.
 *
 * @code
 *  int fd = open ("/dev/flashdisk0", O_WRONLY, 0);
 *  if (fd < 0)
 *  {
 *    printf ("driver open failed: %s\n", strerror (errno));
 *    exit (1);
 *  }
 *  if (ioctl (fd, RTEMS_FDISK_IOCTL_ERASE_DISK) < 0)
 *  {
 *    printf ("driver erase failed: %s\n", strerror (errno));
 *    exit (1);
 *  }
 *  close (fd);
 * @endcode
 *
 * @{
 */

/**
 * @brief The base name of the flash disks.
 */
#define RTEMS_FLASHDISK_DEVICE_BASE_NAME "/dev/fdd"

#define RTEMS_FDISK_IOCTL_ERASE_DISK   _IO('B', 128)
#define RTEMS_FDISK_IOCTL_COMPACT      _IO('B', 129)
#define RTEMS_FDISK_IOCTL_ERASE_USED   _IO('B', 130)
#define RTEMS_FDISK_IOCTL_MONITORING   _IO('B', 131)
#define RTEMS_FDISK_IOCTL_INFO_LEVEL   _IO('B', 132)
#define RTEMS_FDISK_IOCTL_PRINT_STATUS _IO('B', 133)

/**
 * @brief Flash Disk Monitoring Data allows a user to obtain
 * the current status of the disk.
 */
typedef struct rtems_fdisk_monitor_data
{
  uint32_t block_size;
  uint32_t block_count;
  uint32_t unavail_blocks;
  uint32_t device_count;
  uint32_t segment_count;
  uint32_t page_count;
  uint32_t blocks_used;
  uint32_t segs_available;
  uint32_t segs_used;
  uint32_t segs_failed;
  uint32_t seg_erases;
  uint32_t pages_desc;
  uint32_t pages_active;
  uint32_t pages_used;
  uint32_t pages_bad;
  uint32_t info_level;
} rtems_fdisk_monitor_data;

/**
 * @brief Flash Segment Descriptor holds, number of continuous segments in the
 * device of this type, the base segment number in the device, the address
 * offset of the base segment in the device, and the size of segment.
 *
 * Typically this structure is part of a table of segments in the
 * device which is referenced in the flash disk configuration table.
 * The reference is kept in the driver and used all the time to
 * manage the flash device, therefore it must always exist.
 */
typedef struct rtems_fdisk_segment_desc
{
  uint16_t count;    /**< Number of segments of this type in a row. */
  uint16_t segment;  /**< The base segment number. */
  uint32_t offset;   /**< Address offset of base segment in device. */
  uint32_t size;     /**< Size of the segment in bytes. */
} rtems_fdisk_segment_desc;

/**
 * @brief Return the number of kilo-bytes.
 */
#define RTEMS_FDISK_KBYTES(_k) (UINT32_C(1024) * (_k))

/**
 * Forward declaration of the device descriptor.
 */
struct rtems_fdisk_device_desc;

/**
 * @brief Flash Low Level driver handlers.
 *
 * Typically this structure is part of a table of handlers in the
 * device which is referenced in the flash disk configuration table.
 * The reference is kept in the driver and used all the time to
 * manage the flash device, therefore it must always exist.
 */
typedef struct rtems_fdisk_driver_handlers
{
  /**
   * Read data from the device into the buffer. Return an errno
   * error number if the device cannot be read. A segment descriptor
   * can describe more than one segment in a device if the device has
   * repeating segments. The segment number is the device segment to
   * access and the segment descriptor must reference the segment
   * being requested. For example the segment number must resided in
   * the range [base, base + count).
   *
   * @param sd The segment descriptor.
   * @param device The device to read data from.
   * @param segment The segment within the device to read.
   * @param offset The offset in the segment to read.
   * @param buffer The buffer to read the data into.
   * @param size The amount of data to read.
   * @retval 0 No error.
   * @retval EIO The read did not complete.
   */
  int (*read) (const rtems_fdisk_segment_desc* sd,
               uint32_t                        device,
               uint32_t                        segment,
               uint32_t                        offset,
               void*                           buffer,
               uint32_t                        size);

  /**
   * Write data from the buffer to the device. Return an errno
   * error number if the device cannot be written to. A segment
   * descriptor can describe more than segment in a device if the
   * device has repeating segments. The segment number is the device
   * segment to access and the segment descriptor must reference
   * the segment being requested. For example the segment number must
   * resided in the range [base, base + count).
   *
   * @param sd The segment descriptor.
   * @param device The device to write data from.
   * @param segment The segment within the device to write to.
   * @param offset The offset in the segment to write.
   * @param buffer The buffer to write the data from.
   * @param size The amount of data to write.
   * @retval 0 No error.
   * @retval EIO The write did not complete or verify.
   */
  int (*write) (const rtems_fdisk_segment_desc* sd,
                uint32_t                        device,
                uint32_t                        segment,
                uint32_t                        offset,
                const void*                     buffer,
                uint32_t                        size);

  /**
   * Blank a segment in the device. Return an errno error number
   * if the device cannot be read or is not blank. A segment descriptor
   * can describe more than segment in a device if the device has
   * repeating segments. The segment number is the device segment to
   * access and the segment descriptor must reference the segment
   * being requested. For example the segment number must resided in
   * the range [base, base + count).
   *
   * @param sd The segment descriptor.
   * @param device The device to read data from.
   * @param segment The segment within the device to read.
   * @param offset The offset in the segment to checl.
   * @param size The amount of data to check.
   * @retval 0 No error.
   * @retval EIO The segment is not blank.
   */
  int (*blank) (const rtems_fdisk_segment_desc* sd,
                uint32_t                        device,
                uint32_t                        segment,
                uint32_t                        offset,
                uint32_t                        size);

  /**
   * Verify data in the buffer to the data in the device. Return an
   * errno error number if the device cannot be read. A segment
   * descriptor can describe more than segment in a device if the
   * device has repeating segments. The segment number is the
   * segment to access and the segment descriptor must reference
   * the device segment being requested. For example the segment number
   * must resided in the range [base, base + count).
   *
   * @param sd The segment descriptor.
   * @param device The device to verify data in.
   * @param segment The segment within the device to verify.
   * @param offset The offset in the segment to verify.
   * @param buffer The buffer to verify the data in the device with.
   * @param size The amount of data to verify.
   * @retval 0 No error.
   * @retval EIO The data did not verify.
   */
  int (*verify) (const rtems_fdisk_segment_desc* sd,
                 uint32_t                        device,
                 uint32_t                        segment,
                 uint32_t                        offset,
                 const void*                     buffer,
                 uint32_t                        size);

  /**
   * Erase the segment. Return an errno error number if the
   * segment cannot be erased. A segment descriptor can describe
   * more than segment in a device if the device has repeating
   * segments. The segment number is the device segment to access and
   * the segment descriptor must reference the segment being requested.
   *
   * @param sd The segment descriptor.
   * @param device The device to erase the segment of.
   * @param segment The segment within the device to erase.
   * @retval 0 No error.
   * @retval EIO The segment was not erased.
   */
  int (*erase) (const rtems_fdisk_segment_desc* sd,
                uint32_t                        device,
                uint32_t                        segment);

  /**
   * Erase the device. Return an errno error number if the
   * segment cannot be erased. A segment descriptor can describe
   * more than segment in a device if the device has repeating
   * segments. The segment number is the segment to access and
   * the segment descriptor must reference the segment being requested.
   *
   * @param sd The segment descriptor.
   * @param device The device to erase.
   * @retval 0 No error.
   * @retval EIO The device was not erased.
   */
  int (*erase_device) (const struct rtems_fdisk_device_desc* dd,
                       uint32_t                              device);

} rtems_fdisk_driver_handlers;

/**
 * @brief Flash Device Descriptor holds the segments in a device.
 *
 * The placing of the segments in a device decriptor allows the low level
 * driver to share the segment descriptors for a number of devices.
 *
 * Typically this structure is part of a table of segments in the
 * device which is referenced in the flash disk configuration table.
 * The reference is kept in the driver and used all the time to
 * manage the flash device, therefore it must always exist.
 */
typedef struct rtems_fdisk_device_desc
{
  uint32_t                           segment_count; /**< Number of segments. */
  const rtems_fdisk_segment_desc*    segments;      /**< Array of segments. */
  const rtems_fdisk_driver_handlers* flash_ops;     /**< Device handlers. */
} rtems_fdisk_device_desc;

/**
 * @brief RTEMS Flash Disk configuration table used to initialise the
 * driver.
 *
 * The unavailable blocks count is the number of blocks less than the
 * available number of blocks the file system is given. This means there
 * will always be that number of blocks available when the file system
 * thinks the disk is full. The compaction code needs blocks to compact
 * with so you will never be able to have all the blocks allocated to the
 * file system and be able to full the disk.
 *
 * The compacting segment count is the number of segments that are
 * moved into a new segment. A high number will mean more segments with
 * low active page counts and high used page counts will be moved into
 * avaliable pages how-ever this extends the compaction time due to
 * time it takes the erase the pages. There is no pont making this number
 * greater than the maximum number of pages in a segment.
 *
 * The available compacting segment count is the level when compaction occurs
 * when writing. If you set this to 0 then compaction will fail because
 * there will be no segments to compact into.
 *
 * The info level can be 0 for off with error, and abort messages allowed.
 * Level 1 is warning messages, level 1 is informational messages, and level 3
 * is debugging type prints. The info level can be turned off with a compile
 * time directive on the command line to the compiler of:
 *
 *     -DRTEMS_FDISK_TRACE=0
 */
typedef struct rtems_flashdisk_config
{
  uint32_t                       block_size;     /**< The block size. */
  uint32_t                       device_count;   /**< The number of devices. */
  const rtems_fdisk_device_desc* devices;        /**< The device descriptions. */
  uint32_t                       flags;          /**< Set of flags to control
                                                      driver. */
  /**
   * Number of blocks not available to the file system.  This number must be
   * greater than or equal to the number of blocks in the largest segment to
   * avoid starvation of erased blocks.
   */
  uint32_t                       unavail_blocks;

  uint32_t                       compact_segs;   /**< Max number of segs to
                                                      compact in one pass. */
  /**
   * The number of segments when compaction occurs when writing.  In case the
   * number of segments in the available queue is less than or equal to this
   * number the compaction process will be triggered.  The available queue
   * contains all segments with erased blocks.
   */
  uint32_t                       avail_compact_segs;
  uint32_t                       info_level;     /**< Default info level. */
} rtems_flashdisk_config;

/*
 * Driver flags.
 */

/**
 * Leave the erasing of used segment to the background handler.
 */
#define RTEMS_FDISK_BACKGROUND_ERASE (1 << 0)

/**
 * Leave the compacting of of used segment to the background handler.
 */
#define RTEMS_FDISK_BACKGROUND_COMPACT (1 << 1)

/**
 * Check the pages during initialisation to see which pages are
 * valid and which are not. This could slow down initialising the
 * disk driver.
 */
#define RTEMS_FDISK_CHECK_PAGES (1 << 2)

/**
 * Blank check the flash device before writing to them. This is needed if
 * you think you have a driver or device problem.
 */
#define RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE (1 << 3)

/**
 * Flash disk device driver initialization. Place in a table as the
 * initialisation entry and remainder of the entries are the
 * RTEMS block device generic handlers.
 *
 * @param major Flash disk major device number.
 * @param minor Minor device number, not applicable.
 * @param arg Initialization argument, not applicable.
 * @return The rtems_device_driver is actually just
 *         rtems_status_code.
 */
rtems_device_driver
rtems_fdisk_initialize (rtems_device_major_number major,
                        rtems_device_minor_number minor,
                        void*                     arg);

/**
 * @brief External reference to the configuration. Please supply.
 * Support is present in confdefs.h for providing this variable.
 */
extern const rtems_flashdisk_config rtems_flashdisk_configuration[];

/**
 * @brief External reference to the number of configurations. Please supply.
 * Support is present in confdefs.h for providing this variable.
 */
extern uint32_t rtems_flashdisk_configuration_size;

/** @} */

#endif