杰理 SDK 文档中心
首页
首页
  • 项目概览

    • AD23N SDK 概述与芯片平台
    • 工程结构与模块划分
  • 快速开始

    • 开发环境搭建与工具链
    • 编译构建指南
    • 烧录与固件升级工具
  • 应用框架与产品工作流

    • 应用入口与模式调度
    • 音乐播放应用
    • MIDI 解码与键盘演奏
    • 录音应用
    • LINEIN 与扩音应用
    • USB 从设备应用
    • 待机、软关机与空闲检测
    • 公共 UI 与 LED 显示
  • 音频子系统

    • 音频解码器框架
    • 音频编码器框架
    • 音效算法库
    • 音频管理与输出通路
  • 存储与文件系统

    • 文件系统层
    • NOR Flash 与虚拟机存储
    • 设备与设备管理
  • 系统服务与运行时

    • 消息机制与事件分发
    • 按键扫描与输入处理
    • 电源管理与低功耗控制
    • 定时器与系统任务
  • 外设驱动与平台

    • CPU 平台与启动流程
    • USB 协议栈与主机/设备驱动
    • SPI 与通用外设接口
  • 固件升级与构建工具

    • 固件升级机制
    • 编译后处理与镜像打包
    • 构建系统与命令行工具

设备与设备管理

本文档介绍 AD23N 固件 SDK 中「设备」抽象与「设备管理」机制:从底层驱动接口(dev_io_t)、中层设备注册与操作(dev_node / device / dev_* API)到应用层设备管理器(device_mge)的完整分层结构、工作流程、配置与故障处理。

Purpose and Scope

本页覆盖 AD23N SDK 中**设备(Device)**相关的完整机制:

  • 底层设备驱动接口 dev_io_t(见 device_drive.h)
  • 设备抽象层:dev_node、device_operations、device 结构与 dev_open/dev_close/dev_ioctl/dev_byte_read/dev_bulk_read 等标准 API(见 device.h)
  • 应用层设备管理器 device_mge:设备号枚举、引用计数式开关、在线状态轮询、UFW 升级入口(见 device_mge.h 与 device_mge.c)

不在本页范围:具体文件系统挂载(如 norfs/胖文件系统,属于存储/文件系统目录)、具体外设驱动实现(SD 卡控制器、USB Host 等)、升级协议细节(update.h)。这些属于同级目录下其他页面。

Overview

AD23N SDK 中「设备」指系统可访问的存储或外设单元,典型设备包括:

设备名宏实际名字符串说明
__UDISK0"udisk0"U 盘(USB Host)
__SD0_NANE"sd0"SD/TF 卡
__SFC_NANE"sfc"内置 SPI Flash 控制器(可读写)
__EXT_FLASH_NANE"ext_flsh"外挂 Flash(可读写)
__OTG"otg"USB OTG

这些宏定义于 device.h。

设备体系分为三层,职责边界清晰:

  1. 驱动层(device_drive.h):定义 dev_io_t,提供 mount/unmount/read/write/ioctrl/power/detect 原语,以及设备类型、状态、错误码、通用 ioctl 命令。每个硬件驱动按此接口实现。
  2. 抽象层(device.h):定义 dev_node(节点,含名字与操作集)与 device(实例,含引用计数),通过 REGISTER_DEVICE 宏把节点放入 .device 段完成静态注册,向上提供 dev_open/dev_close/dev_ioctl/dev_byte_read/dev_bulk_read 等统一 API。
  3. 应用层(device_mge):面向业务代码的设备管理器。把设备映射为整数设备号(UDISK_INDEX、SD0_INDEX、INNER_FLASH_RO、INNER_FLASH_RW、EXT_FLASH_RW),提供引用计数式 device_open/device_close、在线状态查询 device_status/device_online,以及设备升级入口 device_update。

设计意图:业务代码只需要和设备号打交道,不关心底层是 SD 卡还是 Flash;驱动层与抽象层解耦后,新增一种存储介质只需实现 dev_io_t 并注册 dev_node,上层无需改动。

Architecture

