第 5 章 - Azure RTOS FileX 的 I/O 驱动程序

本章介绍 Azure RTOS FileX 的 I/O 驱动程序,旨在帮助开发人员编写应用程序特定的驱动程序。

I/O 驱动程序简介

FileX 支持多个媒体设备。 FX_MEDIA 结构定义管理媒体设备所需的一切。 此结构包含所有媒体信息,其中包括用于在驱动程序与 FileX 之间传递信息和状态的媒体特定 I/O 驱动程序及关联参数。 在大多数系统中,每个 FileX 媒体实例都有唯一的 I/O 驱动程序。

I/O 驱动程序入口

每个 FileX I/O 驱动程序具有单个入口函数,该函数由 fx_media_open 服务调用定义。 驱动程序入口函数的格式如下:

void my_driver_entry(FX_MEDIA *media_ptr);

FileX 调用 I/O 驱动程序入口函数来请求所有物理媒体访问权限,包括初始化和启动扇区读取权限。 向驱动程序发出的请求是按顺序进行的;即,FileX 会先等待当前请求完成,然后再发送另一个请求。

I/O 驱动程序请求

由于每个 I/O 驱动程序都具有单个入口函数,因此 FileX 会通过媒体控制块发出特定的请求。 具体而言,FX_MEDIA 的 fx_media_driver_request 成员用于指定确切的驱动程序请求。 I/O 驱动程序通过 FX_MEDIA 的 fx_media_driver_status 成员来传达请求的成功或失败结果 。 如果驱动程序请求成功,则在驱动程序返回之前,会将 FX_SUCCESS 放入此字段。 否则,如果检测到错误,则将 FX_IO_ERROR 放入此字段。

驱动程序初始化

尽管实际的驱动程序初始化处理特定于应用程序,但这种处理通常包括数据结构初始化,并可能包括某种预备性的硬件初始化。 此请求首先由 FileX 发出,在 fx_media_open 服务内部完成。

如果检测到媒体写入保护,则应将 FX_MEDIA 的驱动程序 fx_media_driver_write_protect 成员设置为 FX_TRUE。

以下 FX_MEDIA 成员用于 I/O 驱动程序初始化请求:

FX_MEDIA 成员 含义
fx_media_driver_request FX_DRIVER_INIT

FileX 提供一种机制,用于在扇区不再使用时通知应用程序驱动程序。 对于必须管理已映射到闪存的所有使用中逻辑扇区的闪存管理器,此机制特别有用。

如果需要此类可用扇区通知,应用程序驱动程序只需将关联的 FX_MEDIA 结构中的 fx_media_driver_free_sector_update 字段设置为 FX_TRUE 即可 。 设置后,FileX 会发出 FX_DRIVER_RELEASE_SECTORS I/O 驱动程序调用,指示一个或多个连续扇区何时可用。

启动扇区读取

FileX 发出特定的请求来读取媒体的启动扇区,而不是使用标准读取请求。 以下 FX_MEDIA 成员用于 I/O 驱动程序启动扇区读取请求:

FX_MEDIA 成员 含义
fx_media_driver_request FX_DRIVER_BOOT_READ
fx_media_driver_buffer 启动扇区目标的地址。

启动扇区写入

FileX 发出特定的请求来写入媒体的启动扇区,而不是使用标准写入请求。 以下 FX_MEDIA 成员用于 I/O 驱动程序启动扇区写入请求:

FX_MEDIA 成员 含义
fx_media_driver_request FX_DRIVER_BOOT_WRITE
fx_media_driver_buffer 启动扇区源的地址。

扇区读取

FileX 通过向 I/O 驱动程序发出读取请求,将一个或多个扇区读入内存。 以下 FX_MEDIA 成员用于 I/O 驱动程序读取请求:

FX_MEDIA 成员 含义
fx_media_driver_request FX_DRIVER_READ
fx_media_driver_logical_sector 要读取的逻辑扇区
fx_media_driver_sectors 要读取的扇区数
fx_media_driver_buffer 要读取的扇区的目标缓冲区
fx_media_driver_data_sector_read 如果请求了文件数据扇区,则设置为 FX_TRUE。 否则,如果请求了系统扇区(FAT 或目录扇区),则设置为 FX_FALSE。
fx_media_driver_sector_type 定义所请求扇区的显式类型,如下所示:
FX_FAT_SECTOR (2)
FX_DIRECTORY_SECTOR (3)
FX_DATA_SECTOR (4)

扇区写入

FileX 通过向 I/O 驱动程序发出写入请求,将一个或多个扇区写入物理媒体。 以下 FX_MEDIA 成员用于 I/O 驱动程序写入请求:

FX_MEDIA 成员 含义
fx_media_driver_request FX_DRIVER_WRITE
fx_media_driver_logical_sector 要写入的逻辑扇区
fx_media_driver_sectors 要写入的扇区数
fx_media_driver_buffer 要写入的扇区的源缓冲区
fx_media_driver_system_write 如果请求了系统扇区(FAT 或目录扇区),则设置为 FX_TRUE。 否则,如果请求了文件数据扇区,则设置为 FX_FALSE。
fx_media_driver_sector_type 定义所请求扇区的显式类型,如下所示:

FX_FAT_SECTOR (2)
FX_DIRECTORY_SECTOR (3)
FX_DATA_SECTOR (4)。

驱动程序刷新

FileX 通过向 I/O 驱动程序发出刷新请求,将当前位于驱动程序扇区缓存中的所有扇区刷新到物理媒体。 当然,如果驱动程序不缓存扇区,则此请求就不需要进行驱动程序处理。 以下 FX_MEDIA 成员用于 I/O 驱动程序刷新请求:

FX_MEDIA 成员 含义
fx_media_driver_request FX_DRIVER_FLUSH

驱动程序中止

FileX 通过向 I/O 驱动程序发出中止请求,通知驱动程序中止物理媒体中所有进一步的物理 I/O 活动。 在驱动程序重新初始化之前,它不应再次执行任何 I/O。 以下 FX_MEDIA 成员用于 I/O 驱动程序中止请求:

FX_MEDIA 成员 含义
fx_media_driver_request FX_DRIVER_ABORT

释放扇区

如果驱动程序以前在初始化期间已选择此设置,则每当有一个或多个连续扇区可用时,FileX 就会通知驱动程序。 如果驱动程序实际上是闪存管理器,则可以使用此信息告知闪存管理器不再需要这些扇区。 以下 FX_MEDIA 成员用于 I/O 释放扇区请求:

FX_MEDIA 成员 含义
fx_media_driver_request FX_DRIVER_RELEASE_SECTORS
fx_media_driver_logical_sector 可用扇区的起始位置
fx_media_driver_sectors 可用扇区数

驱动程序挂起

由于物理媒体中的 I/O 可能需要一段时间,因此经常需要挂起调用线程。 当然,此操作假设基础 I/O 操作的完成是由中断驱动的。 如果是这样,则使用 ThreadX 信号灯即可轻松实现线程挂起。 开始输入或输出操作后,I/O 驱动程序会在收到其自己的内部 I/O 信号灯(在驱动程序初始化期间创建,初始计数为零)时挂起。 在驱动程序 I/O 完成中断处理过程中,将设置相同的 I/O 信号灯,从而唤醒已挂起的线程。

扇区转换

由于 FileX 将媒体视为线性逻辑扇区,因此向 I/O 驱动程序发出的 I/O 请求是对逻辑扇区发出的。 驱动程序负责在媒体的逻辑扇区与物理几何结构(这可能包括磁头、磁轨和物理扇区)之间进行转换。 对于闪存和 RAM 磁盘媒体,逻辑扇区通常将目录映射到物理扇区。 对于任何情况,都可以使用以下典型公式在 I/O 驱动程序中执行逻辑扇区到物理扇区的映射:

media_ptr -> fx_media_driver_physical_sector =
    (media_ptr -> fx_media_driver_logical_sector % media_ptr -> fx_media_sectors_per_track) + 1;

media_ptr -> fx_media_driver_physical_head =
    (media_ptr -> fx_media_driver_logical_sector/ media_ptr ->
    fx_media_sectors_per_track) % media_ptr -> fx_media_heads;

media_ptr -> fx_media_driver_physical_track =(media_ptr ->
    fx_media_driver_logical_sector/ (media_ptr -> fx_media_sectors_per_track *
    media_ptr -> fx_media_heads)));

请注意,物理扇区从 1 开始,而逻辑扇区从 0 开始。

隐藏的扇区

隐藏的扇区驻留在媒体上的启动记录之前。 由于它们实际上超出了 FAT 文件系统布局的范围,因此必须在驱动程序执行的每个逻辑扇区操作中考虑到它们。

媒体写入保护

FileX 驱动程序可以通过在媒体控制块中设置 fx_media_driver_write_protect 字段来启用写入保护。 如果发出任何 FileX 调用来试图写入该媒体,则会导致返回错误。

示例 RAM 驱动程序

FileX 演示系统随附了一个小型 RAM 磁盘驱动程序,该驱动程序在文件 fx_ram_driver.c 中定义。 该驱动程序假设有 32K 内存空间,并且会为 256 个 128 字节的扇区创建启动记录。 此文件提供了一个很好的示例用于演示如何实现应用程序特定的 FileX I/O 驱动程序。

/*FUNCTION: _fx_ram_driver
RELEASE: PORTABLE C 5.7
AUTHOR: William E. Lamie, Microsoft, Inc.
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
    12-12-2005     William E.     Lamie Initial Version 5.0
    07-18-2007     William E.     Lamie Modified comment(s),
                                resulting in version 5.1
    03-01-2009     William E.     Lamie Modified comment(s),
                                resulting in version 5.2
    11-01-2015     William E.     Lamie Modified comment(s),
                                resulting in version 5.3
    04-15-2016     William E.     Lamie Modified comment(s),
                                resulting in version 5.4
    04-03-2017     William E.     Lamie Modified comment(s),
                                fixed compiler warnings,
                                resulting in version 5.5
    12-01-2018     William E.     Lamie Modified comment(s),
                                checked buffer overflow,
                                resulting in version 5.6
    08-15-2019     William E.     Lamie Modified comment(s),
                                resulting in version 5.7
*/

