Back to home page

LXR

 
 

    


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

0001 /*
0002  * Copyright (C) 2023 Aaron Nyholm
0003  *
0004  * Redistribution and use in source and binary forms, with or without
0005  * modification, are permitted provided that the following conditions
0006  * are met:
0007  * 1. Redistributions of source code must retain the above copyright
0008  *    notice, this list of conditions and the following disclaimer.
0009  * 2. Redistributions in binary form must reproduce the above copyright
0010  *    notice, this list of conditions and the following disclaimer in the
0011  *    documentation and/or other materials provided with the distribution.
0012  *
0013  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0014  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0015  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0016  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0017  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0018  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0019  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0020  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0021  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0022  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0023  * POSSIBILITY OF SUCH DAMAGE.
0024  */
0025 
0026 #ifdef HAVE_CONFIG_H
0027 #include "config.h"
0028 #endif
0029 
0030 #include <dev/flash/flashdev.h>
0031 
0032 #include <rtems/imfs.h>
0033 #include <rtems/score/assert.h>
0034 
0035 #include <errno.h>
0036 #include <fcntl.h>
0037 #include <stdlib.h>
0038 #include <string.h>
0039 #include <unistd.h>
0040 #include <assert.h>
0041 
0042 #define RTEMS_FLASHDEV_REGION_ALLOC_FULL 0xFFFFFFFFUL
0043 #define RTEMS_FLASHDEV_REGION_UNDEFINED 0xFFFFFFFFUL
0044 #define RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH 32
0045 
0046 #define RTEMS_FLASHDEV_BITALLOC_LENGTH(t) \
0047   (t->max_regions/RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH)
0048 #define RTEMS_FLASHDEV_BITALLOC_FINAL_BITS(t) \
0049   (t->max_regions%RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH)
0050 
0051 static int rtems_flashdev_do_init(
0052   rtems_flashdev *flash,
0053   void ( *destroy )( rtems_flashdev *flash )
0054 );
0055 
0056 static int rtems_flashdev_read_write(
0057   rtems_libio_t *iop,
0058   const void *write_buff,
0059   void *read_buff,
0060   size_t count
0061 );
0062 
0063 static int rtems_flashdev_ioctl_erase(
0064   rtems_flashdev *flash,
0065   rtems_libio_t *iop,
0066   void *arg
0067 );
0068 
0069 static off_t rtems_flashdev_get_region_offset(
0070   rtems_flashdev *flash,
0071   rtems_libio_t *iop
0072 );
0073 
0074 static size_t rtems_flashdev_get_region_size(
0075   rtems_flashdev *flash,
0076   rtems_libio_t *iop
0077 );
0078 
0079 static int rtems_flashdev_ioctl_set_region(
0080   rtems_flashdev *flash,
0081   rtems_libio_t *iop,
0082   void *arg
0083 );
0084 
0085 static int rtems_flashdev_ioctl_create_region(
0086   rtems_flashdev *flash,
0087   rtems_libio_t *iop,
0088   rtems_flashdev_region *region_in
0089 );
0090 
0091 static int rtems_flashdev_ioctl_update_region(
0092   rtems_flashdev *flash,
0093   rtems_libio_t *iop,
0094   rtems_flashdev_region *region_in
0095 );
0096 
0097 static int rtems_flashdev_ioctl_clear_region(
0098   rtems_flashdev *flash,
0099   rtems_libio_t *iop
0100 );
0101 
0102 static uint32_t rtems_flashdev_ioctl_jedec_id(
0103   rtems_flashdev *flash
0104 );
0105 
0106 static uint32_t rtems_flashdev_ioctl_flash_type(
0107   rtems_flashdev *flash,
0108   void *arg
0109 );
0110 
0111 static int rtems_flashdev_ioctl_pageinfo_offset(
0112   rtems_flashdev *flash,
0113   void *arg
0114 );
0115 
0116 static int rtems_flashdev_ioctl_pageinfo_index(
0117   rtems_flashdev *flash,
0118   void *arg
0119 );
0120 
0121 static int rtems_flashdev_ioctl_page_count(
0122   rtems_flashdev *flash,
0123   void *arg
0124 );
0125 
0126 static int rtems_flashdev_ioctl_sectorinfo_offset(
0127   rtems_flashdev *flash,
0128   void *arg
0129 );
0130 
0131 static int rtems_flashdev_ioctl_sector_count(
0132   rtems_flashdev *flash,
0133   void *arg
0134 );
0135 
0136 static int rtems_flashdev_ioctl_write_block_size(
0137   rtems_flashdev *flash,
0138   void *arg
0139 );
0140 
0141 static int rtems_flashdev_get_addr(
0142   rtems_flashdev *flash,
0143   rtems_libio_t *iop,
0144   size_t count,
0145   off_t *addr
0146 );
0147 
0148 static int rtems_flashdev_get_abs_addr(
0149   rtems_flashdev *flash,
0150   rtems_libio_t *iop,
0151   size_t count,
0152   off_t *addr
0153 );
0154 
0155 static int rtems_flashdev_update_and_return(
0156   rtems_libio_t *iop,
0157   int status,
0158   size_t count,
0159   off_t new_offset
0160 );
0161 
0162 static uint32_t rtems_flashdev_find_unallocated_region(
0163   rtems_flashdev_region_table *region_table
0164 );
0165 
0166 static uint32_t rtems_flashdev_set_region(
0167   rtems_flashdev_region_table *region_table,
0168   int index
0169 );
0170 
0171 static uint32_t rtems_flashdev_unset_region(
0172   rtems_flashdev_region_table *region_table,
0173   int index
0174 );
0175 
0176 static uint32_t rtems_flashdev_check_allocation(
0177   rtems_flashdev_region_table *region_table,
0178   int index
0179 );
0180 
0181 static int rtems_flashdev_open(
0182   rtems_libio_t *iop,
0183   const char *path,
0184   int oflag,
0185   mode_t mode
0186 );
0187 
0188 static int rtems_flashdev_close(
0189   rtems_libio_t *iop
0190 );
0191 
0192 static ssize_t rtems_flashdev_read(
0193   rtems_libio_t *iop,
0194   void *buffer,
0195   size_t count
0196 );
0197 
0198 static ssize_t rtems_flashdev_write(
0199   rtems_libio_t *iop,
0200   const void *buffer,
0201   size_t count
0202 );
0203 
0204 static int rtems_flashdev_ioctl(
0205   rtems_libio_t *iop,
0206   ioctl_command_t command,
0207   void *arg
0208 );
0209 
0210 static off_t rtems_flashdev_lseek(
0211   rtems_libio_t *iop,
0212   off_t offset,
0213   int whence
0214 );
0215 
0216 static void rtems_flashdev_node_destroy(
0217   IMFS_jnode_t *node
0218 );
0219 
0220 static const rtems_filesystem_file_handlers_r rtems_flashdev_handler = {
0221   .open_h = rtems_flashdev_open,
0222   .close_h = rtems_flashdev_close,
0223   .read_h = rtems_flashdev_read,
0224   .write_h = rtems_flashdev_write,
0225   .ioctl_h = rtems_flashdev_ioctl,
0226   .lseek_h = rtems_flashdev_lseek,
0227   .fstat_h = IMFS_stat,
0228   .ftruncate_h = rtems_filesystem_default_ftruncate,
0229   .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
0230   .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
0231   .fcntl_h = rtems_filesystem_default_fcntl,
0232   .kqfilter_h = rtems_filesystem_default_kqfilter,
0233   .mmap_h = rtems_filesystem_default_mmap,
0234   .poll_h = rtems_filesystem_default_poll,
0235   .readv_h = rtems_filesystem_default_readv,
0236   .writev_h = rtems_filesystem_default_writev };
0237 
0238 static const IMFS_node_control
0239   rtems_flashdev_node_control = IMFS_GENERIC_INITIALIZER(
0240     &rtems_flashdev_handler,
0241     IMFS_node_initialize_generic,
0242     rtems_flashdev_node_destroy
0243 );
0244 
0245 static void rtems_flashdev_node_destroy(
0246   IMFS_jnode_t *node
0247 )
0248 {
0249   rtems_flashdev *flash;
0250 
0251   flash = IMFS_generic_get_context_by_node( node );
0252 
0253   ( *flash->destroy )( flash );
0254 
0255   IMFS_node_destroy_default( node );
0256 }
0257 
0258 static uint32_t rtems_flashdev_get_region_index(
0259   rtems_libio_t *iop
0260 )
0261 {
0262   return (uint32_t)iop->data0;
0263 }
0264 
0265 static int rtems_flashdev_is_region_defined(
0266   rtems_libio_t *iop
0267 )
0268 {
0269   return (rtems_flashdev_get_region_index( iop ) != RTEMS_FLASHDEV_REGION_UNDEFINED);
0270 }
0271 
0272 static void rtems_flashdev_set_region_index(
0273   rtems_libio_t *iop,
0274   uint32_t index
0275 )
0276 {
0277   iop->data0 = index;
0278 }
0279 
0280 static int rtems_flashdev_check_offset_region(
0281   rtems_flashdev *flash,
0282   rtems_libio_t *iop,
0283   off_t offset
0284 )
0285 {
0286   if ( ( rtems_flashdev_is_region_defined( iop ) ) &&
0287        ( offset > rtems_flashdev_get_region_size( flash, iop ) ) ) {
0288     rtems_set_errno_and_return_minus_one( EINVAL );
0289   }
0290   return 0;
0291 }
0292 
0293 static void rtems_flashdev_obtain( rtems_flashdev *flash )
0294 {
0295   rtems_recursive_mutex_lock( &flash->mutex );
0296 }
0297 
0298 static void rtems_flashdev_release( rtems_flashdev *flash )
0299 {
0300   rtems_recursive_mutex_unlock( &flash->mutex );
0301 }
0302 
0303 static ssize_t rtems_flashdev_read(
0304   rtems_libio_t *iop,
0305   void *buffer,
0306   size_t count
0307 )
0308 {
0309   return rtems_flashdev_read_write( iop, NULL, buffer, count );
0310 }
0311 
0312 static ssize_t rtems_flashdev_write(
0313   rtems_libio_t *iop,
0314   const void *buffer,
0315   size_t count
0316 )
0317 {
0318   return rtems_flashdev_read_write( iop, buffer, NULL, count);
0319 }
0320 
0321 static int rtems_flashdev_read_write(
0322   rtems_libio_t *iop,
0323   const void *write_buff,
0324   void *read_buff,
0325   size_t count
0326 )
0327 {
0328   rtems_flashdev *flash = IMFS_generic_get_context_by_iop( iop );
0329   off_t addr;
0330   int status;
0331 
0332   if ( read_buff == NULL && write_buff == NULL ) {
0333     rtems_set_errno_and_return_minus_one( EINVAL );
0334   }
0335 
0336   /* Get flash address */
0337   status = rtems_flashdev_get_addr( flash, iop, count, &addr );
0338   if ( status < 0 ) {
0339     return status;
0340   }
0341 
0342   /* Read or Write to flash */
0343   rtems_flashdev_obtain( flash );
0344   if ( read_buff != NULL ) {
0345     status = ( *flash->read )( flash, addr, count, read_buff );
0346   } else if ( write_buff != NULL ) {
0347     status = ( *flash->write )( flash, addr, count, write_buff );
0348   }
0349   rtems_flashdev_release( flash );
0350 
0351   /* Update offset and return */
0352   return rtems_flashdev_update_and_return( iop, status, count, addr + count );
0353 }
0354 
0355 static int rtems_flashdev_ioctl(
0356   rtems_libio_t *iop,
0357   ioctl_command_t command,
0358   void *arg
0359 )
0360 {
0361   rtems_flashdev *flash = IMFS_generic_get_context_by_iop( iop );
0362   int err = 0;
0363 
0364   rtems_flashdev_obtain( flash );
0365 
0366   switch ( command ) {
0367     case RTEMS_FLASHDEV_IOCTL_OBTAIN:
0368       rtems_flashdev_obtain( flash );
0369       err = 0;
0370       break;
0371     case RTEMS_FLASHDEV_IOCTL_RELEASE:
0372       rtems_flashdev_release( flash );
0373       err = 0;
0374       break;
0375     case RTEMS_FLASHDEV_IOCTL_JEDEC_ID:
0376       *( (uint32_t *) arg ) = rtems_flashdev_ioctl_jedec_id( flash );
0377       err = 0;
0378       break;
0379     case RTEMS_FLASHDEV_IOCTL_ERASE:
0380       err = rtems_flashdev_ioctl_erase( flash, iop, arg );
0381       break;
0382     case RTEMS_FLASHDEV_IOCTL_REGION_SET:
0383       err = rtems_flashdev_ioctl_set_region( flash, iop, arg );
0384       break;
0385     case RTEMS_FLASHDEV_IOCTL_REGION_UNSET:
0386       err = rtems_flashdev_ioctl_clear_region( flash, iop );
0387       break;
0388     case RTEMS_FLASHDEV_IOCTL_TYPE:
0389       err = rtems_flashdev_ioctl_flash_type( flash, arg );
0390       break;
0391     case RTEMS_FLASHDEV_IOCTL_PAGEINFO_BY_OFFSET:
0392       err = rtems_flashdev_ioctl_pageinfo_offset( flash, arg );
0393       break;
0394     case RTEMS_FLASHDEV_IOCTL_PAGEINFO_BY_INDEX:
0395       err = rtems_flashdev_ioctl_pageinfo_index( flash, arg );
0396       break;
0397     case RTEMS_FLASHDEV_IOCTL_PAGE_COUNT:
0398       err = rtems_flashdev_ioctl_page_count( flash, arg );
0399       break;
0400     case RTEMS_FLASHDEV_IOCTL_SECTORINFO_BY_OFFSET:
0401       err = rtems_flashdev_ioctl_sectorinfo_offset( flash, arg );
0402       break;
0403     case RTEMS_FLASHDEV_IOCTL_SECTOR_COUNT:
0404       err = rtems_flashdev_ioctl_sector_count( flash, arg );
0405       break;
0406     case RTEMS_FLASHDEV_IOCTL_WRITE_BLOCK_SIZE:
0407       err = rtems_flashdev_ioctl_write_block_size( flash, arg );
0408       break;
0409     default:
0410       err = EINVAL;
0411   }
0412 
0413   rtems_flashdev_release( flash );
0414   if ( err != 0 ) {
0415     rtems_set_errno_and_return_minus_one( err );
0416   } else {
0417     return 0;
0418   }
0419 }
0420 
0421 static off_t rtems_flashdev_lseek(
0422   rtems_libio_t *iop,
0423   off_t offset,
0424   int whence
0425 )
0426 {
0427   off_t tmp_offset;
0428   rtems_flashdev *flash = IMFS_generic_get_context_by_iop( iop );
0429 
0430   if ( offset < 0 ) {
0431     rtems_set_errno_and_return_minus_one( EINVAL );
0432   }
0433 
0434   switch ( whence ) {
0435     case SEEK_CUR:
0436       tmp_offset = iop->offset + offset;
0437       break;
0438     case SEEK_SET:
0439       tmp_offset = offset;
0440       break;
0441     case SEEK_END:
0442     default:
0443       rtems_set_errno_and_return_minus_one( EINVAL );
0444   }
0445 
0446   if ( ( rtems_flashdev_is_region_defined(iop) ) &&
0447        ( tmp_offset > rtems_flashdev_get_region_size( flash, iop ) ) ) {
0448     rtems_set_errno_and_return_minus_one( EINVAL );
0449   }
0450 
0451   iop->offset = tmp_offset;
0452   return iop->offset;
0453 }
0454 
0455 static int rtems_flashdev_close(
0456   rtems_libio_t *iop
0457 )
0458 {
0459   rtems_flashdev *flash = IMFS_generic_get_context_by_iop( iop );
0460   rtems_flashdev_ioctl_clear_region( flash, iop );
0461   return rtems_filesystem_default_close( iop );
0462 }
0463 
0464 static int rtems_flashdev_open(
0465   rtems_libio_t *iop,
0466   const char *path,
0467   int oflag,
0468   mode_t mode
0469 )
0470 {
0471   int ret = rtems_filesystem_default_open( iop, path, oflag, mode );
0472   rtems_flashdev_set_region_index(iop, RTEMS_FLASHDEV_REGION_UNDEFINED);
0473   return ret;
0474 }
0475 
0476 int rtems_flashdev_register(
0477   rtems_flashdev *flash,
0478   const char *flash_path
0479 )
0480 {
0481   int rv;
0482   rtems_flashdev_region_table *table = flash->region_table;
0483   int alloc_array_len;
0484 
0485   rv = IMFS_make_generic_node(
0486     flash_path,
0487     S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
0488     &rtems_flashdev_node_control,
0489     flash
0490   );
0491 
0492   if ( rv != 0 ) {
0493     ( *flash->destroy )( flash );
0494   }
0495 
0496   alloc_array_len = RTEMS_FLASHDEV_BITALLOC_LENGTH(table) +
0497     ((RTEMS_FLASHDEV_BITALLOC_FINAL_BITS(table)) != 0);
0498 
0499   memset(table->bit_allocator, 0, alloc_array_len);
0500 
0501   return rv;
0502 }
0503 
0504 static int rtems_flashdev_do_init(
0505   rtems_flashdev *flash,
0506   void ( *destroy )( rtems_flashdev *flash )
0507 )
0508 {
0509   rtems_recursive_mutex_init( &flash->mutex, "RTEMS_FLASHDEV Flash" );
0510   flash->destroy = destroy;
0511   flash->read = NULL;
0512   flash->write = NULL;
0513   flash->erase = NULL;
0514   flash->jedec_id = NULL;
0515   flash->flash_type = NULL;
0516   flash->page_info_by_offset = NULL;
0517   flash->page_info_by_index = NULL;
0518   flash->page_count = NULL;
0519   flash->write_block_size = NULL;
0520   flash->region_table = NULL;
0521   return 0;
0522 }
0523 
0524 void rtems_flashdev_destroy( rtems_flashdev *flash )
0525 {
0526   rtems_recursive_mutex_destroy( &flash->mutex );
0527 }
0528 
0529 void rtems_flashdev_destroy_and_free( rtems_flashdev *flash )
0530 {
0531   if ( flash == NULL ) {
0532     return;
0533   }
0534   rtems_recursive_mutex_destroy( &( flash->mutex ) );
0535   free( flash );
0536   flash = NULL;
0537   return;
0538 }
0539 
0540 int rtems_flashdev_init( rtems_flashdev *flash )
0541 {
0542   memset( flash, 0, sizeof( *flash ) );
0543 
0544   return rtems_flashdev_do_init( flash, rtems_flashdev_destroy );
0545 }
0546 
0547 rtems_flashdev *rtems_flashdev_alloc_and_init( size_t size )
0548 {
0549   rtems_flashdev *flash = NULL;
0550 
0551   if ( size >= sizeof( *flash ) ) {
0552     flash = calloc( 1, size );
0553     if ( flash != NULL ) {
0554       int rv;
0555 
0556       rv = rtems_flashdev_do_init( flash, rtems_flashdev_destroy_and_free );
0557       if ( rv != 0 ) {
0558         rtems_recursive_mutex_destroy( &flash->mutex );
0559         free( flash );
0560         return NULL;
0561       }
0562     }
0563   }
0564 
0565   return flash;
0566 }
0567 
0568 static int rtems_flashdev_get_addr(
0569   rtems_flashdev *flash,
0570   rtems_libio_t *iop,
0571   size_t count,
0572   off_t *addr
0573 )
0574 {
0575   off_t new_offset;
0576 
0577   /* Check address is in valid region */
0578   new_offset = iop->offset + count;
0579 
0580   if (rtems_flashdev_check_offset_region(flash, iop, new_offset)) {
0581     return -1;
0582   }
0583 
0584   /* Get address for operation */
0585   if ( !rtems_flashdev_is_region_defined( iop ) ) {
0586     *addr = iop->offset;
0587   } else {
0588     *addr = ( iop->offset + rtems_flashdev_get_region_offset( flash, iop ) );
0589   }
0590   return 0;
0591 }
0592 
0593 static int rtems_flashdev_get_abs_addr(
0594   rtems_flashdev *flash,
0595   rtems_libio_t *iop,
0596   size_t count,
0597   off_t *addr
0598 )
0599 {
0600   off_t new_offset;
0601 
0602   /* Check address is in valid region */
0603   new_offset = *addr + count;
0604 
0605   if (rtems_flashdev_check_offset_region(flash, iop, new_offset)) {
0606     return -1;
0607   }
0608 
0609   /* Get address for operation */
0610   if ( rtems_flashdev_is_region_defined( iop ) ) {
0611     *addr = ( *addr + rtems_flashdev_get_region_offset( flash, iop ) );
0612   }
0613   return 0;
0614 }
0615 static int rtems_flashdev_update_and_return(
0616   rtems_libio_t *iop,
0617   int status,
0618   size_t count,
0619   off_t new_offset
0620 )
0621 {
0622   /* Update offset and return */
0623   if ( status == 0 ) {
0624     iop->offset = new_offset;
0625     return count;
0626   } else {
0627     rtems_set_errno_and_return_minus_one( status );
0628   }
0629 }
0630 
0631 static int rtems_flashdev_ioctl_erase(
0632   rtems_flashdev *flash,
0633   rtems_libio_t *iop,
0634   void *arg
0635 )
0636 {
0637   rtems_flashdev_region *erase_args_1;
0638   off_t new_offset;
0639   int status;
0640 
0641   if ( flash->erase == NULL ) {
0642     return 0;
0643   }
0644 
0645   erase_args_1 = (rtems_flashdev_region *) arg;
0646   /* Check erasing valid region */
0647   new_offset = erase_args_1->offset;
0648   status = rtems_flashdev_get_abs_addr(flash, iop, erase_args_1->size, &new_offset);
0649   if ( status < 0 ) {
0650     return status;
0651   }
0652 
0653   /* Erase flash */
0654   status = ( *flash->erase )( flash, new_offset, erase_args_1->size );
0655   return status;
0656 }
0657 
0658 static int rtems_flashdev_ioctl_set_region(
0659   rtems_flashdev *flash,
0660   rtems_libio_t *iop,
0661   void *arg
0662 )
0663 {
0664   rtems_flashdev_region *region_in;
0665   rtems_flashdev_region_table *table = flash->region_table;
0666   region_in = (rtems_flashdev_region *) arg;
0667 
0668   if (flash->region_table == NULL) {
0669     rtems_set_errno_and_return_minus_one( ENOMEM );
0670   }
0671 
0672   if ( !rtems_flashdev_is_region_defined( iop ) ) {
0673     if (
0674       rtems_flashdev_find_unallocated_region(table)
0675         == RTEMS_FLASHDEV_REGION_ALLOC_FULL
0676     )
0677     {
0678       /* New region to allocate and all regions allocated */
0679       rtems_set_errno_and_return_minus_one( ENOMEM );
0680     } else {
0681       /* New region to allocate and space to allocate region */
0682       return rtems_flashdev_ioctl_create_region( flash, iop, region_in );
0683     }
0684   } else {
0685     /* Updating existing region */
0686     return rtems_flashdev_ioctl_update_region( flash, iop, region_in );
0687   }
0688 
0689 }
0690 
0691 static int rtems_flashdev_ioctl_create_region(
0692   rtems_flashdev *flash,
0693   rtems_libio_t *iop,
0694   rtems_flashdev_region *region_in
0695 )
0696 {
0697   int i;
0698   rtems_flashdev_region_table *table = flash->region_table;
0699 
0700   /* Find unallocated region slot */
0701   i = rtems_flashdev_find_unallocated_region(flash->region_table);
0702   if (i == RTEMS_FLASHDEV_REGION_ALLOC_FULL) {
0703     rtems_set_errno_and_return_minus_one( ENOMEM );
0704   }
0705 
0706   /* Set region values */
0707   table->regions[ i ].offset = region_in->offset;
0708   table->regions[ i ].size = region_in->size;
0709 
0710   /* Set region as allocated and link iop */
0711   rtems_flashdev_set_region(flash->region_table, i);
0712   rtems_flashdev_set_region_index( iop, i );
0713 
0714   return 0;
0715 }
0716 
0717 static int rtems_flashdev_ioctl_update_region(
0718   rtems_flashdev *flash,
0719   rtems_libio_t *iop,
0720   rtems_flashdev_region *region_in
0721 )
0722 {
0723   uint32_t region_index = rtems_flashdev_get_region_index( iop );
0724   rtems_flashdev_region_table *table = flash->region_table;
0725 
0726   /**
0727    * If region index is larger then maximum region index or region
0728    * index at given index is undefined return an error.
0729    */
0730   if (
0731        ( region_index >= flash->region_table->max_regions ) ||
0732        ( rtems_flashdev_check_allocation( table, region_index ) == 0)
0733      )
0734   {
0735     rtems_set_errno_and_return_minus_one( EINVAL );
0736   }
0737 
0738   /* Set region values */
0739   table->regions[ region_index ].offset = region_in->offset;
0740   table->regions[ region_index ].size = region_in->size;
0741 
0742   return 0;
0743 }
0744 
0745 static int rtems_flashdev_ioctl_clear_region(
0746   rtems_flashdev *flash,
0747   rtems_libio_t *iop
0748 )
0749 {
0750   uint32_t region_index = rtems_flashdev_get_region_index( iop );
0751 
0752   if (flash->region_table == NULL) {
0753     rtems_set_errno_and_return_minus_one( ENOMEM );
0754   }
0755 
0756   /* Check region to clear */
0757   if ( region_index == RTEMS_FLASHDEV_REGION_UNDEFINED ) {
0758     rtems_set_errno_and_return_minus_one( EINVAL );
0759   }
0760 
0761   /* Clear region */
0762   rtems_flashdev_unset_region( flash->region_table, region_index );
0763   rtems_flashdev_set_region_index( iop, RTEMS_FLASHDEV_REGION_UNDEFINED );
0764   return 0;
0765 }
0766 
0767 static off_t rtems_flashdev_get_region_offset(
0768   rtems_flashdev *flash,
0769   rtems_libio_t *iop
0770 )
0771 {
0772   /* Region is already checked to be defined */
0773   assert( rtems_flashdev_get_region_index( iop ) != RTEMS_FLASHDEV_REGION_UNDEFINED );
0774   rtems_flashdev_region_table *table = flash->region_table;
0775   return table->regions[ rtems_flashdev_get_region_index( iop ) ].offset;
0776 }
0777 
0778 static size_t rtems_flashdev_get_region_size(
0779   rtems_flashdev *flash,
0780   rtems_libio_t *iop
0781 )
0782 {
0783   /* Region is already checked to be defined */
0784   assert( rtems_flashdev_get_region_index( iop ) != RTEMS_FLASHDEV_REGION_UNDEFINED );
0785   rtems_flashdev_region_table *table = flash->region_table;
0786   return table->regions[ rtems_flashdev_get_region_index( iop ) ].size;
0787 }
0788 
0789 static uint32_t rtems_flashdev_ioctl_jedec_id( rtems_flashdev *flash )
0790 {
0791   if ( flash->jedec_id == NULL ) {
0792     return 0;
0793   } else {
0794     return ( *flash->jedec_id )( flash );
0795   }
0796 }
0797 
0798 static uint32_t rtems_flashdev_ioctl_flash_type(
0799   rtems_flashdev *flash,
0800   void *arg
0801 )
0802 {
0803   rtems_flashdev_flash_type *type = (rtems_flashdev_flash_type*)arg;
0804   if ( flash->flash_type == NULL ) {
0805     return 0;
0806   } else {
0807     return ( *flash->flash_type )( flash, type );
0808   }
0809 }
0810 
0811 static int rtems_flashdev_ioctl_pageinfo_offset(
0812   rtems_flashdev *flash,
0813   void *arg
0814 )
0815 {
0816   rtems_flashdev_ioctl_page_info *page_info;
0817 
0818   if ( arg == NULL ) {
0819     rtems_set_errno_and_return_minus_one( EINVAL );
0820   }
0821   if ( flash->page_info_by_offset == NULL ) {
0822     return 0;
0823   } else {
0824     page_info = (rtems_flashdev_ioctl_page_info *) arg;
0825     return ( *flash->page_info_by_offset )( flash,
0826                                          page_info->location,
0827                                          &page_info->page_info.offset,
0828                                          &page_info->page_info.size );
0829   }
0830 }
0831 
0832 static int rtems_flashdev_ioctl_pageinfo_index( rtems_flashdev *flash,
0833                                                 void *arg )
0834 {
0835   rtems_flashdev_ioctl_page_info *page_info;
0836 
0837   if ( arg == NULL ) {
0838     rtems_set_errno_and_return_minus_one( EINVAL );
0839   }
0840   if ( flash->page_info_by_index == NULL ) {
0841     return 0;
0842   } else {
0843     page_info = (rtems_flashdev_ioctl_page_info *) arg;
0844     return ( *flash->page_info_by_index )( flash,
0845                                            page_info->location,
0846                                            &page_info->page_info.offset,
0847                                            &page_info->page_info.size );
0848   }
0849 }
0850 
0851 static int rtems_flashdev_ioctl_page_count( rtems_flashdev *flash, void *arg )
0852 {
0853   if ( arg == NULL ) {
0854     rtems_set_errno_and_return_minus_one( EINVAL );
0855   }
0856   if ( flash->page_count == NULL ) {
0857     return 0;
0858   } else {
0859     return ( *flash->page_count )( flash, ( (int *) arg ) );
0860   }
0861 }
0862 
0863 static int rtems_flashdev_ioctl_sectorinfo_offset(
0864   rtems_flashdev *flash,
0865   void *arg
0866 )
0867 {
0868   rtems_flashdev_ioctl_sector_info *sector_info;
0869 
0870   if ( arg == NULL ) {
0871     rtems_set_errno_and_return_minus_one( EINVAL );
0872   }
0873   if ( flash->sector_info_by_offset == NULL ) {
0874     return 0;
0875   } else {
0876     sector_info = (rtems_flashdev_ioctl_sector_info *) arg;
0877     return ( *flash->sector_info_by_offset )( flash,
0878                                          sector_info->location,
0879                                          &sector_info->sector_info.offset,
0880                                          &sector_info->sector_info.size );
0881   }
0882 }
0883 
0884 static int rtems_flashdev_ioctl_sector_count( rtems_flashdev *flash, void *arg )
0885 {
0886   if ( arg == NULL ) {
0887     rtems_set_errno_and_return_minus_one( EINVAL );
0888   }
0889   if ( flash->sector_count == NULL ) {
0890     return 0;
0891   } else {
0892     return ( *flash->sector_count )( flash, ( (int *) arg ) );
0893   }
0894 }
0895 
0896 static int rtems_flashdev_ioctl_write_block_size(
0897   rtems_flashdev *flash,
0898   void *arg
0899 )
0900 {
0901   if ( arg == NULL ) {
0902     rtems_set_errno_and_return_minus_one( EINVAL );
0903   }
0904   if ( flash->write_block_size == NULL ) {
0905     return 0;
0906   } else {
0907     return ( *flash->write_block_size )( flash, ( (size_t *) arg ) );
0908   }
0909 }
0910 
0911 static uint32_t rtems_flashdev_find_unallocated_region(
0912   rtems_flashdev_region_table *region_table
0913 )
0914 {
0915   int array_index = 0;
0916   int bit_index = 0;
0917   int shift;
0918 
0919   while ( bit_index < region_table->max_regions) {
0920     /* Get uint32_t holding the ith bit */
0921     array_index = bit_index / RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH;
0922     shift = bit_index % RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH;
0923 
0924     /* Check if region available in next BITALLOC_LENGTH regions */
0925     if (
0926         (shift == 0) &&
0927         (region_table->bit_allocator[ array_index ] == RTEMS_FLASHDEV_REGION_ALLOC_FULL)
0928     )
0929     {
0930       bit_index = bit_index + RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH;
0931       continue;
0932     }
0933 
0934     /* Check individual bit */
0935     if ( ! ( ( ( region_table->bit_allocator[ array_index ] ) >> shift ) & 1UL ) ) {
0936       return bit_index;
0937     }
0938 
0939     bit_index++;
0940   }
0941 
0942   return RTEMS_FLASHDEV_REGION_ALLOC_FULL;
0943 }
0944 
0945 static uint32_t rtems_flashdev_set_region(
0946   rtems_flashdev_region_table *region_table,
0947   int index
0948 )
0949 {
0950   int array_index = index / RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH;
0951   int shift = index % RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH;
0952 
0953   region_table->bit_allocator[ array_index ] |= 1UL << shift;
0954 
0955   return index;
0956 }
0957 
0958 static uint32_t rtems_flashdev_unset_region(
0959   rtems_flashdev_region_table *region_table,
0960   int index
0961 )
0962 {
0963   int array_index = index / RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH;
0964   int shift = index % RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH;
0965 
0966   region_table->bit_allocator[ array_index ] &= ~( 1UL << shift );
0967 
0968   return index;
0969 }
0970 
0971 static uint32_t rtems_flashdev_check_allocation(
0972   rtems_flashdev_region_table *region_table,
0973   int index
0974 )
0975 {
0976   int array_index = index / RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH;
0977   int shift = index%RTEMS_FLASHDEV_REGION_BITALLOC_LENGTH;
0978 
0979   return ( ( region_table->bit_allocator[ array_index ] >> shift ) & 1UL );
0980 }