flowchart TD
    subgraph sg_App["应用层 (mbox_flash/common)"]
        App["业务代码 / 文件系统 / 升级模块"]
        Mge["device_mge 设备管理器<br/>device_open / device_close / device_status / device_online / device_update"]
        MgeTbl["device_mge[MAX_DEVICE]<br/>引用计数 + 句柄缓存"]
    end

    subgraph sg_Dev["抽象层 (dev_mg)"]
        DevApi["dev_open / dev_close / dev_ioctl<br/>dev_byte_read / dev_byte_write / dev_bulk_read / dev_bulk_write"]
        DevNode["dev_node (name + ops)<br/>REGISTER_DEVICE 静态注册到 .device 段"]
        DevInst["device 实例<br/>atomic ref + ops + driver_data"]
    end

    subgraph sg_Drv["驱动层 (device)"]
        DrvIo["dev_io_t<br/>mount / read / write / ioctrl / power / detect"]
        SdDrv["sd0 驱动"]
        SfcDrv["sfc / ext_flsh 驱动"]
        UdiskDrv["udisk0 / otg 驱动"]
    end

    App -->|"设备号 device_id"| Mge
    Mge -->|"dev_open(name)"| DevApi
    Mge --> MgeTbl
    DevApi --> DevNode
    DevNode -->|"ops->open / ops->ioctl / ops->read ..."| DevInst
    DevInst -->|"调用驱动回调"| DrvIo
    DrvIo --> SdDrv
    DrvIo --> SfcDrv
    DrvIo --> UdiskDrv

    Mge -.->|"device_update: ufw 升级检查"| Update["update / dev_update_check"]

架构要点:

  • device_mge 是应用层唯一入口:业务代码通过 device_open(INNER_FLASH_RW) 这类调用获得设备句柄,句柄与引用计数缓存在静态数组 device_mge[MAX_DEVICE] 中。
  • dev_* API 是抽象层门面:dev_open 按名字在 .device 段注册表中查找 dev_node,调用其 ops->open 创建 device 实例。
  • 驱动只实现 dev_io_t:dev_node 的 ops 回调(如 ioctl、bulk_read)内部再分发到 dev_io_t 的方法,形成两层适配。
  • 升级功能独立接入:device_update 通过 dev_update_check 检查/执行 UFW 升级,与设备开关管理解耦。

设备抽象层:dev_node / device / device_operations

核心数据结构

抽象层定义于 device.h:

struct device_operations {
    bool (*online)(const struct dev_node *node);
    int (*init)(const struct dev_node *node, void *);
    int (*open)(const char *name, struct device **device, void *arg);
    int (*read)(struct device *device, void *buf, u32 len, u32);
    int (*write)(struct device *device, void *buf, u32 len, u32);
    int (*bulk_read)(struct device *device, void *buf, u32 len, u32);
    int (*bulk_write)(struct device *device, void *buf, u32 len, u32);
    int (*seek)(struct device *device, u32 offset, int orig);
    int (*ioctl)(struct device *device, u32 cmd, u32 arg);
    int (*close)(struct device *device);
};

struct dev_node {
    const char *name;
    const struct device_operations *ops;
    void *priv_data;
};

struct device {
    atomic_t ref;
    void *private_data;
    const struct device_operations *ops;
    void *platform_data;
    void *driver_data;
};

设计要点:

  • dev_node 是「设备节点」:静态描述一个设备——名字 + 操作集 + 私有数据。它不持有运行期状态,可被多个实例共享。
  • device 是「设备实例」:每次 open 生成的句柄对象,ref 是原子引用计数,driver_data 存放驱动私有运行期数据(如驱动上下文、缓冲区指针)。
  • device_operations 是全部操作契约:online 探测、init 初始化、open/close 生命周期、read/write 字节流、bulk_read/bulk_write 块读写(按扇区)、seek 定位、ioctl 控制命令。这一契约同时服务字符型设备(字节读写)与块设备(扇区读写)。

静态注册机制

节点通过段属性宏注册到 .device 链接段:

#define REGISTER_DEVICE(node) \
    const struct dev_node node sec_used(.device)

#define REGISTER_DEVICES(node) \
    const struct dev_node node[] sec_used(.device)

Source: device.h

REGISTER_DEVICE 注册单个节点,REGISTER_DEVICES 注册节点数组(如一个驱动暴露多个逻辑设备)。链接器将所有 .device 段中的节点聚合成设备注册表,devices_init() 在启动阶段扫描并初始化它们——这种「零初始化代码」的注册方式,使新增驱动只需要在任意源文件里写一个 REGISTER_DEVICE 宏即可挂载到系统。

对外 API

int devices_init();
bool dev_online(const char *name);
void *dev_open(const char *name, void *arg);
int dev_ioctl(void *device, int cmd, u32 arg);
int dev_close(void *device);
int dev_byte_read(void *_device, void *buf, u32 offset, u32 len);
int dev_byte_write(void *_device, void *buf, u32 offset, u32 len);
int dev_bulk_read(void *_device, void *buf, u32 sector, u32 sector_num);
int dev_bulk_write(void *_device, void *buf, u32 sector, u32 sector_num);

