/***************************************************************************
 * Copyright (c) 2024 Microsoft Corporation
 * Copyright (c) 2026-present Eclipse ThreadX contributors
 *
 * 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
 **************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** USBX Component                                                        */
/**                                                                       */
/**   Storage Class                                                       */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/


/* Include necessary system files.  */

#define UX_SOURCE_CODE

#include "ux_api.h"
#include "ux_host_class_storage.h"
#include "ux_host_stack.h"


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _ux_host_class_storage_media_mount                  PORTABLE C      */
/*                                                           6.1.9        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function tries to read the first sector on the media. It then  */
/*    determines if this is a partition sector or a boot sector. If the   */
/*    sector contains partition information, each partition is checked    */
/*    for a DOS aware partition and mounted by UX_MEDIA (default FileX).  */
/*                                                                        */
/*    If there is no partition sector or boot sector, we simply inform    */
/*    UX_MEDIA (default FileX) that a device is present and that its boot */
/*    sector  should be periodically read to see if the device is mounted.*/
/*    This mechanism applies to storage where the media can be removed    */
/*    (floppy, ZIP, flash/smart media readers).                           */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    storage                               Pointer to storage class      */
/*    sector                                Boot sector start             */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _ux_host_class_storage_media_mount    Mount media                   */
/*    _ux_host_class_storage_media_open     Open media                    */
/*    _ux_host_class_storage_partition_read Read partition                */
/*    _ux_host_class_storage_media_read     Read sectors of media         */
/*    _ux_host_class_storage_start_stop     Start the media               */
/*    _ux_host_class_storage_unit_ready_test                              */
/*                                          Test unit ready               */
/*    _ux_utility_memory_allocate           Allocate memory block         */
/*    _ux_utility_memory_free               Release memory block          */
/*    _ux_utility_short_get                 Get 16-bit value              */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Storage Class                                                       */
/*                                                                        */
/**************************************************************************/
UINT  _ux_host_class_storage_media_mount(UX_HOST_CLASS_STORAGE *storage, ULONG sector)
{

#if defined(UX_HOST_CLASS_STORAGE_NO_FILEX)
    UX_PARAMETER_NOT_USED(storage);
    UX_PARAMETER_NOT_USED(sector);
    return(UX_FUNCTION_NOT_SUPPORTED);
#else
UCHAR           *sector_memory;
UINT            status;

    /* If trace is enabled, insert this event into the trace buffer.  */
    UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_MEDIA_MOUNT, storage, sector, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)

#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT
    /* If the device is UFI, we need to start it.  */
    if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceProtocol == UX_HOST_CLASS_STORAGE_PROTOCOL_CBI)
    {

        /* Start the media.  */
        status =  _ux_host_class_storage_start_stop(storage, UX_HOST_CLASS_STORAGE_START_MEDIA);

        /* Check the status. If the device does not start, fail the operation.
           But we still return SUCCESS otherwise the storage thread has no chance to inspect this instance.  */
        if (status != UX_SUCCESS)
            return(UX_SUCCESS);
    }
#endif

    /* Obtain memory for reading the partition sector, we do not use the storage instance
       memory because this function is reentrant.  */
    sector_memory =  _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, storage -> ux_host_class_storage_sector_size);
    if (sector_memory == UX_NULL)
        return(UX_MEMORY_INSUFFICIENT);

    /* Check if the device is now ready.  */
    status =  _ux_host_class_storage_unit_ready_test(storage);

    /* Check the status. There may be a transport error or the device is not ready.  */
    if (status == UX_SUCCESS)

        /* Read the very first sector from the media.  */
        status =  _ux_host_class_storage_media_read(storage, sector, 1, sector_memory);

    /* Check the status. There may be a transport error or the device is not ready.  */
    if (status != UX_SUCCESS)
    {

        /* In any case, we free the sector memory.  */
        _ux_utility_memory_free(sector_memory);

        if (status == UX_HOST_CLASS_STORAGE_SENSE_ERROR)

            /* The media is not there, so we will try to check for it at the storage
               thread level every x seconds.  */
            return(UX_SUCCESS);

        /* Since there was a transport error or command error, we do not try to mount
           a fake media.  */
        return(status);
    }

    /* We need to examine this sector and determine if this is a partition sector
       or a boot sector.  */
    if (_ux_utility_short_get(sector_memory + 510) == UX_HOST_CLASS_STORAGE_PARTITION_SIGNATURE)
    {

        /* If we have a signature, we know it is a valid media and it may be formatted.
           If there is no signature, the media exist but maybe not formatted. Now determine
           if this is a boot sector. The boot sector has a special signature in the first
           couple of bytes.
           BUT !!! The first boot sector of an iPod is actually a partition, so we need to
           look further and see if the number of sector per FAT and the first partition are
           set to 0. This will indicate a partition sector. Though exFAT has no BIOS parameters
           block and should be excluded in this case.
           */
        if ((*sector_memory == 0xe9) || ((*sector_memory == 0xeb) && *(sector_memory + 2) == 0x90))
        {

            /* Check for fake boot sector.  */
            if (_ux_utility_short_get(sector_memory + 0x16) != 0x0 ||
                _ux_utility_long_get(sector_memory + 0x24) != 0x0 ||
                (*(sector_memory + 1) == 0x76) /* exFAT volume  */)
            {

                /* This is a boot sector signature.  */
                _ux_host_class_storage_media_open(storage, sector);
                _ux_utility_memory_free(sector_memory);
                return(UX_SUCCESS);
            }
        }

        _ux_host_class_storage_partition_read(storage, sector_memory, sector);
        _ux_utility_memory_free(sector_memory);
        return(UX_SUCCESS);
    }
    else
    {

        /* The drive does not contain a partition table or FAT MBR at LBA 0*/
        status =  UX_ERROR;
    }

    /* Free all resources used.  */
    _ux_utility_memory_free(sector_memory);

    /* Return completion status.  */
    return(status);
#endif /* !defined(UX_HOST_CLASS_STORAGE_NO_FILEX) */
}
