Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:14

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup libblock
0007  *
0008  * @brief Non-Volatile Disk Block Device Implementation
0009  */
0010 
0011 /*
0012  * Copyright (C) 2007 Chris Johns
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 #ifdef HAVE_CONFIG_H
0037 #include "config.h"
0038 #endif
0039 
0040 #include <rtems.h>
0041 #include <rtems/libio.h>
0042 #include <errno.h>
0043 #include <stdlib.h>
0044 #include <stdio.h>
0045 #include <string.h>
0046 #include <inttypes.h>
0047 
0048 #include <rtems/blkdev.h>
0049 #include <rtems/nvdisk.h>
0050 #include <rtems/thread.h>
0051 
0052 /**
0053  * @note
0054  *
0055  * The use of pages can vary. The rtems_nvdisk_*_page set
0056  * routines use an absolute page number relative to the segment
0057  * while all other page numbera are relative to the number of
0058  * page descriptor pages a segment has. You need to add the
0059  * number of page descriptor pages (pages_desc) to the page number
0060  * when call the rtems_nvdisk_*_page functions.
0061  *
0062  * You must always show the page number as relative in any trace
0063  * or error message as device-page and if you have to
0064  * the page number as absolute use device~page. This
0065  * can be seen in the page copy routine.
0066  *
0067  * The code is like this to avoid needing the pass the pages_desc
0068  * value around. It is only used in selected places and so the
0069  * extra parameter was avoided.
0070  */
0071 
0072 /**
0073  * Control tracing. It can be compiled out of the code for small
0074  * footprint targets. Leave in by default.
0075  */
0076 #if !defined (RTEMS_NVDISK_TRACE)
0077 #define RTEMS_NVDISK_TRACE 0
0078 #endif
0079 
0080 /**
0081  * NV Device Control holds the segment controls
0082  */
0083 typedef struct rtems_nvdisk_device_ctl
0084 {
0085   /**
0086    * The device this segment resides on.
0087    */
0088   uint32_t device;
0089 
0090   /**
0091    * Total number of pages in the device.
0092    */
0093   uint32_t pages;
0094 
0095   /**
0096    * Number of pages used for page checksums.
0097    */
0098   uint32_t pages_desc;
0099 
0100   /**
0101    * First block number for this device.
0102    */
0103   uint32_t block_base;
0104 
0105   /**
0106    * Device descriptor.
0107    */
0108   const rtems_nvdisk_device_desc* descriptor;
0109 } rtems_nvdisk_device_ctl;
0110 
0111 /**
0112  * The NV disk control structure for a single disk. There is one
0113  * for each minor disk in the system.
0114  */
0115 typedef struct
0116 {
0117   uint32_t                  flags;        /**< configuration flags. */
0118   uint32_t                  block_size;   /**< The block size for this disk. */
0119   uint32_t                  block_count;  /**< The number of available blocks. */
0120   rtems_nvdisk_device_ctl*  devices;      /**< The NV devices for this disk. */
0121   uint32_t                  device_count; /**< The number of NV devices. */
0122   uint32_t                  cs_pages;     /**< The num of pages of checksums. */
0123   rtems_mutex               lock;         /**< Mutex for threading protection.*/
0124   uint32_t info_level;                    /**< The info trace level. */
0125 } rtems_nvdisk;
0126 
0127 /**
0128  * The CRC16 factor table. Created during initialisation.
0129  */
0130 static uint16_t* rtems_nvdisk_crc16_factor;
0131 
0132 /**
0133  * Calculate the CRC16 checksum.
0134  *
0135  * @param _b The byte to checksum.
0136  * @param _c The current checksum.
0137  */
0138 #define rtems_nvdisk_calc_crc16(_b, _c) \
0139   rtems_nvdisk_crc16_factor[((_b) ^ ((_c) & 0xff)) & 0xff] ^ (((_c) >> 8) & 0xff)
0140 
0141 /**
0142  * Generate the CRC table.
0143  *
0144  * @param pattern The seed pattern for the table of factors.
0145  * @relval RTEMS_SUCCESSFUL The table was generated.
0146  * @retval RTEMS_NO_MEMORY The table could not be allocated from the heap.
0147  */
0148 static rtems_status_code
0149 rtems_nvdisk_crc16_gen_factors (uint16_t pattern)
0150 {
0151   uint32_t b;
0152 
0153   rtems_nvdisk_crc16_factor = malloc (sizeof (uint16_t) * 256);
0154   if (!rtems_nvdisk_crc16_factor)
0155     return RTEMS_NO_MEMORY;
0156 
0157   for (b = 0; b < 256; b++)
0158   {
0159     uint32_t i;
0160     uint16_t v = b;
0161     for (i = 8; i--;)
0162       v = v & 1 ? (v >> 1) ^ pattern : v >> 1;
0163     rtems_nvdisk_crc16_factor[b] = v & 0xffff;
0164   }
0165   return RTEMS_SUCCESSFUL;
0166 }
0167 
0168 #if RTEMS_NVDISK_TRACE
0169 /**
0170  * Print a message to the nvdisk output and flush it.
0171  *
0172  * @param nvd The nvdisk control structure.
0173  * @param format The format string. See printf for details.
0174  * @param ... The arguments for the format text.
0175  * @return int The number of bytes written to the output.
0176  */
0177 static int
0178 rtems_nvdisk_printf (const rtems_nvdisk* nvd, const char *format, ...)
0179 {
0180   int ret = 0;
0181   if (nvd->info_level >= 3)
0182   {
0183     va_list args;
0184     va_start (args, format);
0185     fprintf (stdout, "nvdisk:");
0186     ret =  vfprintf (stdout, format, args);
0187     fprintf (stdout, "\n");
0188     fflush (stdout);
0189     va_end (args);
0190   }
0191   return ret;
0192 }
0193 
0194 /**
0195  * Print a info message to the nvdisk output and flush it.
0196  *
0197  * @param nvd The nvdisk control structure.
0198  * @param format The format string. See printf for details.
0199  * @param ... The arguments for the format text.
0200  * @return int The number of bytes written to the output.
0201  */
0202 static int
0203 rtems_nvdisk_info (const rtems_nvdisk* nvd, const char *format, ...)
0204 {
0205   int ret = 0;
0206   if (nvd->info_level >= 2)
0207   {
0208     va_list args;
0209     va_start (args, format);
0210     fprintf (stdout, "nvdisk:");
0211     ret =  vfprintf (stdout, format, args);
0212     fprintf (stdout, "\n");
0213     fflush (stdout);
0214     va_end (args);
0215   }
0216   return ret;
0217 }
0218 
0219 /**
0220  * Print a warning to the nvdisk output and flush it.
0221  *
0222  * @param nvd The nvdisk control structure.
0223  * @param format The format string. See printf for details.
0224  * @param ... The arguments for the format text.
0225  * @return int The number of bytes written to the output.
0226  */
0227 static int
0228 rtems_nvdisk_warning (const rtems_nvdisk* nvd, const char *format, ...)
0229 {
0230   int ret = 0;
0231   if (nvd->info_level >= 1)
0232   {
0233     va_list args;
0234     va_start (args, format);
0235     fprintf (stdout, "nvdisk:warning:");
0236     ret =  vfprintf (stdout, format, args);
0237     fprintf (stdout, "\n");
0238     fflush (stdout);
0239     va_end (args);
0240   }
0241   return ret;
0242 }
0243 #endif
0244 
0245 /**
0246  * Print an error to the nvdisk output and flush it.
0247  *
0248  * @param format The format string. See printf for details.
0249  * @param ... The arguments for the format text.
0250  * @return int The number of bytes written to the output.
0251  */
0252 static int
0253 rtems_nvdisk_error (const char *format, ...)
0254 {
0255   int ret;
0256   va_list args;
0257   va_start (args, format);
0258   fprintf (stderr, "nvdisk:error:");
0259   ret =  vfprintf (stderr, format, args);
0260   fprintf (stderr, "\n");
0261   fflush (stderr);
0262   va_end (args);
0263   return ret;
0264 }
0265 
0266 /**
0267  * Get the descriptor for a device.
0268  */
0269 static const rtems_nvdisk_device_desc*
0270 rtems_nvdisk_device_descriptor (const rtems_nvdisk* nvd, uint32_t device)
0271 {
0272   return nvd->devices[device].descriptor;
0273 }
0274 
0275 /**
0276  * Read a block of data from a device.
0277  */
0278 static int
0279 rtems_nvdisk_device_read (const rtems_nvdisk* nvd,
0280                           uint32_t            device,
0281                           uint32_t            offset,
0282                           void*               buffer,
0283                           uint32_t            size)
0284 {
0285   const rtems_nvdisk_device_desc*     dd;
0286   const rtems_nvdisk_driver_handlers* ops;
0287   dd  = rtems_nvdisk_device_descriptor (nvd, device);
0288   ops = nvd->devices[device].descriptor->nv_ops;
0289 #if RTEMS_NVDISK_TRACE
0290   rtems_nvdisk_printf (nvd, "  dev-read: %02d-%08x: s=%d",
0291                       device, offset, size);
0292 #endif
0293   return ops->read (device, dd->flags, dd->base, offset, buffer, size);
0294 }
0295 
0296 /**
0297  * Write a block of data to a device.
0298  */
0299 static int
0300 rtems_nvdisk_device_write (const rtems_nvdisk* nvd,
0301                            uint32_t            device,
0302                            uint32_t            offset,
0303                            const void*         buffer,
0304                            uint32_t            size)
0305 {
0306   const rtems_nvdisk_device_desc*     dd;
0307   const rtems_nvdisk_driver_handlers* ops;
0308   dd  = rtems_nvdisk_device_descriptor (nvd, device);
0309   ops = nvd->devices[device].descriptor->nv_ops;
0310 #if RTEMS_NVDISK_TRACE
0311   rtems_nvdisk_printf (nvd, "  dev-write: %02d-%08x: s=%d",
0312                       device, offset, size);
0313 #endif
0314   return ops->write (device, dd->flags, dd->base, offset, buffer, size);
0315 }
0316 
0317 #if NOT_USED
0318 /**
0319  * Verify the data with the data in a segment.
0320  */
0321 static int
0322 rtems_nvdisk_device_verify (const rtems_nvdisk* nvd,
0323                             uint32_t            device,
0324                             uint32_t            offset,
0325                             const void*         buffer,
0326                             uint32_t            size)
0327 {
0328   const rtems_nvdisk_device_desc*     dd;
0329   const rtems_nvdisk_driver_handlers* ops;
0330   dd  = rtems_nvdisk_device_descriptor (nvd, device);
0331   ops = nvd->devices[device].descriptor->nv_ops;
0332 #if RTEMS_NVDISK_TRACE
0333   rtems_nvdisk_printf (nvd, "  seg-verify: %02d-%08x: s=%d",
0334                       device, offset, size);
0335 #endif
0336   return ops->verify (device, dd->flags, dd->base, offset, buffer, size);
0337 }
0338 #endif
0339 
0340 /**
0341  * Read a page of data from the device.
0342  */
0343 static int
0344 rtems_nvdisk_read_page (const rtems_nvdisk* nvd,
0345                         uint32_t            device,
0346                         uint32_t            page,
0347                         void*               buffer)
0348 {
0349   return rtems_nvdisk_device_read (nvd, device,
0350                                    page * nvd->block_size, buffer,
0351                                    nvd->block_size);
0352 }
0353 
0354 /**
0355  * Write a page of data to a device.
0356  */
0357 static int
0358 rtems_nvdisk_write_page (const rtems_nvdisk* nvd,
0359                          uint32_t            device,
0360                          uint32_t            page,
0361                          const void*         buffer)
0362 {
0363   return rtems_nvdisk_device_write (nvd, device,
0364                                     page * nvd->block_size,
0365                                     buffer, nvd->block_size);
0366 }
0367 
0368 /**
0369  * Read the checksum from the device.
0370  */
0371 static int
0372 rtems_nvdisk_read_checksum (const rtems_nvdisk* nvd,
0373                             uint32_t            device,
0374                             uint32_t            page,
0375                             uint16_t*           cs)
0376 {
0377   return rtems_nvdisk_device_read (nvd, device,
0378                                    page * sizeof (uint16_t),
0379                                    cs, sizeof (uint16_t));
0380 }
0381 
0382 /**
0383  * Write the checksum to the device.
0384  */
0385 static int
0386 rtems_nvdisk_write_checksum (const rtems_nvdisk* nvd,
0387                              uint32_t            device,
0388                              uint32_t            page,
0389                              const uint16_t      cs)
0390 {
0391   return rtems_nvdisk_device_write (nvd, device,
0392                                     page * sizeof (uint16_t),
0393                                     &cs, sizeof (uint16_t));
0394 }
0395 
0396 /**
0397  * Calculate the pages in a device give the device descriptor and the
0398  * page size.
0399  *
0400  * @param dd The device descriptor.
0401  * @param page_size The page size in bytes.
0402  */
0403 static uint32_t
0404 rtems_nvdisk_pages_in_device (const rtems_nvdisk*             nvd,
0405                               const rtems_nvdisk_device_desc* dd)
0406 {
0407   return dd->size / nvd->block_size;
0408 }
0409 
0410 /**
0411  * Calculate the number of pages needed to hold the page descriptors.
0412  * The calculation need to round up.
0413  */
0414 static uint32_t
0415 rtems_nvdisk_page_desc_pages (const rtems_nvdisk*             nvd,
0416                               const rtems_nvdisk_device_desc* dd)
0417 {
0418   uint32_t pages = rtems_nvdisk_pages_in_device (nvd, dd);
0419   uint32_t bytes = pages * sizeof (uint16_t);
0420   return ((bytes - 1) / nvd->block_size) + 1;
0421 }
0422 
0423 /**
0424  * Calculate the checksum of a page.
0425  */
0426 static uint16_t
0427 rtems_nvdisk_page_checksum (const uint8_t* buffer, uint32_t page_size)
0428 {
0429   uint16_t cs = 0xffff;
0430   uint32_t i;
0431 
0432   for (i = 0; i < page_size; i++, buffer++)
0433     cs = rtems_nvdisk_calc_crc16 (*buffer, cs);
0434 
0435   return cs;
0436 }
0437 
0438 /**
0439  * Map a block to a device.
0440  */
0441 static rtems_nvdisk_device_ctl*
0442 rtems_nvdisk_get_device (rtems_nvdisk* nvd, uint32_t block)
0443 {
0444   uint32_t device;
0445 
0446   if (block >= nvd->block_count)
0447   {
0448     rtems_nvdisk_error ("read-block: bad block: %d", block);
0449     return NULL;
0450   }
0451 
0452   for (device = 0; device < nvd->device_count; device++)
0453   {
0454     rtems_nvdisk_device_ctl* dc = &nvd->devices[device];
0455     if ((block >= dc->block_base) &&
0456         (block < (dc->block_base + dc->pages - dc->pages_desc)))
0457       return dc;
0458   }
0459 
0460   rtems_nvdisk_error ("map-block:%d: no device/page map found", block);
0461 
0462   return NULL;
0463 }
0464 
0465 /**
0466  * Get the page for a block in a device.
0467  */
0468 static uint32_t
0469 rtems_nvdisk_get_page (rtems_nvdisk_device_ctl* dc,
0470                        uint32_t                 block)
0471 {
0472   return block - dc->block_base;
0473 }
0474 
0475 /**
0476  * Read a block. The block is checked to see if the page referenced
0477  * is valid and the page has a valid crc.
0478  *
0479  * @param nvd The rtems_nvdisk control table.
0480  * @param block The block number to read.
0481  * @param buffer The buffer to write the data into.
0482  * @return 0 No error.
0483  * @return EIO Invalid block number or crc.
0484  */
0485 static int
0486 rtems_nvdisk_read_block (rtems_nvdisk* nvd, uint32_t block, uint8_t* buffer)
0487 {
0488   rtems_nvdisk_device_ctl* dc;
0489   uint32_t                 page;
0490   uint16_t                 crc;
0491   uint16_t                 cs;
0492   int                      ret;
0493 
0494   dc = rtems_nvdisk_get_device (nvd, block);
0495 
0496   if (!dc)
0497     return EIO;
0498 
0499   page = rtems_nvdisk_get_page (dc, block);
0500 
0501 #if RTEMS_NVDISK_TRACE
0502   rtems_nvdisk_info (nvd, " read-block:%d=>%02d-%03d, cs:%04x",
0503                      block, dc->device, page, crc);
0504 #endif
0505 
0506   ret = rtems_nvdisk_read_checksum (nvd, dc->device, page, &crc);
0507 
0508   if (ret)
0509     return ret;
0510 
0511   if (crc == 0xffff)
0512   {
0513 #if RTEMS_NVDISK_TRACE
0514     rtems_nvdisk_warning (nvd, "read-block: crc not set: %d", block);
0515 #endif
0516     memset (buffer, 0, nvd->block_size);
0517     return 0;
0518   }
0519 
0520   ret = rtems_nvdisk_read_page (nvd, dc->device, page + dc->pages_desc, buffer);
0521 
0522   if (ret)
0523     return ret;
0524 
0525   cs = rtems_nvdisk_page_checksum (buffer, nvd->block_size);
0526 
0527   if (cs != crc)
0528   {
0529     rtems_nvdisk_error ("read-block: crc failure: %d: buffer:%04x page:%04x",
0530                         block, cs, crc);
0531     return EIO;
0532   }
0533 
0534   return 0;
0535 }
0536 
0537 /**
0538  * Write a block.
0539  *
0540  * @param nvd The rtems_nvdisk control table.
0541  * @param block The block number to read.
0542  * @param block_size The size of the block. Must match what we have.
0543  * @param buffer The buffer to write the data into.
0544  * @return 0 No error.
0545  * @return EIO Invalid block size, block number, segment pointer, crc,
0546  *             page flags.
0547  */
0548 static int
0549 rtems_nvdisk_write_block (rtems_nvdisk*        nvd,
0550                           uint32_t             block,
0551                           const unsigned char* buffer)
0552 {
0553   rtems_nvdisk_device_ctl* dc;
0554   uint32_t                 page;
0555   uint16_t                 cs;
0556   int                      ret;
0557 
0558   dc = rtems_nvdisk_get_device (nvd, block);
0559 
0560   if (!dc)
0561     return EIO;
0562 
0563   page = rtems_nvdisk_get_page (dc, block);
0564 
0565   cs = rtems_nvdisk_page_checksum (buffer, nvd->block_size);
0566 
0567 #if RTEMS_NVDISK_TRACE
0568   rtems_nvdisk_info (nvd, " write-block:%d=>%02d-%03d", block, dc->device, page);
0569 #endif
0570 
0571   ret = rtems_nvdisk_write_page (nvd, dc->device, page + dc->pages_desc, buffer);
0572 
0573   if (ret)
0574     return ret;
0575 
0576   return rtems_nvdisk_write_checksum (nvd, dc->device, page, cs);
0577 }
0578 
0579 /**
0580  * Disk READ request handler. This primitive copies data from the
0581  * flash disk to the supplied buffer and invoke the callout function
0582  * to inform upper layer that reading is completed.
0583  *
0584  * @param req Pointer to the READ block device request info.
0585  * @retval int The ioctl return value.
0586  */
0587 static int
0588 rtems_nvdisk_read (rtems_nvdisk* nvd, rtems_blkdev_request* req)
0589 {
0590   rtems_blkdev_sg_buffer* sg = req->bufs;
0591   uint32_t                bufs;
0592   int                     ret = 0;
0593 
0594 #if RTEMS_NVDISK_TRACE
0595   rtems_nvdisk_info (nvd, "read: blocks=%d", req->bufnum);
0596 #endif
0597 
0598   for (bufs = 0; (ret == 0) && (bufs < req->bufnum); bufs++, sg++)
0599   {
0600     uint8_t* data;
0601     uint32_t nvb;
0602     uint32_t b;
0603     nvb = sg->length / nvd->block_size;
0604     data = sg->buffer;
0605     for (b = 0; b < nvb; b++, data += nvd->block_size)
0606     {
0607       ret = rtems_nvdisk_read_block (nvd, sg->block + b, data);
0608       if (ret)
0609         break;
0610     }
0611   }
0612 
0613   rtems_blkdev_request_done (req, ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL);
0614 
0615   return 0;
0616 }
0617 
0618 /**
0619  * Flash disk WRITE request handler. This primitive copies data from
0620  * supplied buffer to NV disk and invoke the callout function to inform
0621  * upper layer that writing is completed.
0622  *
0623  * @param req Pointers to the WRITE block device request info.
0624  * @retval int The ioctl return value.
0625  */
0626 static int
0627 rtems_nvdisk_write (rtems_nvdisk* nvd, rtems_blkdev_request* req)
0628 {
0629   rtems_blkdev_sg_buffer* sg = req->bufs;
0630   uint32_t                bufs;
0631   int                     ret = 0;
0632 
0633 #if RTEMS_NVDISK_TRACE
0634   rtems_nvdisk_info (nvd, "write: blocks=%d", req->bufnum);
0635 #endif
0636 
0637   for (bufs = 0; (ret == 0) && (bufs < req->bufnum); bufs++, sg++)
0638   {
0639     uint8_t* data;
0640     uint32_t nvb;
0641     uint32_t b;
0642     nvb = sg->length / nvd->block_size;
0643     data = sg->buffer;
0644     for (b = 0; b < nvb; b++, data += nvd->block_size)
0645     {
0646       ret = rtems_nvdisk_write_block (nvd, sg->block + b, data);
0647       if (ret)
0648         break;
0649     }
0650   }
0651 
0652   rtems_blkdev_request_done (req, ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL);
0653 
0654   return 0;
0655 }
0656 
0657 /**
0658  * NV disk erase disk sets all the checksums for 0xffff.
0659  *
0660  * @param nvd The nvdisk data.
0661  * @retval int The ioctl return value.
0662  */
0663 static int
0664 rtems_nvdisk_erase_disk (rtems_nvdisk* nvd)
0665 {
0666   uint32_t device;
0667 
0668 #if RTEMS_NVDISK_TRACE
0669   rtems_nvdisk_info (nvd, "erase-disk");
0670 #endif
0671 
0672   for (device = 0; device < nvd->device_count; device++)
0673   {
0674     rtems_nvdisk_device_ctl* dc = &nvd->devices[device];
0675     uint32_t                 page;
0676     for (page = 0; page < (dc->pages - dc->pages_desc); page++)
0677     {
0678       int ret = rtems_nvdisk_write_checksum (nvd, dc->device, page, 0xffff);
0679       if (ret)
0680         return ret;
0681     }
0682   }
0683 
0684   return 0;
0685 }
0686 
0687 /**
0688  * NV disk IOCTL handler.
0689  *
0690  * @param dd Disk device.
0691  * @param req IOCTL request code.
0692  * @param argp IOCTL argument.
0693  * @retval The IOCTL return value
0694  */
0695 static int
0696 rtems_nvdisk_ioctl (rtems_disk_device *dd, uint32_t req, void* argp)
0697 {
0698   rtems_nvdisk*         nvd = rtems_disk_get_driver_data (dd);
0699   rtems_blkdev_request* r = argp;
0700 
0701   if (nvd->device_count == 0)
0702   {
0703     errno = ENODEV;
0704     return -1;
0705   }
0706 
0707   errno = 0;
0708 
0709   rtems_mutex_lock (&nvd->lock);
0710 
0711   switch (req)
0712   {
0713     case RTEMS_BLKIO_REQUEST:
0714       switch (r->req)
0715       {
0716         case RTEMS_BLKDEV_REQ_READ:
0717           errno = rtems_nvdisk_read (nvd, r);
0718           break;
0719 
0720         case RTEMS_BLKDEV_REQ_WRITE:
0721           errno = rtems_nvdisk_write (nvd, r);
0722           break;
0723 
0724         default:
0725           errno = EINVAL;
0726           break;
0727       }
0728       break;
0729 
0730     case RTEMS_NVDISK_IOCTL_ERASE_DISK:
0731       errno = rtems_nvdisk_erase_disk (nvd);
0732       break;
0733 
0734     case RTEMS_NVDISK_IOCTL_INFO_LEVEL:
0735       nvd->info_level = (uintptr_t) argp;
0736       break;
0737 
0738     default:
0739       rtems_blkdev_ioctl (dd, req, argp);
0740       break;
0741   }
0742 
0743   rtems_mutex_unlock (&nvd->lock);
0744 
0745   return errno == 0 ? 0 : -1;
0746 }
0747 
0748 /**
0749  * NV disk device driver initialization.
0750  *
0751  * @todo Memory clean up on error is really badly handled.
0752  *
0753  * @param major NV disk major device number.
0754  * @param minor Minor device number, not applicable.
0755  * @param arg Initialization argument, not applicable.
0756  */
0757 rtems_device_driver
0758 rtems_nvdisk_initialize (rtems_device_major_number major RTEMS_UNUSED,
0759                         rtems_device_minor_number minor RTEMS_UNUSED,
0760                         void*                     arg RTEMS_UNUSED)
0761 {
0762   const rtems_nvdisk_config* c = rtems_nvdisk_configuration;
0763   rtems_nvdisk*              nvd;
0764   rtems_status_code          sc;
0765   uint32_t                   i;
0766 
0767   sc = rtems_nvdisk_crc16_gen_factors (0x8408);
0768   if (sc != RTEMS_SUCCESSFUL)
0769       return sc;
0770 
0771   nvd = calloc (rtems_nvdisk_configuration_size, sizeof (*nvd));
0772   if (!nvd)
0773     return RTEMS_NO_MEMORY;
0774 
0775   for (i = 0; i < rtems_nvdisk_configuration_size; i++, c++, nvd++)
0776   {
0777     char     name[] = RTEMS_NVDISK_DEVICE_BASE_NAME "a";
0778     uint32_t device;
0779     uint32_t blocks = 0;
0780 
0781     name [sizeof(RTEMS_NVDISK_DEVICE_BASE_NAME)] += i;
0782 
0783     nvd->flags        = c->flags;
0784     nvd->block_size   = c->block_size;
0785     nvd->info_level   = c->info_level;
0786 
0787     nvd->devices = calloc (c->device_count, sizeof (rtems_nvdisk_device_ctl));
0788     if (!nvd->devices)
0789       return RTEMS_NO_MEMORY;
0790 
0791     for (device = 0; device < c->device_count; device++)
0792     {
0793       rtems_nvdisk_device_ctl* dc = &nvd->devices[device];
0794 
0795       dc->device     = device;
0796       dc->pages      = rtems_nvdisk_pages_in_device (nvd, &c->devices[device]);
0797       dc->pages_desc = rtems_nvdisk_page_desc_pages (nvd, &c->devices[device]);
0798       dc->block_base = blocks;
0799 
0800       blocks += dc->pages - dc->pages_desc;
0801 
0802       dc->descriptor = &c->devices[device];
0803     }
0804 
0805     nvd->block_count  = blocks;
0806     nvd->device_count = c->device_count;
0807 
0808     sc = rtems_blkdev_create(name, c->block_size, blocks,
0809                              rtems_nvdisk_ioctl, nvd);
0810     if (sc != RTEMS_SUCCESSFUL)
0811     {
0812       rtems_nvdisk_error ("disk create phy failed");
0813       return sc;
0814     }
0815 
0816     rtems_mutex_init (&nvd->lock, "NV Disk");
0817   }
0818 
0819   return RTEMS_SUCCESSFUL;
0820 }