Source: device.h

  • dev_open(name, arg):按名字查注册表,返回 device 句柄(void* 形式),失败返回 NULL。
  • dev_byte_read/byte_write:按字节偏移读写,适合小数据量、地址对齐不敏感的场景(如读取 Flash 配置区)。
  • dev_bulk_read/bulk_write:按扇区号 + 扇区数读写,适合大块数据传输(如文件系统缓存刷盘、音频数据流)。
  • dev_ioctl(device, cmd, arg):控制命令通道,命令码定义见驱动层 DEV_GET_STATUS 等。
  • dev_close:释放句柄,内部递减引用计数,计数归零才真正销毁驱动实例。

应用层设备管理器:device_mge

设备号枚举

设备管理器把设备映射为整数 ID,定义于 device_mge.h:

enum {
    UDISK_INDEX = 0,        // U 盘
    SD0_INDEX,              // SD/TF 卡
    INNER_FLASH_RO,         // 只读内置 flash,用于读取资源文件
    INNER_FLASH_RW,         // 可读写内置 flash,用于挂载 norfs 或访问虚拟设备
#if EXT_FLASH_EN
    EXT_FLASH_RW,           // 可读写外挂 flash
#endif
    MAX_DEVICE,
    NO_DEVICE = 0xff,
};

设计意图:用枚举而非字符串做业务接口,编译期即可发现非法设备号,且 device_mge[MAX_DEVICE] 数组天然与枚举一一对应。NO_DEVICE = 0xff 作为哨兵值表示"无设备"。INNER_FLASH_RO 在多数场景下不真正打开(见下文),只作为"资源文件只读区"的占位。EXT_FLASH_RW 受编译宏 EXT_FLASH_EN 控制,未使能时直接不参与数组与枚举计数。

设备名映射与句柄表

const char *device_name[MAX_DEVICE] = {
    __UDISK0,
    __SD0_NANE,
    NULL,               // 只读内置 flash,用于读取资源文件
    __SFC_NANE,         // 可读写内置 flash,用于挂载 norfs 或访问虚拟设备
#if EXT_FLASH_EN
    __EXT_FLASH_NANE,   // 可读写外挂 flash
#endif
};

typedef struct __device_mge_t {
    void *p_device;          // 已打开的设备句柄
    u8 device_used_cnt;      // 该设备的引用计数
} device_mge_t;
static device_mge_t device_mge[MAX_DEVICE];

Source: device_mge.c

注意 INNER_FLASH_RO 的名字为 NULL——它在设备名表中没有对应名字,因为该槽位不允许被 dev_open 打开(device_open 对 INNER_FLASH_RO 直接返回 NULL),其在线状态由 device_online 强制置位。这样设计是为了让资源文件读取路径(只读区)永远"在线可用",不依赖任何热插拔探测。

引用计数式开关设备

device_open 与 device_close 实现了「共享句柄 + 引用计数」:

void *device_open(u8 device_id)
{
    if (device_id >= MAX_DEVICE) {
        return NULL;
    }
    if (INNER_FLASH_RO == device_id) {
        return NULL;
    }
    device_mge[device_id].p_device = dev_open((void *)device_name[device_id], 0);
    if (NULL == device_mge[device_id].p_device) {
        return NULL;
    }
    device_mge[device_id].device_used_cnt++;
    return device_mge[device_id].p_device;
}

Source: device_mge.c

u32 device_close(u8 device_id)
{
    if ((device_id >= MAX_DEVICE) || (0 == device_mge[device_id].device_used_cnt)) {
        return 0;
    }
    if (INNER_FLASH_RO == device_id) {
        return 0;
    }
    u32 res = 0;
    device_mge[device_id].device_used_cnt--;
    if (0 == device_mge[device_id].device_used_cnt) {
        u32 retry = 20;
        do {
            res = dev_close(device_mge[device_id].p_device);
            if (0 == res) {
                device_mge[device_id].p_device = NULL;
            }
            retry--;
        } while (res && (0 != retry));
    }
    if (0 != res) {
        log_info("close_device FAIL\n");
        return 0;
    }
    return 1;
}

Source: device_mge.c

关键行为:

  • 引用计数保证多模块共享安全:文件系统、升级模块、应用可能同时使用同一设备。计数未归零时 device_close 只减计数、不真正关闭,避免"我关了别人还在用"的悬垂句柄。
  • 关闭失败重试 20 次:dev_close 失败(如设备忙)时循环重试,最多 20 次;若最终失败返回 0 且打印 close_device FAIL。注意此时计数已减、句柄未置空,属于"延迟释放"路径,需要上层配合重试或强制复位。
  • 返回值语义:0 = 未关闭/关闭失败,1 = 关闭成功。