VOID _fx_ram_driver(FX_MEDIA *media_ptr) { UCHAR *source_buffer;
                                           UCHAR *destination_buffer;
                                           UINT bytes_per_sector;

    /* 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: {

            /* Calculate the RAM disk sector offset. Note the RAM disk memory
            is pointed to by the fx_media_driver_info pointer, which is supplied
            by the application in the call to fx_media_open. */

            source_buffer = ((UCHAR *)media_ptr -> fx_media_driver_info) +
                ((media_ptr -> fx_media_driver_logical_sector +
                media_ptr -> fx_media_hidden_sectors) * media_ptr -> fx_media_bytes_per_sector);

            /* Copy the RAM sector into the destination. */

             _fx_utility_memory_copy(source_buffer,
                media_ptr -> fx_media_driver_buffer, media_ptr -> fx_media_driver_sectors *
                media_ptr -> fx_media_bytes_per_sector);

            /* Successful driver request. */

            media_ptr -> fx_media_driver_status = FX_SUCCESS; break; }

        case FX_DRIVER_WRITE: {

            /* Calculate the RAM disk sector offset. Note the RAM disk memory
                is pointed to by the fx_media_driver_info pointer, which is supplied
                by the application in the call to fx_media_open. */

            destination_buffer = ((UCHAR *)media_ptr -> fx_media_driver_info) +
                ((media_ptr -> fx_media_driver_logical_sector +
                media_ptr -> fx_media_hidden_sectors) * media_ptr -> fx_media_bytes_per_sector);

            /* Copy the source to the RAM sector. */

            _fx_utility_memory_copy(media_ptr -> fx_media_driver_buffer,
                destination_buffer, media_ptr -> fx_media_driver_sectors *
                media_ptr -> fx_media_bytes_per_sector);

            /* 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: {

            /* FLASH drivers are responsible for setting several fields
                in the media structure, as follows:
                media_ptr -> fx_media_driver_free_sector_update media_ptr ->
                fx_media_driver_write_protect
                The fx_media_driver_free_sector_update flag is used to instruct
                FileX to inform the driver whenever sectors are not being used.
                This is especially useful for FLASH managers so they don't have
                maintain mapping for sectors no longer in use.
                The fx_media_driver_write_protect flag can be set anytime by
                the driver to indicate the media is not writable. Write attempts
                made when this flag is set are returned as errors. */
            /* Perform basic initialization here... since the boot record is going
                to be read subsequently and again for volume name requests. */
            /* 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: {
            /* Read the boot record and return to the caller. */

            /* Calculate the RAM disk boot sector offset, which is at the
                very beginning of the RAM disk. Note the RAM disk memory is pointed
                to by the fx_media_driver_info pointer, which is supplied by the
                application in the call to fx_media_open. */

            source_buffer = (UCHAR *)media_ptr -> fx_media_driver_info;

            /* For RAM driver, determine if the boot record is valid. */

            if ((source_buffer[0] != (UCHAR)0xEB) ||

            ((source_buffer[1] != (UCHAR)0x34) &&

            (source_buffer[1] != (UCHAR)0x76)) || /* exFAT jump code. */

            (source_buffer[2] != (UCHAR)0x90)) {

            /* Invalid boot record, return an error! */
            media_ptr -> fx_media_driver_status = FX_MEDIA_INVALID; return; }

            /* For RAM disk only, pickup the bytes per sector. */

            bytes_per_sector =
                _fx_utility_16_unsigned_read(&source_buffer[FX_BYTES_SECTOR]); #ifdef FX_ENABLE_EXFAT

            /* if byte per sector is zero, then treat it as exFAT volume. */

            if (bytes_per_sector == 0 && (source_buffer[1] == (UCHAR)0x76)) {

            /* Pickup the byte per sector shift, and calculate byte per sector. */ 
            bytes_per_sector = (UINT)(1 << source_buffer[FX_EF_BYTE_PER_SECTOR_SHIFT]);

            }

            #endif /* FX_ENABLE_EXFAT */

            /* Ensure this is less than the media memory size. */
            if (bytes_per_sector \media_ptr -> fx_media_memory_size) {

            media_ptr -> fx_media_driver_status = FX_BUFFER_ERROR; break; }

            /* Copy the RAM boot sector into the destination. */

            _fx_utility_memory_copy(source_buffer, media_ptr -> fx_media_driver_buffer, bytes_per_sector);

            /* Successful driver request. */

            media_ptr -> fx_media_driver_status = FX_SUCCESS; break; }

        case FX_DRIVER_BOOT_WRITE: {

            /* Write the boot record and return to the caller. */

            /* Calculate the RAM disk boot sector offset, which is at the
                very beginning of the RAM disk. Note the RAM disk memory is
                pointed to by the fx_media_driver_info pointer, which is supplied
                by the application in the call to fx_media_open. */ 
            destination_buffer = (UCHAR *)media_ptr -> fx_media_driver_info;

            /* Copy the RAM boot sector into the destination. */

            _fx_utility_memory_copy(media_ptr -> fx_media_driver_buffer,
                destination_buffer, media_ptr -> fx_media_bytes_per_sector);

            /* 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;}
    }
}