/***************************************************************************
 * Copyright (c) 2024 Microsoft Corporation
 *
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 *
 * SPDX-License-Identifier: MIT
 **************************************************************************/

/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** FileX Component                                                       */
/**                                                                       */
/**   RAM Disk Driver                                                     */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

/* Include necessary system files.  */

#include "fx_api.h"
#include "usbh_core.h"
#include "usbh_msc.h"

/* The RAM driver relies on the fx_media_format call to be made prior to
   the fx_media_open call. The following call will format the default
   32KB RAM drive, with a sector size of 128 bytes per sector.

        fx_media_format(&ram_disk,
                            _fx_usbh_msc_driver,    // Driver entry
                            NULL,                   // RAM disk memory pointer
                            media_memory,           // Media buffer pointer
                            sizeof(media_memory),   // Media buffer size
                            "MY_RAM_DISK",          // Volume Name
                            2,                      // Number of FATs
                            128,                    // Directory Entries
                            0,                      // Hidden sectors
                            256,                    // Total sectors
                            128,                    // Sector size
                            1,                      // Sectors per cluster
                            1,                      // Heads
                            1);                     // Sectors per track

 */

VOID _fx_usbh_msc_driver(FX_MEDIA *media_ptr);

/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _fx_ram_driver                                      PORTABLE C      */
/*                                                           6.1.5        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    William E. Lamie, Microsoft Corporation                             */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function is the entry point to the generic RAM disk driver     */
/*    that is delivered with all versions of FileX. The format of the     */
/*    RAM disk is easily modified by calling fx_media_format prior        */
/*    to opening the media.                                               */
/*                                                                        */
/*    This driver also serves as a template for developing FileX drivers  */
/*    for actual devices. Simply replace the read/write sector logic with */
/*    calls to read/write from the appropriate physical device            */
/*                                                                        */
/*    FileX RAM/FLASH structures look like the following:                 */
/*                                                                        */
/*          Physical Sector                 Contents                      */
/*                                                                        */
/*              0                       Boot record                       */
/*              1                       FAT Area Start                    */
/*              +FAT Sectors            Root Directory Start              */
/*              +Directory Sectors      Data Sector Start                 */
/*                                                                        */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    media_ptr                             Media control block pointer   */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    None                                                                */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _fx_utility_memory_copy               Copy sector memory            */
/*    _fx_utility_16_unsigned_read          Read 16-bit unsigned          */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    FileX System Functions                                              */
/*                                                                        */
/*  RELEASE HISTORY                                                       */
/*                                                                        */
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
/*  09-30-2020     William E. Lamie         Modified comment(s),          */
/*                                            resulting in version 6.1    */
/*  03-02-2021     William E. Lamie         Modified comment(s),          */
/*                                            resulting in version 6.1.5  */
/*                                                                        */
/**************************************************************************/
VOID _fx_usbh_msc_driver(FX_MEDIA *media_ptr)
{
    struct usbh_msc *msc_class;
    int ret;

    /* There are several useful/important pieces of information contained in
       the media structure, some of which are supplied by FileX and others
       are for the driver to setup. The following is a summary of the
       necessary FX_MEDIA structure members:

            FX_MEDIA Member                    Meaning

        fx_media_driver_request             FileX request type. Valid requests from
                                            FileX are as follows:

                                                    FX_DRIVER_READ
                                                    FX_DRIVER_WRITE
                                                    FX_DRIVER_FLUSH
                                                    FX_DRIVER_ABORT
                                                    FX_DRIVER_INIT
                                                    FX_DRIVER_BOOT_READ
                                                    FX_DRIVER_RELEASE_SECTORS
                                                    FX_DRIVER_BOOT_WRITE
                                                    FX_DRIVER_UNINIT

        fx_media_driver_status              This value is RETURNED by the driver.
                                            If the operation is successful, this
                                            field should be set to FX_SUCCESS for
                                            before returning. Otherwise, if an
                                            error occurred, this field should be
                                            set to FX_IO_ERROR.

        fx_media_driver_buffer              Pointer to buffer to read or write
                                            sector data. This is supplied by
                                            FileX.

        fx_media_driver_logical_sector      Logical sector FileX is requesting.

        fx_media_driver_sectors             Number of sectors FileX is requesting.


       The following is a summary of the optional FX_MEDIA structure members:

            FX_MEDIA Member                              Meaning

        fx_media_driver_info                Pointer to any additional information
                                            or memory. This is optional for the
                                            driver use and is setup from the
                                            fx_media_open call. The RAM disk uses
                                            this pointer for the RAM disk memory
                                            itself.

        fx_media_driver_write_protect       The DRIVER sets this to FX_TRUE when
                                            media is write protected. This is
                                            typically done in initialization,
                                            but can be done anytime.

        fx_media_driver_free_sector_update  The DRIVER sets this to FX_TRUE when
                                            it needs to know when clusters are
                                            released. This is important for FLASH
                                            wear-leveling drivers.

        fx_media_driver_system_write        FileX sets this flag to FX_TRUE if the
                                            sector being written is a system sector,
                                            e.g., a boot, FAT, or directory sector.
                                            The driver may choose to use this to
                                            initiate error recovery logic for greater
                                            fault tolerance.

        fx_media_driver_data_sector_read    FileX sets this flag to FX_TRUE if the
                                            sector(s) being read are file data sectors,
                                            i.e., NOT system sectors.

        fx_media_driver_sector_type         FileX sets this variable to the specific
                                            type of sector being read or written. The
                                            following sector types are identified:

                                                    FX_UNKNOWN_SECTOR
                                                    FX_BOOT_SECTOR
                                                    FX_FAT_SECTOR
                                                    FX_DIRECTORY_SECTOR
                                                    FX_DATA_SECTOR
     */

    /* Process the driver request specified in the media control block.  */
    switch (media_ptr->fx_media_driver_request) {
    case FX_DRIVER_READ: {
        msc_class = (struct usbh_msc *)media_ptr->fx_media_driver_info;

        ret = usbh_msc_scsi_read10(msc_class, media_ptr->fx_media_driver_logical_sector + media_ptr->fx_media_hidden_sectors, media_ptr->fx_media_driver_buffer,
                                   media_ptr->fx_media_driver_sectors);

        if (ret < 0) {
            media_ptr->fx_media_driver_status = FX_IO_ERROR;
            return;
        }
        /* Successful driver request.  */
        media_ptr->fx_media_driver_status = FX_SUCCESS;
        break;
    }

    case FX_DRIVER_WRITE: {
        msc_class = (struct usbh_msc *)media_ptr->fx_media_driver_info;

        ret = usbh_msc_scsi_write10(msc_class, media_ptr->fx_media_driver_logical_sector + media_ptr->fx_media_hidden_sectors,
                                    media_ptr->fx_media_driver_buffer, media_ptr->fx_media_driver_sectors);
        if (ret < 0) {
            media_ptr->fx_media_driver_status = FX_IO_ERROR;
            return;
        }
        /* Successful driver request.  */
        media_ptr->fx_media_driver_status = FX_SUCCESS;
        break;
    }

    case FX_DRIVER_FLUSH: {
        /* Return driver success.  */
        media_ptr->fx_media_driver_status = FX_SUCCESS;
        break;
    }

    case FX_DRIVER_ABORT: {
        /* Return driver success.  */
        media_ptr->fx_media_driver_status = FX_SUCCESS;
        break;
    }

    case FX_DRIVER_INIT: {
        msc_class = usbh_find_class_instance(media_ptr->fx_media_name);
        if (!msc_class) {
            USB_LOG_ERR("No instance found for %s", media_ptr->fx_media_name);
            media_ptr->fx_media_driver_status = FX_MEDIA_INVALID;
            return;
        }

        if (usbh_msc_scsi_init(msc_class) < 0) {
            media_ptr->fx_media_driver_status = FX_MEDIA_INVALID;
            return;
        }

        media_ptr->fx_media_driver_info = msc_class;
        /* Successful driver request.  */
        media_ptr->fx_media_driver_status = FX_SUCCESS;
        break;
    }

    case FX_DRIVER_UNINIT: {
        /* There is nothing to do in this case for the RAM driver.  For actual
           devices some shutdown processing may be necessary.  */

        /* Successful driver request.  */
        media_ptr->fx_media_driver_status = FX_SUCCESS;
        break;
    }

    case FX_DRIVER_BOOT_READ: {
        msc_class = (struct usbh_msc *)media_ptr->fx_media_driver_info;

        ret = usbh_msc_scsi_read10(msc_class, 0, media_ptr->fx_media_driver_buffer, 1);
        if (ret < 0) {
            media_ptr->fx_media_driver_status = FX_IO_ERROR;
            return;
        }

        /* Successful driver request.  */
        media_ptr->fx_media_driver_status = FX_SUCCESS;
        break;
    }

    case FX_DRIVER_BOOT_WRITE: {
        msc_class = (struct usbh_msc *)media_ptr->fx_media_driver_info;

        ret = usbh_msc_scsi_write10(msc_class, 0, media_ptr->fx_media_driver_buffer, 1);
        if (ret < 0) {
            media_ptr->fx_media_driver_status = FX_IO_ERROR;
            return;
        }
        /* Successful driver request.  */
        media_ptr->fx_media_driver_status = FX_SUCCESS;
        break;
    }

    default: {
        /* Invalid driver request.  */
        media_ptr->fx_media_driver_status = FX_IO_ERROR;
        break;
    }
    }
}