在线状态查询

device_status(index, mode) 是设备管理器最核心的查询接口:

u32 device_status(u32 index, bool mode)
{
    if (index >= MAX_DEVICE) {
        return E_IDEV_ILL;
    }
    if (INNER_FLASH_RO == index) {
        return 0;
    }
    bool lost = 0;
    if (!dev_online((void *)&device_name[index][0])) {
        log_info("Ask device:%d is't online ", index);
        if (0 != mode) {
            device_close(index);
            lost = 1;
        }
    } else {
        log_info("device:%d status is ok\n", index);
        return 0;
    }
    bool bres = dev_online(&device_name[index][0]);
    if (bres) {
        if (lost) {
            return E_DEV_LOST;
        } else {
            return 0;
        }
    } else {
        return E_DEV_OFFLINE;
    }
}

Source: device_mge.c

  • mode == 0:纯查询,不产生副作用。
  • mode != 0:发现离线时先调用 device_close(index) 回收句柄(防止句柄泄漏),标记 lost。
  • 返回值:0 在线;E_DEV_LOST 设备曾在线但本次掉线且已被回收;E_DEV_OFFLINE 确认离线;E_IDEV_ILL 非法设备号。
  • device_online() 汇总所有设备状态为位图(每设备占 1 bit),INNER_FLASH_RO 恒为在线——这是上层判断"当前有哪些可用存储"的快捷接口:
u32 device_online(void)
{
    u32 online = 0;
    for (u32 i = 0; i < MAX_DEVICE; i++) {
        if (INNER_FLASH_RO == i) {
            online |= BIT(i);
            continue;
        }
        if (E_DEV_OFFLINE != device_status(i, 1)) {
            online |= BIT(i);
        }
    }
    return online;
}

Source: device_mge.c

驱动层接口:dev_io_t

设备驱动操作结构

驱动层契约定义于 device_drive.h:

typedef struct DEV_IO {
    const char name[8];
    s32(*mount)(void *volatile parm);
    s32(*unmount)();
    s32(*read)(u8 *volatile buf, u32 addr, u32 len);
    s32(*write)(u8 *volatile buf, u32 addr, u32 len);
    s32(*ioctrl)(void *volatile parm, u32 cmd);
    s32(*power)(u32 mod);
    s32(*detect)();                       // 设备状态检测
    struct dev_mutex *mutex;              // 驱动互斥锁
    dev_type device_type;                 // 设备类型
    void *private_data;                   // 设备私有属性
} dev_io_t;
  • mount/unmount:设备挂载/卸载(如 SD 卡初始化、文件系统底层对接)。
  • read/write:按地址读写。
  • ioctrl:控制命令分发,命令码为 DEV_GET_STATUS 等通用命令。
  • power:电源状态管理(开/关/待机/唤醒,见 DEV_POWER_*)。
  • detect:热插拔检测钩子,配合应用层的 device_status_emit 上报事件。
  • mutex:驱动内互斥锁指针,串行化并发访问,属于驱动层的并发控制手段。

设备类型 / 状态 / 错误码

typedef enum _dev_type {
    DEV_SDCRAD_0 = 0X10, DEV_SDCRAD_1, DEV_SDCRAD_2,
    DEV_UDISK_H0, DEV_UDISK_H1, DEV_UDISK_F0,
    DEV_NOR_FLASH, DEV_NAND_FLASH,
    DEV_STORAGE = 0x100, DEV_LOGIC_DISK = 0x101, DEV_USB_SLAVE, DEV_USB_HOST,
    DEV_HID = 0x200, DEV_NET, DEV_AUDIO, DEV_ISP,
} dev_type;

Source: device_drive.h

  • 设备类型分段:0x10 段为存储介质(SD、U 盘、Flash),0x100 段为逻辑设备(存储抽象、逻辑盘、USB 角色),0x200 段为外设类(HID、网络、音频、ISP)。这种分段便于按类型做能力探测与策略分发。
typedef enum dev_sta {
    DEV_OFFLINE  = 0,   // 设备从在线切换到离线
    DEV_ONLINE = 1,     // 设备从离线切换到在线
    DEV_HOLD = 2,       // 设备状态未改变
    DEV_POWER_ON = 0x10, // 开机
    DEV_POWER_OFF,       // 关机
    DEV_POWER_STANDBY,   // 待机
    DEV_POWER_WAKEUP,    // 唤醒
} DEV_STA;

Source: device_drive.h

