From 5a2b5b22c85a635b14a68242f4f1f106421e28d4 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Thu, 1 May 2008 04:15:36 +0000 Subject: 2008-05-01 Chris Johns * libblock/include/rtems/nvdisk-sram.h, libblock/include/rtems/nvdisk.h, libblock/src/nvdisk-sram.c, libblock/src/nvdisk.c: New. A Non-volatile memory disk drive. * Makefile.am, preinstall.am, libblock/Makefile.am: Updated for the NV disk driver. --- cpukit/ChangeLog | 9 + cpukit/Makefile.am | 1 + cpukit/libblock/Makefile.am | 6 +- cpukit/libblock/include/rtems/nvdisk-sram.h | 25 + cpukit/libblock/include/rtems/nvdisk.h | 209 +++++++ cpukit/libblock/src/nvdisk-sram.c | 64 ++ cpukit/libblock/src/nvdisk.c | 884 ++++++++++++++++++++++++++++ cpukit/preinstall.am | 8 + 8 files changed, 1204 insertions(+), 2 deletions(-) create mode 100644 cpukit/libblock/include/rtems/nvdisk-sram.h create mode 100644 cpukit/libblock/include/rtems/nvdisk.h create mode 100644 cpukit/libblock/src/nvdisk-sram.c create mode 100644 cpukit/libblock/src/nvdisk.c (limited to 'cpukit') diff --git a/cpukit/ChangeLog b/cpukit/ChangeLog index 527fd55aaf..3bcdcbd41d 100644 --- a/cpukit/ChangeLog +++ b/cpukit/ChangeLog @@ -1,3 +1,12 @@ +2008-05-01 Chris Johns + + * libblock/include/rtems/nvdisk-sram.h, + libblock/include/rtems/nvdisk.h, + libblock/src/nvdisk-sram.c, + libblock/src/nvdisk.c: New. A Non-volatile memory disk drive. + * Makefile.am, preinstall.am, libblock/Makefile.am: Updated for + the NV disk driver. + 2008-05-01 Maarten Van Es * libnetworking/rtems/rtems_dhcp.c: Removed panic()s. Added interface for rtems_dhcp_failsafe. diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am index 7f9b55270b..ad230e6927 100644 --- a/cpukit/Makefile.am +++ b/cpukit/Makefile.am @@ -85,6 +85,7 @@ if !UNIX include_rtems_HEADERS += libblock/include/rtems/bdbuf.h \ libblock/include/rtems/blkdev.h libblock/include/rtems/diskdevs.h \ libblock/include/rtems/flashdisk.h libblock/include/rtems/ramdisk.h \ + libblock/include/rtems/nvdisk.h libblock/include/rtems/nvdisk-sram.h \ libblock/include/rtems/ide_part_table.h endif diff --git a/cpukit/libblock/Makefile.am b/cpukit/libblock/Makefile.am index 7124d48e16..f48a5d977c 100644 --- a/cpukit/libblock/Makefile.am +++ b/cpukit/libblock/Makefile.am @@ -8,10 +8,12 @@ include $(top_srcdir)/automake/compile.am if !UNIX noinst_LIBRARIES = libblock.a libblock_a_SOURCES = src/bdbuf.c src/blkdev.c src/diskdevs.c src/flashdisk.c \ - src/ramdisk.c src/ide_part_table.c src/show_bdbuf.c \ + src/ramdisk.c src/ide_part_table.c src/show_bdbuf.c src/nvdisk.c \ + src/nvdisk-sram.c \ include/rtems/bdbuf.h include/rtems/blkdev.h \ include/rtems/diskdevs.h include/rtems/flashdisk.h \ - include/rtems/ramdisk.h include/rtems/ide_part_table.h + include/rtems/ramdisk.h include/rtems/nvdisk.h include/rtems/nvdisk-sram.h \ + include/rtems/ide_part_table.h endif include $(top_srcdir)/automake/local.am diff --git a/cpukit/libblock/include/rtems/nvdisk-sram.h b/cpukit/libblock/include/rtems/nvdisk-sram.h new file mode 100644 index 0000000000..e963cdd9e8 --- /dev/null +++ b/cpukit/libblock/include/rtems/nvdisk-sram.h @@ -0,0 +1,25 @@ +/* + * $Id$ + * + * RTEMS Project (http://www.rtems.org/) + * + * Copyright 2007 Chris Johns (chrisj@rtems.org) + */ + +/** + * NV Disk Static RAM Device Driver. + * + * This driver maps an NV disk to static RAM. You can use this + */ + +#if !defined (_RTEMS_NVDISK_SRAM_H_) +#define _RTEMS_NVDISK_SRAM_H_ + +#include + +/** + * The handlers for the NV Disk SRAM driver. + */ +extern const rtems_nvdisk_driver_handlers rtems_nvdisk_sram_handlers; + +#endif diff --git a/cpukit/libblock/include/rtems/nvdisk.h b/cpukit/libblock/include/rtems/nvdisk.h new file mode 100644 index 0000000000..5e2a0dd6af --- /dev/null +++ b/cpukit/libblock/include/rtems/nvdisk.h @@ -0,0 +1,209 @@ +/* + * nvdisk.h -- Non-volatile disk block device implementation + * + * 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$ + */ + +/** + * The Non-volatile disk provides a simple directly mapped disk + * driver with checksums for each. It is designed to provied a + * disk that can survive a restart. Examples are EEPROM devices + * which have byte writeable locations, or a battery backed up + * RAM disk. + * + * The low level driver provides the physical access to the + * hardware. + */ +#if !defined (_RTEMS_NVDISK_H_) +#define _RTEMS_NVDISK_H_ + +#include +#include + +#include + +/** + * The base name of the nv disks. + */ +#define RTEMS_NVDISK_DEVICE_BASE_NAME "/dev/nvdisk" + +/** + * NV disk specific ioctl request types. To use open the + * device and issue the ioctl call. + * + * @code + * int fd = open ("/dev/nvdisk0", O_WRONLY, 0); + * if (fd < 0) + * { + * printf ("driver open failed: %s\n", strerror (errno)); + * exit (1); + * } + * if (ioctl (fd, RTEMS_NVDISK_IOCTL_ERASE_DISK) < 0) + * { + * printf ("driver erase failed: %s\n", strerror (errno)); + * exit (1); + * } + * close (fd); + * @endcode + */ +#define RTEMS_NVDISK_IOCTL_ERASE_DISK _IO('B', 128) +#define RTEMS_NVDISK_IOCTL_MONITORING _IO('B', 129) +#define RTEMS_NVDISK_IOCTL_INFO_LEVEL _IO('B', 130) +#define RTEMS_NVDISK_IOCTL_PRINT_STATUS _IO('B', 131) + +/** + * NV Disk Monitoring Data allows a user to obtain + * the current status of the disk. + */ +typedef struct rtems_nvdisk_monitor_data +{ + uint32_t block_size; + uint32_t block_count; + uint32_t page_count; + uint32_t pages_available; + uint32_t pages_used; + uint32_t info_level; +} rtems_nvdisk_monitor_data; + +/** + * Return the number of kilo-bytes. + */ +#define RTEMS_NVDISK_KBYTES(_k) ((_k) * 1024) + +/** + * NV Low Level driver handlers. + + * Typically this structure is part of a table of handlers in the + * device which is referenced in the nvdisk configuration table. + * The reference is kept in the driver and used all the time to + * manage the nv device, therefore it must always exist. + */ +typedef struct rtems_nvdisk_driver_handlers +{ + /** + * Read data from the device into the buffer. Return an errno + * error number if the data cannot be read. + * + * @param device The device to read data from. + * @param flags Device specific flags for the driver. + * @param base The base address of the device. + * @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) (uint32_t device, uint32_t flags, uint32_t base, + 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. + * + * @param device The device to write data to. + * @param flags Device specific flags for the driver. + * @param base The base address of the device. + * @param offset The offset in the device to write to. + * @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) (uint32_t device, uint32_t flags, uint32_t base, + uint32_t offset, const void* buffer, 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 or the data verified. + * + * @param device The device to verify the data with. + * @param flags Device specific flags for the driver. + * @param base The base address of the device. + * @param offset The offset in the device 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) (uint32_t device, uint32_t flags, uint32_t base, + uint32_t offset, const void* buffer, uint32_t size); + +} rtems_nvdisk_driver_handlers; + +/** + * NV Device Descriptor holds the description of a device that is + * part of the NV disk. + * + * Typically this structure is part of a table of the device which + * is referenced in the nvdisk configuration table. + * The reference is kept in the driver and used all the time to + * manage the nv device, therefore it must always exist. + */ +typedef struct rtems_nvdisk_device_desc +{ + uint32_t flags; /**< Private user flags. */ + uint32_t base; /**< Base address of the device. */ + uint32_t size; /**< Size of the device. */ + const rtems_nvdisk_driver_handlers* nv_ops; /**< Device handlers. */ +} rtems_nvdisk_device_desc; + +/** + * RTEMS Non-Volatile Disk configuration table used to initialise the + * driver. + */ +typedef struct rtems_nvdisk_config +{ + uint32_t block_size; /**< The block size. */ + uint32_t device_count; /**< The number of devices. */ + const rtems_nvdisk_device_desc* devices; /**< The device descriptions. */ + uint32_t flags; /**< Set of flags to control + driver. */ + uint32_t info_level; /**< Default info level. */ +} rtems_nvdisk_config; + +/* + * Driver flags. + */ + +/** + * 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_NVDISK_CHECK_PAGES (1 << 0) + +/** + * Non-volatile 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 NV 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_nvdisk_initialize (rtems_device_major_number major, + rtems_device_minor_number minor, + void* arg); + +/** + * External reference to the configuration. Please supply. + * Support is present in confdefs.h for providing this variable. + */ +extern const rtems_nvdisk_config rtems_nvdisk_configuration[]; + +/** + * External reference to the number of configurations. Please supply. + * Support is present in confdefs.h for providing this variable. + */ +extern uint32_t rtems_nvdisk_configuration_size; + +#endif diff --git a/cpukit/libblock/src/nvdisk-sram.c b/cpukit/libblock/src/nvdisk-sram.c new file mode 100644 index 0000000000..aa78689ec7 --- /dev/null +++ b/cpukit/libblock/src/nvdisk-sram.c @@ -0,0 +1,64 @@ +/* + * $Id$ + * + * RTEMS Project (http://www.rtems.org/) + * + * Copyright 2007 Chris Johns (chrisj@rtems.org) + */ +/** + * Provide SRAM support for the NV Disk. + */ + +#include +#include + +#include + +#include + +#ifndef NVDISK_SRAM_ERROR_TRACE +#define NVDISK_SRAM_ERROR_TRACE (0) +#endif + +static int +rtems_nvdisk_sram_read (uint32_t device, + uint32_t flags, + uint32_t base, + uint32_t offset, + void* buffer, + uint32_t size) +{ + memcpy (buffer, (char*) (base + offset), size); + return 0; +} + +static int +rtems_nvdisk_sram_write (uint32_t device, + uint32_t flags, + uint32_t base, + uint32_t offset, + const void* buffer, + uint32_t size) +{ + memcpy ((char*) (base + offset), buffer, size); + return 0; +} + +static int +rtems_nvdisk_sram_verify (uint32_t device, + uint32_t flags, + uint32_t base, + uint32_t offset, + const void* buffer, + uint32_t size) +{ + return memcmp ((char*) (base + offset), buffer, size) == 0 ? 0 : EIO; +} + + +const rtems_nvdisk_driver_handlers rtems_nvdisk_sram_handlers = +{ + read: rtems_nvdisk_sram_read, + write: rtems_nvdisk_sram_write, + verify: rtems_nvdisk_sram_verify +}; diff --git a/cpukit/libblock/src/nvdisk.c b/cpukit/libblock/src/nvdisk.c new file mode 100644 index 0000000000..0ee094bdc6 --- /dev/null +++ b/cpukit/libblock/src/nvdisk.c @@ -0,0 +1,884 @@ +/* + * nvdisk.c -- Non-volatile disk block device implementation + * + * 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 HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "rtems/blkdev.h" +#include "rtems/diskdevs.h" +#include "rtems/nvdisk.h" + +/** + * @note + * + * The use of pages can vary. The rtems_nvdisk_*_page set + * routines use an absolute page number relative to the segment + * while all other page numbera are relative to the number of + * page descriptor pages a segment has. You need to add the + * number of page descriptor pages (pages_desc) to the page number + * when call the rtems_nvdisk_*_page functions. + * + * You must always show the page number as relative in any trace + * or error message as device-page and if you have to + * the page number as absolute use device~page. This + * can be seen in the page copy routine. + * + * The code is like this to avoid needing the pass the pages_desc + * value around. It is only used in selected places and so the + * extra parameter was avoided. + */ + +/** + * Control tracing. It can be compiled out of the code for small + * footprint targets. Leave in by default. + */ +#if !defined (RTEMS_NVDISK_TRACE) +#define RTEMS_NVDISK_TRACE 1 +#endif + +/** + * Provide a basic boolean type. + */ +#define bool int +#define true (1) +#define false (0) + +/** + * NV Device Control holds the segment controls + */ +typedef struct rtems_nvdisk_device_ctl +{ + /** + * The device this segment resides on. + */ + uint32_t device; + + /** + * Total number of pages in the device. + */ + uint32_t pages; + + /** + * Number of pages used for page checksums. + */ + uint32_t pages_desc; + + /** + * First block number for this device. + */ + uint32_t block_base; + + /** + * Device descriptor. + */ + const rtems_nvdisk_device_desc* descriptor; +} rtems_nvdisk_device_ctl; + +/** + * The NV disk control structure for a single disk. There is one + * for each minor disk in the system. + */ +typedef struct rtems_mvdisk +{ + rtems_device_major_number major; /**< The driver's major number. */ + rtems_device_minor_number minor; /**< The driver's minor number. */ + uint32_t flags; /**< configuration flags. */ + uint32_t block_size; /**< The block size for this disk. */ + uint32_t block_count; /**< The number of available blocks. */ + rtems_nvdisk_device_ctl* devices; /**< The NV devices for this disk. */ + uint32_t device_count; /**< The number of NV devices. */ + uint32_t cs_pages; /**< The num of pages of checksums. */ + rtems_id lock; /**< Mutex for threading protection.*/ + uint32_t info_level; /**< The info trace level. */ +} rtems_nvdisk; + +/** + * The array of NV disks we support. + */ +static rtems_nvdisk* rtems_nvdisks; + +/** + * The number of NV disks we have. + */ +static uint32_t rtems_nvdisk_count; + +/** + * The CRC16 factor table. Created during initialisation. + */ +static uint16_t* rtems_nvdisk_crc16_factor; + +/** + * Calculate the CRC16 checksum. + * + * @param _b The byte to checksum. + * @param _c The current checksum. + */ +#define rtems_nvdisk_calc_crc16(_b, _c) \ + rtems_nvdisk_crc16_factor[((_b) ^ ((_c) & 0xff)) & 0xff] ^ (((_c) >> 8) & 0xff) + +/** + * Generate the CRC table. + * + * @param pattern The seed pattern for the table of factors. + * @relval RTEMS_SUCCESSFUL The table was generated. + * @retval RTEMS_NO_MEMORY The table could not be allocated from the heap. + */ +rtems_status_code +rtems_nvdisk_crc16_gen_factors (uint16_t pattern) +{ + uint32_t b; + + rtems_nvdisk_crc16_factor = malloc (sizeof (uint16_t) * 256); + if (!rtems_nvdisk_crc16_factor) + return RTEMS_NO_MEMORY; + + for (b = 0; b < 256; b++) + { + uint32_t i; + uint16_t v = b; + for (i = 8; i--;) + v = v & 1 ? (v >> 1) ^ pattern : v >> 1; + rtems_nvdisk_crc16_factor[b] = v & 0xffff; + } + return RTEMS_SUCCESSFUL; +} + +#if RTEMS_NVDISK_TRACE +/** + * Print a message to the nvdisk output and flush it. + * + * @param fd The flashdisk control structure. + * @param format The format string. See printf for details. + * @param ... The arguments for the format text. + * @return int The number of bytes written to the output. + */ +static int +rtems_nvdisk_printf (const rtems_nvdisk* nvd, const char *format, ...) +{ + int ret = 0; + if (nvd->info_level >= 3) + { + va_list args; + va_start (args, format); + fprintf (stdout, "nvdisk:"); + ret = vfprintf (stdout, format, args); + fprintf (stdout, "\n"); + fflush (stdout); + } + return ret; +} + +/** + * Print a info message to the nvdisk output and flush it. + * + * @param fd The flashdisk control structure. + * @param format The format string. See printf for details. + * @param ... The arguments for the format text. + * @return int The number of bytes written to the output. + */ +static int +rtems_nvdisk_info (const rtems_nvdisk* nvd, const char *format, ...) +{ + int ret = 0; + if (nvd->info_level >= 2) + { + va_list args; + va_start (args, format); + fprintf (stdout, "nvdisk:"); + ret = vfprintf (stdout, format, args); + fprintf (stdout, "\n"); + fflush (stdout); + } + return ret; +} + +/** + * Print a warning to the nvdisk output and flush it. + * + * @param fd The flashdisk control structure. + * @param format The format string. See printf for details. + * @param ... The arguments for the format text. + * @return int The number of bytes written to the output. + */ +static int +rtems_nvdisk_warning (const rtems_nvdisk* nvd, const char *format, ...) +{ + int ret = 0; + if (nvd->info_level >= 1) + { + va_list args; + va_start (args, format); + fprintf (stdout, "nvdisk:warning:"); + ret = vfprintf (stdout, format, args); + fprintf (stdout, "\n"); + fflush (stdout); + } + return ret; +} +#endif + +/** + * Print an error to the nvdisk output and flush it. + * + * @param format The format string. See printf for details. + * @param ... The arguments for the format text. + * @return int The number of bytes written to the output. + */ +static int +rtems_nvdisk_error (const char *format, ...) +{ + int ret; + va_list args; + va_start (args, format); + fprintf (stderr, "nvdisk:error:"); + ret = vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + fflush (stderr); + return ret; +} + +/** + * Print an abort message, flush it then abort the program. + * + * @param format The format string. See printf for details. + * @param ... The arguments for the format text. + */ +static void +rtems_nvdisk_abort (const char *format, ...) +{ + va_list args; + va_start (args, format); + fprintf (stderr, "nvdisk:abort:"); + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + fflush (stderr); + exit (1); +} + +/** + * Get the descriptor for a device. + */ +static const rtems_nvdisk_device_desc* +rtems_nvdisk_device_descriptor (const rtems_nvdisk* nvd, uint32_t device) +{ + return nvd->devices[device].descriptor; +} + +/** + * Read a block of data from a device. + */ +static int +rtems_nvdisk_device_read (const rtems_nvdisk* nvd, + uint32_t device, + uint32_t offset, + void* buffer, + uint32_t size) +{ + const rtems_nvdisk_device_desc* dd; + const rtems_nvdisk_driver_handlers* ops; + dd = rtems_nvdisk_device_descriptor (nvd, device); + ops = nvd->devices[device].descriptor->nv_ops; +#if RTEMS_NVDISK_TRACE + rtems_nvdisk_printf (nvd, " dev-read: %02d-%08x: s=%d", + device, offset, size); +#endif + return ops->read (device, dd->flags, dd->base, offset, buffer, size); +} + +/** + * Write a block of data to a device. + */ +static int +rtems_nvdisk_device_write (const rtems_nvdisk* nvd, + uint32_t device, + uint32_t offset, + const void* buffer, + uint32_t size) +{ + const rtems_nvdisk_device_desc* dd; + const rtems_nvdisk_driver_handlers* ops; + dd = rtems_nvdisk_device_descriptor (nvd, device); + ops = nvd->devices[device].descriptor->nv_ops; +#if RTEMS_NVDISK_TRACE + rtems_nvdisk_printf (nvd, " dev-write: %02d-%08x: s=%d", + device, offset, size); +#endif + return ops->write (device, dd->flags, dd->base, offset, buffer, size); +} + +/** + * Verify the data with the data in a segment. + */ +static int +rtems_nvdisk_device_verify (const rtems_nvdisk* nvd, + uint32_t device, + uint32_t offset, + const void* buffer, + uint32_t size) +{ + const rtems_nvdisk_device_desc* dd; + const rtems_nvdisk_driver_handlers* ops; + dd = rtems_nvdisk_device_descriptor (nvd, device); + ops = nvd->devices[device].descriptor->nv_ops; +#if RTEMS_NVDISK_TRACE + rtems_nvdisk_printf (nvd, " seg-verify: %02d-%08x: s=%d", + device, offset, size); +#endif + return ops->verify (device, dd->flags, dd->base, offset, buffer, size); +} + +/** + * Read a page of data from the device. + */ +static int +rtems_nvdisk_read_page (const rtems_nvdisk* nvd, + uint32_t device, + uint32_t page, + void* buffer) +{ + return rtems_nvdisk_device_read (nvd, device, + page * nvd->block_size, buffer, + nvd->block_size); +} + +/** + * Write a page of data to a device. + */ +static int +rtems_nvdisk_write_page (const rtems_nvdisk* nvd, + uint32_t device, + uint32_t page, + const void* buffer) +{ + return rtems_nvdisk_device_write (nvd, device, + page * nvd->block_size, + buffer, nvd->block_size); +} + +/** + * Verify a page of data with the data in the device. + */ +static int +rtems_nvdisk_verify_page (const rtems_nvdisk* nvd, + uint32_t device, + uint32_t page, + const void* buffer) +{ + return rtems_nvdisk_device_verify (nvd, device, + page * nvd->block_size, + buffer, nvd->block_size); +} + +/** + * Read the checksum from the device. + */ +static int +rtems_nvdisk_read_checksum (const rtems_nvdisk* nvd, + uint32_t device, + uint32_t page, + uint16_t* cs) +{ + return rtems_nvdisk_device_read (nvd, device, + page * sizeof (uint16_t), + cs, sizeof (uint16_t)); +} + +/** + * Write the checksum to the device. + */ +static int +rtems_nvdisk_write_checksum (const rtems_nvdisk* nvd, + uint32_t device, + uint32_t page, + const uint16_t cs) +{ + return rtems_nvdisk_device_write (nvd, device, + page * sizeof (uint16_t), + &cs, sizeof (uint16_t)); +} + +/** + * Calculate the pages in a device give the device descriptor and the + * page size. + * + * @param dd The device descriptor. + * @param page_size The page size in bytes. + */ +static uint32_t +rtems_nvdisk_pages_in_device (const rtems_nvdisk* nvd, + const rtems_nvdisk_device_desc* dd) +{ + return dd->size / nvd->block_size; +} + +/** + * Calculate the number of pages needed to hold the page descriptors. + * The calculation need to round up. + */ +static uint32_t +rtems_nvdisk_page_desc_pages (const rtems_nvdisk* nvd, + const rtems_nvdisk_device_desc* dd) +{ + uint32_t pages = rtems_nvdisk_pages_in_device (nvd, dd); + uint32_t bytes = pages * sizeof (uint16_t); + return ((bytes - 1) / nvd->block_size) + 1; +} + +/** + * Calculate the checksum of a page. + */ +static uint16_t +rtems_nvdisk_page_checksum (const uint8_t* buffer, uint32_t page_size) +{ + uint16_t cs = 0xffff; + uint32_t i; + + for (i = 0; i < page_size; i++, buffer++) + cs = rtems_nvdisk_calc_crc16 (cs, *buffer); + + return cs; +} + +/** + * Map a block to a device. + */ +static rtems_nvdisk_device_ctl* +rtems_nvdisk_get_device (rtems_nvdisk* nvd, uint32_t block) +{ + uint32_t device; + + if (block >= nvd->block_count) + { + rtems_nvdisk_error ("read-block: bad block: %d", block); + return NULL; + } + + for (device = 0; device < nvd->device_count; device++) + { + rtems_nvdisk_device_ctl* dc = &nvd->devices[device]; + if ((block >= dc->block_base) && + (block < (dc->block_base + dc->pages - dc->pages_desc))) + return dc; + } + + rtems_nvdisk_error ("map-block:%d: no device/page map found", block); + + return NULL; +} + +/** + * Get the page for a block in a device. + */ +static uint32_t +rtems_nvdisk_get_page (rtems_nvdisk_device_ctl* dc, + uint32_t block) +{ + return block - dc->block_base; +} + +/** + * Read a block. The block is checked to see if the page referenced + * is valid and the page has a valid crc. + * + * @param nvd The rtems_nvdisk control table. + * @param block The block number to read. + * @param buffer The buffer to write the data into. + * @return 0 No error. + * @return EIO Invalid block number or crc. + */ +static int +rtems_nvdisk_read_block (rtems_nvdisk* nvd, uint32_t block, uint8_t* buffer) +{ + rtems_nvdisk_device_ctl* dc; + uint32_t page; + uint16_t crc; + uint16_t cs; + int ret; + + dc = rtems_nvdisk_get_device (nvd, block); + + if (!dc) + return EIO; + + page = rtems_nvdisk_get_page (dc, block); + +#if RTEMS_NVDISK_TRACE + rtems_nvdisk_info (nvd, " read-block:%d=>%02d-%03d, cs:%04x", + block, dc->device, page, crc); +#endif + + ret = rtems_nvdisk_read_checksum (nvd, dc->device, page, &crc); + + if (ret) + return ret; + + if (crc == 0xffff) + { + rtems_nvdisk_warning (nvd, "read-block: crc not set: %d", block); + memset (buffer, 0, nvd->block_size); + return 0; + } + + ret = rtems_nvdisk_read_page (nvd, dc->device, page + dc->pages_desc, buffer); + + if (ret) + return ret; + + cs = rtems_nvdisk_page_checksum (buffer, nvd->block_size); + + if (cs != crc) + { + rtems_nvdisk_error ("read-block: crc failure: %d: buffer:%04x page:%04x", + block, cs, crc); + return EIO; + } + + return 0; +} + +/** + * Write a block. + * + * @param nvd The rtems_nvdisk control table. + * @param block The block number to read. + * @param block_size The size of the block. Must match what we have. + * @param buffer The buffer to write the data into. + * @return 0 No error. + * @return EIO Invalid block size, block number, segment pointer, crc, + * page flags. + */ +static int +rtems_nvdisk_write_block (rtems_nvdisk* nvd, + uint32_t block, + const unsigned char* buffer) +{ + rtems_nvdisk_device_ctl* dc; + uint32_t page; + uint16_t cs; + int ret; + + dc = rtems_nvdisk_get_device (nvd, block); + + if (!dc) + return EIO; + + page = rtems_nvdisk_get_page (dc, block); + + cs = rtems_nvdisk_page_checksum (buffer, nvd->block_size); + +#if RTEMS_NVDISK_TRACE + rtems_nvdisk_info (nvd, " write-block:%d=>%02d-%03d", block, dc->device, page); +#endif + + ret = rtems_nvdisk_write_page (nvd, dc->device, page + dc->pages_desc, buffer); + + if (ret) + return ret; + + return rtems_nvdisk_write_checksum (nvd, dc->device, page, cs); +} + +/** + * Disk READ request handler. This primitive copies data from the + * flash disk to the supplied buffer and invoke the callout function + * to inform upper layer that reading is completed. + * + * @param req Pointer to the READ block device request info. + * @retval int The ioctl return value. + */ +static int +rtems_nvdisk_read (rtems_nvdisk* nvd, blkdev_request* req) +{ + blkdev_sg_buffer* sg = req->bufs; + uint32_t block = req->start; + uint32_t b; + int32_t remains; + int ret = 0; + +#if RTEMS_NVDISK_TRACE + rtems_nvdisk_info (nvd, "read: blocks=%d", req->bufnum); +#endif + + remains = req->count * nvd->block_size; + + for (b = 0; b < req->bufnum; b++, block++, sg++) + { + uint32_t length = sg->length; + + if (remains <= 0) + rtems_nvdisk_error ("nvdisk-read: remains size <= 0"); + + if (sg->length != nvd->block_size) + { + rtems_nvdisk_error ("nvdisk-read: length is not the block size: "\ + "bd:%d nvd:%d", sg->length, nvd->block_size); + + if (length > nvd->block_size) + length = nvd->block_size; + } + + ret = rtems_nvdisk_read_block (nvd, block, sg->buffer); + + if (ret) + break; + + remains -= length; + } + + req->req_done (req->done_arg, + ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR, ret); + + return ret; +} + +/** + * Flash disk WRITE request handler. This primitive copies data from + * supplied buffer to NV disk and invoke the callout function to inform + * upper layer that writing is completed. + * + * @param req Pointers to the WRITE block device request info. + * @retval int The ioctl return value. + */ +static int +rtems_nvdisk_write (rtems_nvdisk* nvd, blkdev_request* req) +{ + blkdev_sg_buffer* sg = req->bufs; + uint32_t block = req->start; + uint32_t b; + int ret = 0; + +#if RTEMS_NVDISK_TRACE + rtems_nvdisk_info (nvd, "write: blocks=%d", req->bufnum); +#endif + + for (b = 0; b < req->bufnum; b++, block++, sg++) + { + if (sg->length != nvd->block_size) + { + rtems_nvdisk_error ("nvdisk-write: length is not the block size: " \ + "bd:%d nvd:%d", sg->length, nvd->block_size); + } + + ret = rtems_nvdisk_write_block (nvd, block, sg->buffer); + + if (ret) + break; + } + + req->req_done (req->done_arg, + ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR, ret); + + return 0; +} + +/** + * NV disk erase disk sets all the checksums for 0xffff. + * + * @param nvd The nvdisk data. + * @retval int The ioctl return value. + */ +static int +rtems_nvdisk_erase_disk (rtems_nvdisk* nvd) +{ + uint32_t device; + +#if RTEMS_NVDISK_TRACE + rtems_nvdisk_info (nvd, "erase-disk"); +#endif + + for (device = 0; device < nvd->device_count; device++) + { + rtems_nvdisk_device_ctl* dc = &nvd->devices[device]; + uint32_t page; + for (page = 0; page < (dc->pages - dc->pages_desc); page++) + { + int ret = rtems_nvdisk_write_checksum (nvd, dc->device, page, 0xffff); + if (ret) + return ret; + } + } + + return 0; +} + +/** + * MV disk IOCTL handler. + * + * @param dev Device number (major, minor number). + * @param req IOCTL request code. + * @param argp IOCTL argument. + * @retval The IOCTL return value + */ +static int +rtems_nvdisk_ioctl (dev_t dev, uint32_t req, void* argp) +{ + rtems_device_minor_number minor = rtems_filesystem_dev_minor_t (dev); + blkdev_request* r = argp; + rtems_status_code sc; + + errno = 0; + + sc = rtems_semaphore_obtain (rtems_nvdisks[minor].lock, RTEMS_WAIT, 0); + if (sc != RTEMS_SUCCESSFUL) + errno = EIO; + else + { + switch (req) + { + case BLKIO_REQUEST: + if ((minor >= rtems_nvdisk_count) || + (rtems_nvdisks[minor].device_count == 0)) + { + errno = ENODEV; + } + else + { + switch (r->req) + { + case BLKDEV_REQ_READ: + errno = rtems_nvdisk_read (&rtems_nvdisks[minor], r); + break; + + case BLKDEV_REQ_WRITE: + errno = rtems_nvdisk_write (&rtems_nvdisks[minor], r); + break; + + default: + errno = EBADRQC; + break; + } + } + break; + + case RTEMS_NVDISK_IOCTL_ERASE_DISK: + errno = rtems_nvdisk_erase_disk (&rtems_nvdisks[minor]); + break; + + case RTEMS_NVDISK_IOCTL_INFO_LEVEL: + rtems_nvdisks[minor].info_level = (uint32_t) argp; + break; + + default: + errno = EBADRQC; + break; + } + + sc = rtems_semaphore_release (rtems_nvdisks[minor].lock); + if (sc != RTEMS_SUCCESSFUL) + errno = EIO; + } + + return errno == 0 ? 0 : -1; +} + +/** + * NV disk device driver initialization. + * + * @todo Memory clean up on error is really badly handled. + * + * @param major NV disk major device number. + * @param minor Minor device number, not applicable. + * @param arg Initialization argument, not applicable. + */ +rtems_device_driver +rtems_nvdisk_initialize (rtems_device_major_number major, + rtems_device_minor_number minor, + void* arg) +{ + const rtems_nvdisk_config* c = rtems_nvdisk_configuration; + rtems_nvdisk* nvd; + rtems_status_code sc; + + sc = rtems_disk_io_initialize (); + if (sc != RTEMS_SUCCESSFUL) + return sc; + + sc = rtems_nvdisk_crc16_gen_factors (0x8408); + if (sc != RTEMS_SUCCESSFUL) + return sc; + + rtems_nvdisks = calloc (rtems_nvdisk_configuration_size, + sizeof (rtems_nvdisk)); + + if (!rtems_nvdisks) + return RTEMS_NO_MEMORY; + + for (minor = 0; minor < rtems_nvdisk_configuration_size; minor++, c++) + { + char name[sizeof (RTEMS_NVDISK_DEVICE_BASE_NAME) + 10]; + dev_t dev = rtems_filesystem_make_dev_t (major, minor); + uint32_t device; + uint32_t blocks = 0; + + nvd = &rtems_nvdisks[minor]; + + snprintf (name, sizeof (name), + RTEMS_NVDISK_DEVICE_BASE_NAME "%" PRIu32, minor); + + nvd->major = major; + nvd->minor = minor; + nvd->flags = c->flags; + nvd->block_size = c->block_size; + nvd->info_level = c->info_level; + + nvd->devices = calloc (c->device_count, sizeof (rtems_nvdisk_device_ctl)); + if (!nvd->devices) + return RTEMS_NO_MEMORY; + + for (device = 0; device < c->device_count; device++) + { + rtems_nvdisk_device_ctl* dc = &nvd->devices[device]; + + dc->device = device; + dc->pages = rtems_nvdisk_pages_in_device (nvd, &c->devices[device]); + dc->pages_desc = rtems_nvdisk_page_desc_pages (nvd, &c->devices[device]); + dc->block_base = blocks; + + blocks += dc->pages - dc->pages_desc; + + dc->descriptor = &c->devices[device]; + } + + nvd->block_count = blocks; + nvd->device_count = c->device_count; + + sc = rtems_disk_create_phys(dev, c->block_size, blocks, + rtems_nvdisk_ioctl, name); + if (sc != RTEMS_SUCCESSFUL) + { + rtems_nvdisk_error ("disk create phy failed"); + return sc; + } + + sc = rtems_semaphore_create (rtems_build_name ('N', 'V', 'D', 'K'), 1, + RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | + RTEMS_INHERIT_PRIORITY, 0, &nvd->lock); + if (sc != RTEMS_SUCCESSFUL) + { + rtems_nvdisk_error ("disk lock create failed"); + return sc; + } + } + + rtems_nvdisk_count = rtems_nvdisk_configuration_size; + + return RTEMS_SUCCESSFUL; +} diff --git a/cpukit/preinstall.am b/cpukit/preinstall.am index 41758c8c9e..7e13481738 100644 --- a/cpukit/preinstall.am +++ b/cpukit/preinstall.am @@ -164,6 +164,14 @@ $(PROJECT_INCLUDE)/rtems/ramdisk.h: libblock/include/rtems/ramdisk.h $(PROJECT_I $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/ramdisk.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/ramdisk.h +$(PROJECT_INCLUDE)/rtems/nvdisk.h: libblock/include/rtems/nvdisk.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/nvdisk.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/nvdisk.h + +$(PROJECT_INCLUDE)/rtems/nvdisk-sram.h: libblock/include/rtems/nvdisk-sram.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/nvdisk-sram.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/nvdisk-sram.h + $(PROJECT_INCLUDE)/rtems/ide_part_table.h: libblock/include/rtems/ide_part_table.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/ide_part_table.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/ide_part_table.h -- cgit v1.2.3