DEV_STA 把「在线性」与「电源状态」合并到一个枚举:低 4 位描述在线/离线迁移,0x10 以上描述电源管理事件。上层通过 DEV_GET_STATUS ioctl 获取这些状态,驱动返回 DEV_HOLD 表示无变化,避免无谓的事件风暴。

通用 ioctl 命令与错误码

#define DEV_GENERAL_MAGIC  0xe0
#define DEV_GET_STATUS      _IOR(DEV_GENERAL_MAGIC,0xe0,u32)  // 获取设备状态
#define DEV_GET_BLOCK_SIZE  _IOR(DEV_GENERAL_MAGIC,0xe1,u32)  // 块大小
#define DEV_GET_BLOCK_NUM   _IOR(DEV_GENERAL_MAGIC,0xe2,u32)  // 块总数
#define DEV_GET_DEV_ID      _IOR(DEV_GENERAL_MAGIC,0xe3,u32)  // 设备 ID(SD/TF 返回 "sdtf"=0x73647466)
#define DEV_SECTOR_ERASE    _IOW(DEV_GENERAL_MAGIC,0xe4,u32)  // 页擦除
#define DEV_BLOCK_ERASE     _IOW(DEV_GENERAL_MAGIC,0xe5,u32)  // 块擦除
#define DEV_CHIP_ERASE      _IOW(DEV_GENERAL_MAGIC,0xe6,u32)  // 整片擦除
#define DEV_GET_TYPE        _IOR(DEV_GENERAL_MAGIC,0xe7,u32)  // 返回 dev_type
#define DEV_CHECK_WPSTA     _IOR(DEV_GENERAL_MAGIC,0xe8,u32)  // 写保护状态检测/设置

Source: device_drive.h

  • 命令码使用 Linux 风格 _IOR/_IOW 宏编码(魔数 + 序号 + 类型),DEV_GET_STATUS 是每个设备必须支持的命令;其余命令设备不支持时返回 -ENOTTY。
  • DEV_GET_DEV_ID 返回设备标识:SD/TF 卡返回 "sdtf"(0x73647466),用于区分介质品牌/类型。
  • DEV_CHECK_WPSTA:参数 -1 查询写保护状态,0 解除写保护,1 加写保护。
  • 错误码 DEV_ERR_* 覆盖 NOT_MOUNT、TIMEOUT、OFFLINE、CRC、STALL、NO_READ/NO_WRITE/NO_IOCTL、INVALID_HANDLE 等场景(见 device_drive.h),为上层提供细粒度诊断信息。

Core Flow

设备打开 → 读写 → 关闭全流程

sequenceDiagram
    participant App as 业务代码
    participant Mge as device_mge
    participant Dev as dev_* 抽象层
    participant Drv as dev_io_t 驱动

    App->>Mge: device_open(INNER_FLASH_RW / SD0_INDEX)
    Mge->>Mge: 校验 device_id & 查 device_name[]
    Mge->>Dev: dev_open("sfc"/"sd0", 0)
    Dev->>Dev: 在 .device 段注册表查找 dev_node
    Dev->>Drv: ops->open → 驱动初始化
    Drv-->>Dev: 返回 device 实例
    Dev-->>Mge: 句柄
    Mge->>Mge: device_used_cnt++ 并缓存句柄
    Mge-->>App: 返回句柄

    App->>Mge: device_obj(index) 取句柄
    App->>Dev: dev_bulk_read/sector / dev_byte_read/offset
    Dev->>Drv: ops->bulk_read / ops->read
    Drv-->>Dev: 数据
    Dev-->>App: 数据

    App->>Mge: device_status(index, mode=1)
    Mge->>Dev: dev_online(name)
    Dev-->>Mge: 在线/离线
    alt 离线
        Mge->>Mge: device_close(index) 回收句柄
        Mge-->>App: E_DEV_LOST / E_DEV_OFFLINE
    else 在线
        Mge-->>App: 0
    end

    App->>Mge: device_close(SD0_INDEX)
    Mge->>Mge: device_used_cnt--
    alt 计数归零
        Mge->>Dev: dev_close(handle) 失败重试≤20次
        Dev->>Drv: ops->close → 驱动释放
    end
    Mge-->>App: 1(成功) / 0(失败)

SD 卡热插拔事件流

SD 驱动通过 device_status_emit 把在线/离线转换为系统事件:

int device_status_emit(const char *device_name, const u8 status)
{
    log_info("device_name:%s status:%d \n", device_name, status);
    if (!strcmp(device_name, "sd0")) {
        if (status) {
            post_event(EVENT_SD0_IN);      // SD 插入
        } else {
            post_event(EVENT_SD0_OUT);     // SD 拔出
        }
    }
    return 0;
}

Source: device_mge.c

EVENT_SD0_IN/EVENT_SD0_OUT 进入系统消息队列后,由事件处理任务通知文件系统卸载/挂载、刷新 UI 显示等。注意该回调以 TFG_SD_EN 编译开关保护,仅在使能 SD 卡功能时编译;配套的 sdx_dev_get_cache_buf() 返回一块 512 字节静态缓冲区供 SD 驱动使用,避免在中断/回调上下文分配内存。

数据模型与持久化行为

设备层本身不持久化业务数据,但它是所有存储读写的必经通道。其"数据模型"可概括为三层句柄链:

erDiagram
    DEVICE_MGE ||--|| DEVICE_MGE_T : "设备号槽位(MAX_DEVICE)"
    DEVICE_MGE_T {
        u8 device_id
        void p_device
        u8 device_used_cnt
    }
    DEVICE_MGE_T ||--o| DEVICE : "持有句柄"
    DEVICE {
        atomic_t ref
        void private_data
        void platform_data
        void driver_data
    }
    DEVICE ||--|| DEV_NODE : "引用节点"
    DEV_NODE {
        string name
        ops ops
        void priv_data
    }
    DEV_NODE ||--|| DEV_IO : "驱动实现 ops"
    DEV_IO {
        string name_8
        func mount
        func read
        func write
        func ioctrl
        func power
        func detect
        dev_type device_type
    }

持久化行为要点:

  • 块设备(SD/Flash):上层文件系统通过 dev_bulk_read/dev_bulk_write 以扇区为单位访问;DEV_GET_BLOCK_SIZE/DEV_GET_BLOCK_NUM 提供几何信息。
  • 字符型访问(虚拟设备/资源区):dev_byte_read/dev_byte_write 按字节偏移直接访问,典型用于读取配置区、资源文件头等。
  • 擦除命令:DEV_SECTOR_ERASE/DEV_BLOCK_ERASE/DEV_CHIP_ERASE 通过 ioctl 下发,上层(如 norfs)按需调用,Flash 驱动需实现磨损均衡与写保护(DEV_CHECK_WPSTA)。

Configuration Options

配置项类型默认/取值说明
EXT_FLASH_EN编译宏0/1是否使能外挂 Flash 设备;为 1 时 EXT_FLASH_RW 加入设备号枚举、设备名表与句柄表
TFG_SD_EN编译宏0/1是否使能 SD 卡;为 1 时编译 sdx_dev_get_cache_buf、device_status_emit 与 SD 热插拔事件上报
TFG_DEV_UPGRADE_SUPPORT编译宏0/1是否使能 device_update 设备升级入口
INNER_FLASH_RO 槽位固定逻辑恒在线、不可 open只读资源区占位,device_name[] 中为 NULL,device_open 返回 NULL,device_online 恒置位
__SFC_NANE / __SD0_NANE / __UDISK0 / __EXT_FLASH_NANE宏定义"sfc"/"sd0"/"udisk0"/"ext_flsh"设备名字符串,必须与驱动 dev_node.name 一致
device_close 重试次数常量20dev_close 失败时的最大重试次数
sd_buffer静态缓冲512 字节SD 驱动缓存缓冲区(TFG_SD_EN 时编译)

API Reference

设备管理器(应用层,device_mge.h)

void *device_open(u8 device_id)

  • 描述:按设备号打开设备并返回句柄,引用计数 +1。
  • 参数:device_id 为 UDISK_INDEX/SD0_INDEX/INNER_FLASH_RW/EXT_FLASH_RW。
  • 返回:成功返回设备句柄;device_id >= MAX_DEVICE、INNER_FLASH_RO 或底层 dev_open 失败时返回 NULL。

u32 device_close(u8 device_id)

  • 描述:释放设备引用,计数归零时真正关闭(失败重试 20 次)。
  • 返回:1 成功;0 非法设备号 / 计数已为 0 / 关闭失败。

void *device_obj(u32 index)

  • 描述:获取指定设备号当前缓存的句柄(不增加引用)。
  • 返回:句柄;index >= MAX_DEVICE 或 INNER_FLASH_RO 返回 0。

u32 device_status(u32 index, bool mode)

  • 描述:查询设备在线状态;mode != 0 时离线自动回收句柄。
  • 返回:0 在线;E_DEV_LOST 掉线且已回收;E_DEV_OFFLINE 离线;E_IDEV_ILL 非法设备号。

u32 device_online(void)

  • 描述:汇总所有设备在线状态为位图(bit i = 设备 i 在线,INNER_FLASH_RO 恒在线)。

u32 device_update(char *t_dev_name, bool check_flag)

  • 描述:设备升级入口。check_flag == 1 仅检查 UFW 文件并记录设备;0 执行升级。t_dev_name == NULL 时沿用上次检查成功的设备。
  • 返回:升级错误码,UPDATA_READY 表示就绪,其余为出错(UPDATA_DEV_ERR 等)。
  • 注意:"udisk0"(U 盘)暂不支持升级,直接返回 UPDATA_DEV_ERR。

抽象层(device.h)

void *dev_open(const char *name, void *arg)

  • 描述:按名字在 .device 段注册表中查找并打开设备。
  • 返回:device 句柄(void*),失败 NULL。

int dev_close(void *device)

  • 描述:关闭设备实例;返回 0 成功,非 0 失败(上层据此重试)。

int dev_ioctl(void *device, int cmd, u32 arg)

  • 描述:下发控制命令(DEV_GET_STATUS、DEV_GET_BLOCK_SIZE、DEV_CHIP_ERASE 等)。

int dev_byte_read/write(void *_device, void *buf, u32 offset, u32 len)

  • 描述:按字节偏移读写;返回实际处理结果码。

int dev_bulk_read/write(void *_device, void *buf, u32 sector, u32 sector_num)

  • 描述:按扇区批量读写;sector 为起始扇区号,sector_num 为扇区数。

bool dev_online(const char *name)

  • 描述:探测设备是否在线(调用节点 ops->online)。

驱动层(device_drive.h)

s32 (*ioctrl)(void *parm, u32 cmd):驱动命令分发,cmd 为 DEV_GENERAL_MAGIC 系列命令;不支持的返回 -ENOTTY。

s32 (*detect)():设备状态检测,返回 DEV_STA 中的在线性状态。

s32 (*power)(u32 mod):电源管理,mod 取 DEV_POWER_ON/OFF/STANDBY/WAKEUP。

Failure Modes, Edge Cases & Concurrency

  • 设备离线与句柄回收:device_status(index, 1) 在设备掉线时先 device_close 回收句柄,避免旧句柄悬垂;返回 E_DEV_LOST(曾在线)与 E_DEV_OFFLINE(本就离线)区分两种语义,上层可据此决定是否提示用户重新插拔。device_online() 内部对每个槽位都做一次带回收的状态查询,因此会引入逐设备的 dev_online 探测开销。
  • device_close 失败重试:dev_close 可能因设备忙/驱动未就绪失败,循环重试 20 次。若仍失败,计数已减但句柄保留,后续 device_open 会复用该句柄(p_device 非空且计数递增),属于"软失败"路径,需上层注意状态一致性。
  • INNER_FLASH_RO 的特殊性:该槽位名字为 NULL、不可 open/close、恒在线。任何代码若误将 INNER_FLASH_RO 传入 device_open/device_close 会得到 NULL/0 的失败结果而非崩溃——这是防御性设计。
  • 并发访问:引用计数 device_used_cnt 为非原子 u8,同一设备号若被多任务并发 open/close 存在计数竞争风险;SDK 的典型用法是文件系统任务与升级任务串行化访问设备。驱动层提供 dev_mutex 保护底层并发,抽象层 device.ref 为 atomic_t。需要跨任务并发共享设备时,上层必须自行加锁。
  • U 盘升级限制:device_update 对 udisk0 直接报错,避免在 U 盘上执行固件升级的不安全路径。
  • SD 热插拔竞态:device_status_emit 在驱动上下文中投递 EVENT_SD0_IN/OUT 事件;若事件处理任务尚未完成挂载而用户再次插拔,可能出现重复挂载/卸载,依赖消息队列的顺序性缓解。

Usage Examples

示例 1:打开内置 Flash 并批量读写

void *dev = device_open(INNER_FLASH_RW);      // 打开可读写内置 flash
if (dev) {
    u8 buf[512];
    // 按扇区读写(挂载 norfs 前通常用 bulk 接口)
    dev_bulk_read(dev, buf, 0, 1);            // 读第 0 扇区
    dev_bulk_write(dev, buf, 0, 1);           // 写回第 0 扇区
    device_close(INNER_FLASH_RW);
}

说明:device_open 的引用计数语义保证文件系统与业务模块可安全共享同一设备;读写接口直接来自 device.h,开关流程来自 device_mge.c。

示例 2:轮询设备在线位图并处理掉线

u32 online = device_online();                 // 返回位图,bit0=udisk0, bit1=sd0, bit3=sfc ...
if (online & BIT(SD0_INDEX)) {
    // SD 卡在线,可挂载文件系统
}
// 对单个设备做带回收的状态查询:
u32 st = device_status(SD0_INDEX, 1);         // 掉线时自动 device_close
if (st == E_DEV_LOST) {
    // 设备曾在线、现已掉线且句柄已回收,提示用户重新插拔
} else if (st == E_DEV_OFFLINE) {
    // 设备未插入
}

说明:device_status 的 mode=1 语义(掉线自动回收)是避免句柄泄漏的关键;位图聚合逻辑见 device_mge.c。

示例 3:驱动注册一个设备节点

static const struct device_operations mydev_ops = {
    .online  = mydev_online,
    .init    = mydev_init,
    .open    = mydev_open,
    .read    = mydev_read,
    .write   = mydev_write,
    .bulk_read  = mydev_bulk_read,
    .bulk_write = mydev_bulk_write,
    .seek    = mydev_seek,
    .ioctl   = mydev_ioctl,
    .close   = mydev_close,
};
REGISTER_DEVICE(my_dev_node) = {
    .name = "mydev",
    .ops  = &mydev_ops,
};

说明:REGISTER_DEVICE 把节点放入 .device 段,devices_init() 启动时自动扫描;新增设备无需改动任何业务代码。宏定义见 device.h。

示例 4:设备升级检查与执行

// 仅检查升级设备上是否存在 ufw 文件
u32 err = device_update("sd0", 1);
if (err == UPDATA_READY) {
    // 设备已就绪,稍后执行升级
    err = device_update(NULL, 0);   // t_dev_name 传 NULL 沿用上次设备
}

说明:device_update 封装 dev_update_check,check_flag 区分检查/执行两个阶段;"udisk0" 不支持升级。实现见 device_mge.c。

Performance & Operational Notes

  • 热路径:文件系统 IO 经过 dev_bulk_read/write → ops->bulk_read/write → dev_io_t.read/write,两层函数指针调用开销极小;块传输避免逐字节调用,适合音频/资源流。
  • 状态轮询成本:device_online() 对每个槽位执行 dev_online 探测,频繁调用会放大探测开销;建议由事件驱动(EVENT_SD0_IN/OUT)配合低频轮询。
  • 内存:SD 驱动使用 512 字节静态缓冲(sd_buffer),避免热插拔回调中的动态分配;驱动私有数据通过 device.driver_data 持有,生命周期与 device 实例一致。
  • 关闭重试:dev_close 失败时 20 次重试可能阻塞调用方;上层应避免在中断上下文调用 device_close。

Extension Points

  1. 新增设备介质:实现 dev_io_t(mount/read/write/ioctrl/power/detect + dev_type),再用 REGISTER_DEVICE 注册 dev_node;如需暴露给业务层,在 device_mge 的枚举与 device_name[] 表中增加槽位(受 EXT_FLASH_EN 等宏控制)。
  2. 新设备类型:扩展 dev_type 枚举段(存储类 0x10、逻辑类 0x100、外设类 0x200),并实现对应 ioctl 命令。
  3. 热插拔事件:在驱动 detect/状态变化处调用 device_status_emit 上报 EVENT_*,事件语义与消息队列由系统消息模块统一处理。
  4. 升级设备:通过 TFG_DEV_UPGRADE_SUPPORT 使能 device_update,新设备接入升级链路只需保证可被 dev_open 打开并承载 ufw 文件。
  5. 只读资源区:INNER_FLASH_RO 槽位是预留的只读资源挂载点,应用可通过扩展 device_name[] 中的 NULL 槽位为其绑定具体资源设备。

Tests

当前仓库未发现针对 device_mge 的独立单元测试文件;其正确性主要依赖运行期验证路径:

  • 启动初始化:devices_init() 扫描 .device 段并初始化所有注册节点。
  • 引用计数验证:多模块交替 device_open/device_close 场景(文件系统 + 升级)。
  • 热插拔验证:EVENT_SD0_IN/OUT 事件流(TFG_SD_EN 使能时)。
  • 升级链路验证:device_update 检查/执行 UFW(TFG_DEV_UPGRADE_SUPPORT 使能时)。

注:源材料中未见测试文件;如需补充测试策略,可基于上述运行期路径设计。

Related Links

  • device.h — 设备抽象层定义
  • device_drive.h — 驱动层接口与错误码
  • device_mge.h — 设备管理器接口
  • device_mge.c — 设备管理器实现
  • music_device.c — 音乐播放场景的设备封装
  • device_list.c — 启动阶段设备清单
Prev
NOR Flash 与虚拟机存储