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

    • SDK 简介与核心特性
    • 芯片平台与硬件资料
    • SDK 版本与发布信息
  • 快速开始

    • 环境搭建与工具链
    • 编译工程
    • 烧录与量产工具
  • 工程结构与构建系统

    • 工程目录布局
    • 构建与链接配置
  • 应用层开发

    • mbox_flash 应用框架
    • 板级支持包 (BSP)
    • 公共应用模块
    • UI 显示子系统
  • 蓝牙子系统

    • BLE 控制器、链路层与 HCI 传输
    • GATT 服务框架
    • BLE 应用示例:遥控器 / Dongle / 对讲机
    • 经典蓝牙支持
  • 音频子系统

    • 音频编解码器
    • 音频设备接口 (DAC / ADC / APA)
    • 音效处理与 EQ
    • 播放、录音与 MIO 工作流
  • 设备与文件系统

    • 存储设备驱动 (NorFlash / SDMMC / USB)
    • 文件系统 (FAT / nor_fs / SYDF)
    • 设备管理框架 (dev_mg)
  • 系统服务与电源管理

    • 消息机制 (msg / hot_msg)
    • 配置与参数存储 (app_config / VM)
    • 电源管理 (SOFT OFF / POWER DOWN)
  • 固件升级

    • 升级框架总览 (code_v1 / code_v2)
    • 双 Bank 升级机制
    • 升级通道:UART / 测试盒 / BLE OTA / USB / SD
  • 补丁包与版本维护

    • 版本升级补丁链 (v1.1.0 → v1.4.0)
    • 问题修复补丁
    • 固件裁剪与资源优化
  • 开发工具与支持

    • 辅助工具与脚本
    • 文档、配置说明与常见问题

设备管理框架 (dev_mg)

设备管理框架(dev_mg)是 AW30N BLE SDK 中面向嵌入式系统的统一设备抽象层:它通过"设备节点 + 操作函数表"的注册式模型,把 SFC Flash、SD 卡、OTG/U 盘等存储外设抽象为可按名称打开、读写、控制的标准设备,供文件系统、VM 存储、USB 协议栈等上层模块统一访问。

目的与范围

本页面向 dev_mg 框架本身,涵盖:

  • 设备模型的核心数据结构(dev_node、device、device_operations、dev_node_mg);
  • 链接器段(.device)驱动的设备注册机制(REGISTER_DEVICE / REGISTER_DEVICES);
  • 运行时设备管理 API(devices_init_api、dev_online、dev_open、dev_ioctl、dev_close、字节/扇区读写接口);
  • IOCTL 命令体系(设置类、查询类、擦除类、VM 信息类);
  • 配套的原子操作实现(dev_mg/atomic.h)。

框架的具体实现以预编译库形式提供(dev_mg_lib.a),本页依据公开头文件描述其对外契约与使用方式。以下主题属于兄弟页面,不在本页展开:文件系统与分区布局、USB Host 协议栈(usb_host.h / usb_storage.h 只是 dev_mg 的使用方)、VM 键值存储的实现细节、以及各具体外设驱动(SFC/SD/OTG)内部的时序。

概述

设计动机

在典型的 MCU 嵌入式固件中,上层模块(文件系统、录音、OTA、VM 存储)需要访问多种存储介质:片内 SPI Flash(sfc)、SD 卡(sd0)、加密 SD(sd_enc)、扩展 Flash(ext_flsh)、USB 大容量存储(udisk0)等。如果每个上层模块都直接面对驱动差异,代码将高度耦合且难以移植。

dev_mg 借鉴类 Linux 的"设备模型"思路,把差异收敛到两层契约:

  1. 注册期:每个设备驱动在编译期通过 REGISTER_DEVICE 宏把自己的 dev_node 放进链接器 .device 段,形成一张静态设备表;
  2. 运行期:上层通过设备名字符串查找节点(dev_online / dev_open),拿到 struct device 句柄后,只调用 device_operations 中的统一函数指针(read/write/ioctl/seek…),无需关心底层介质。

这种设计带来的直接收益是:新增一种存储介质只需"注册节点 + 实现 ops",上层代码零改动;同时设备句柄内置 atomic_t ref 引用计数,配合关中断实现的原子操作,保证多任务环境下打开/关闭/读写的并发安全。

位置与形态

dev_mg 的头文件位于 sdk/apps/include_lib/dev_mg/,实现以静态库形式预编译:

  • device.h:设备模型与全部 API 声明;
  • ioctl_cmds.h:IOCTL 命令号定义;
  • atomic.h:原子整数操作;
  • sdk/apps/include_lib/liba/bd49/mbox_flash/dev_mg_lib.a:BD49 平台 mbox_flash 工程的预编译实现。

它被 flash_wp.h、vm.h、usb_host.h、usb_storage.h 等模块广泛引用,是存储栈的公共底座。

关键概念

概念说明
设备节点 dev_node静态注册的"目录项":名字 + ops + 私有数据,编译期放入 .device 段
设备句柄 device打开后返回的运行时实例:带引用计数、平台数据、驱动数据
操作表 device_operations设备的能力契约:online/init/open/read/write/bulk_read/bulk_write/seek/ioctl/close
IOCTL 命令对设备进行参数配置、状态查询、擦除等控制的面板
原子操作 atomic_t基于关中断(CPU_CRITICAL_ENTER/EXIT)实现的多任务安全计数器

架构

flowchart TD
    subgraph sg_Upper["上层模块 (dev_mg 使用者)"]
        FS["文件系统 / FAT"]
        VM["VM 键值存储"]
        USB["USB Host 协议栈"]
        WP["flash_wp 写保护"]
    end

    subgraph sg_DevMg["dev_mg 设备管理框架"]
        API["devices_init_api / dev_open / dev_ioctl<br/>dev_byte_read / dev_bulk_read ..."]
        TABLE["设备注册表 (.device 段)<br/>device_node_begin[] ~ device_node_end[]"]
        OPS["struct device_operations<br/>read / write / ioctl / seek ..."]
        ATOMIC["atomic.h 原子操作<br/>ref 引用计数"]
    end

    subgraph sg_Driver["设备驱动层 (注册节点)"]
        SFC["sfc (SPI Flash)"]
        SD0["sd0 / sd_enc / sd_cdrom"]
        EXT["ext_flsh (扩展 Flash)"]
        OTG["otg / udisk0 (USB 存储)"]
    end

    FS --> API
    VM --> API
    USB --> API
    WP --> API
    API --> TABLE
    API --> ATOMIC
    TABLE --> OPS
    OPS --> SFC
    OPS --> SD0
    OPS --> EXT
    OPS --> OTG

架构说明:上层模块只与 dev_mg 的 API 交互;dev_open 依据名字在 .device 段注册表(device_node_begin ~ device_node_end)中线性查找节点,取出 device_operations 函数表;后续所有读写控制都经由该函数表分发到具体驱动(sfc/sd/ext_flsh/udisk0)。引用计数(atomic_t ref)贯穿句柄生命周期,保证并发打开/关闭安全。整个框架的实现被封装进 dev_mg_lib.a,对外只暴露头文件契约。

核心数据结构

设备操作表 struct device_operations

device.h 第 20-31 行 定义了设备驱动必须实现的能力集:

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);
};

Source: device.h

每个函数指针的含义与设计意图:

  • online(node):查询设备是否在线(如 SD 卡是否插入、U 盘是否枚举完成),供上层做热插拔检测;
  • init(node, arg):驱动级初始化,在 devices_init 阶段被逐个调用;
  • open(name, device, arg):打开设备并填充 struct device 实例(注意驱动层 open 输出的是句柄指针);
  • read / write:带偏移的字节级读写(u32 参数为偏移量);
  • bulk_read / bulk_write:扇区级批量读写(u32 参数为起始扇区/扇区数),对应文件系统按块访问的模式;
  • seek:移动读写位置,orig 语义类似标准 C 的 SEEK_SET/CUR/END;
  • ioctl(device, cmd, arg):命令面板入口,cmd 取 ioctl_cmds.h 中的命令号;
  • close(device):释放设备。

设备节点 struct dev_node 与句柄 struct device

device.h 第 33-45 行:

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

Source: device.h

  • dev_node 是静态注册表项,name 是全局查找键,priv_data 携带驱动私有配置(如寄存器基址、引脚配置);
  • device 是运行时句柄,ref(atomic_t)记录引用计数,platform_data 与 driver_data 供驱动保存上下文。dev_mg 的打开路径会把 dev_node.ops 复制/关联到句柄的 ops,使上层拿到句柄后即可直接分发调用。

注册表管理 struct dev_node_mg

device.h 第 47-53 行:

struct dev_node_mg {
    struct dev_node *device_node_begin;
    struct dev_node *device_node_end;
};

extern const struct dev_node device_node_begin[];
extern const struct dev_node device_node_end[];

Source: device.h

链接器为 .device 段生成 device_node_begin / device_node_end 两个边界符号,框架据此把"散落"在各驱动文件中的注册项收敛成一段连续内存数组。dev_node_mg 则允许运行时通过 set_device_node() 重新设定注册表范围(例如多分区/多镜像场景下切换设备表)。

内置设备名常量

device.h 第 8-15 行 预定义了框架内置设备的规范名称:

#define __SFC_NANE          "sfc"
#define __SD0_NANE          "sd0"
#define __SD_CDROM          "sd_cdrom"
#define __SD_ENC            "sd_enc"
#define __EXT_FLASH_NANE    "ext_flsh"
#define __OTG               "otg"
// #define __UDISK0            "udisk0"
#define __UDISK             "udisk0"

Source: device.h

这些宏是上层调用 dev_open("sfc", ...) 等接口时的推荐写法——使用宏而非裸字符串可以避免拼写错误,并让设备名变更时只改一处。

设备注册机制

编译期注册宏

device.h 第 55-59 行:

#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

设计要点:

  • sec_used(.device) 是链接器段属性,把变量放进 .device 段,并抑制"定义了未使用"的告警(used 属性);
  • REGISTER_DEVICE 注册单个节点,REGISTER_DEVICES 注册数组(例如 sd0/sd_cdrom/sd_enc 多个 SD 变体可一次性注册);
  • 注册是"零运行时代价"的:不消耗 RAM、不需要构造函数,只是把 const dev_node 布局进 Flash;
  • 多个驱动文件各自调用宏,链接器保证它们在 .device 段内按序排列,配合 device_node_begin/end 边界符号形成完整注册表。

初始化流程

device.h 第 61-62 行:

int devices_init(void);//该接口由devices_init_api()调用,不可单独使用
int devices_init_api(void);

Source: device.h

  • devices_init() 是内部实现函数,头文件注释明确要求不可单独调用,必须经由 devices_init_api() 进入;
  • devices_init_api() 是应用层入口:遍历 .device 段注册表,对每个节点调用 ops->init(node, priv_data) 完成驱动初始化,并建立后续查找所需的管理结构;
  • 这种"内部函数 + 公共 API"双层设计是为了防止调用次序错误——直接调用 devices_init 会跳过框架自身的准备工作。

运行时设备管理 API

device.h 第 63-72 行 声明了全部公共接口:

int devices_init_api(void);
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);
struct dev_node_mg *set_device_node(struct dev_node *node_start, struct dev_node *node_end);
int device_status_emit(const char *device_name, const u8 status);

Source: device.h

各接口职责:

接口职责
dev_online(name)按名查询设备是否在线(包装 ops->online),返回 bool
dev_open(name, arg)按名打开设备:查找节点 → 分配/初始化句柄(递增 ref)→ 返回 struct device *(以 void * 传递)
dev_ioctl(device, cmd, arg)命令面板:透传到 ops->ioctl,cmd 取 ioctl_cmds.h 命令号
dev_close(device)关闭设备:递减引用计数,计数归零时执行 ops->close
dev_byte_read/write字节级带偏移读写(offset/len),面向流式或小数据访问
dev_bulk_read/write扇区级批量读写(sector/sector_num),面向文件系统按块访问
set_device_node(start, end)运行时重设设备注册表范围,返回旧的 dev_node_mg *,用于多镜像/多设备表切换
device_status_emit(name, status)上报设备状态事件(如拔插/就绪),供框架或上层订阅处理

设计意图:void * 句柄传递是嵌入式 C 中常见的"不透明指针"手法——上层无需包含驱动私有头文件,只依赖 dev_mg 的公共契约;而 dev_byte_* 与 dev_bulk_* 的分离,则让流式访问(OTA、录音)与块式访问(文件系统)各取所需,不必为小数据量付出扇区对齐的代价。

IOCTL 命令体系

ioctl_cmds.h 把设备控制命令按功能分组编号,命令号即 dev_ioctl(device, cmd, arg) 的 cmd 参数。

设置类命令(1-48)

ioctl_cmds.h 第 5-45 行:

#define IOCTL_SET_IRQ_NUM               1
#define IOCTL_SET_PRIORITY              2
#define IOCTL_SET_DATA_WIDTH            3
#define IOCTL_SET_SPEED                 4
#define IOCTL_SET_DETECT_MODE           5
#define IOCTL_SET_DETECT_FUNC           6
#define IOCTL_SET_DETECT_TIME_INTERVAL  7
#define IOCTL_SET_PORT                  8
#define IOCTL_SET_PORT_FUNC             9
#define IOCTL_SET_CS_PORT_FUNC          10
#define IOCTL_SET_READ_MODE             11
#define IOCTL_SET_WRITE_MODE            12
#define IOCTL_SET_WRITE_PROTECT         13
#define IOCTL_SET_START_BIT             14
#define IOCTL_SET_STOP_BIT              15
#define IOCTL_FLUSH                     16
#define IOCTL_REGISTER_IRQ_HANDLER      17
#define IOCTL_UNREGISTER_IRQ_HANDLER    18
#define IOCTL_GET_SYS_TIME              19
#define IOCTL_SET_SYS_TIME              20
#define IOCTL_GET_ALARM                 21
#define IOCTL_SET_ALARM                 22
#define IOCTL_SET_CAP_LOWSPEED_CARD     23
#define IOCTL_SET_VDD50_EN              30
#define IOCTL_GET_WEEKDAY               32
#define IOCTL_CLR_READ_MODE             33
#define IOCTL_SET_READ_CRC              34
#define IOCTL_GET_READ_CRC              35
#define IOCTL_GET_VOLUME                36
#define IOCTL_SET_VOLUME                37
#define IOCTL_SET_ALARM_ENABLE          38
#define IOCTL_CMD_RESUME                39
#define IOCTL_CMD_SUSPEND               40
#define IOCTL_SET_BASE_ADDR             41
#define IOCTL_SET_ASYNC_MODE            42
#define IOCTL_SET_READ_USE_CACHE        43
#define IOCTL_SET_CACHE_SYNC_ISR_EN     44
#define IOCTL_SET_POWER_DOWN            45
#define IOCTL_RELEASE_POWER_DOWN        46
#define IOCTL_SET_PROTECT_INFO          47
#define IOCTL_SET_SFC_READ              48

Source: ioctl_cmds.h

查询类命令(100-116)

ioctl_cmds.h 第 47-63 行:

#define IOCTL_GET_ID                    100
#define IOCTL_GET_SECTOR_SIZE           101
#define IOCTL_GET_BLOCK_SIZE            102
#define IOCTL_GET_CAPACITY              103
#define IOCTL_GET_WIDTH                 104
#define IOCTL_GET_HEIGHT                105
#define IOCTL_GET_BLOCK_NUMBER          106
#define IOCTL_CHECK_WRITE_PROTECT       107
#define IOCTL_GET_STATUS                108
#define IOCTL_GET_TYPE                  109
#define IOCTL_GET_MAX_LUN               110
#define IOCTL_GET_CUR_LUN               111
#define IOCTL_SET_CUR_LUN               112
#define IOCTL_SET_FORCE_RESET           113
#define IOCTL_SET_CAPACITY              114
#define IOCTL_GET_NORFS_INFO            115
#define IOCTL_GET_PART_INFO             116

Source: ioctl_cmds.h

擦除与存储控制命令

ioctl_cmds.h 第 65-76 行:

#define IOCTL_ERASE_SECTOR              200
#define IOCTL_ERASE_BLOCK               201
#define IOCTL_ERASE_CHIP                202
#define IOCTL_SET_ENC_END               203
#define IOCTL_ERASE_PAGE                204

#define IOCTL_SET_VM_INFO               210
#define IOCTL_GET_VM_INFO               211

#define IOCTL_SET_DATA_CALLBACK         301

Source: ioctl_cmds.h

命令分组的设计意图:

  • 1-48 段(配置/控制):涵盖 SPI 时序参数(speed/width/port/CS)、检测机制(detect_mode/func/interval)、中断管理(IRQ handler 注册)、电源管理(power_down/vdd50/resume/suspend)、实时时钟(sys_time/alarm/weekday)等——这些命令多数由驱动初始化或系统策略层在启动早期调用;
  • 100-116 段(查询):容量、扇区/块大小、写保护、LUN 数、分区信息等只读几何信息——文件系统挂载前会先通过这些命令探测介质;
  • 200-204 段(擦除):Flash 类介质专属的擦除操作(sector/block/chip/page),IOCTL_SET_ENC_END 用于加密存储的结尾标记;
  • 210-211 段(VM 信息):与 VM 键值存储协作,读写 VM 分区元数据;
  • 301 段(数据回调):注册数据回调(如异步读写完成通知)。

注意命令号并非连续(如 23 之后直接跳到 30,24-29 保留),这是为后续扩展预留了编号空间,新增命令时不应占用已定义编号。

原子操作支持

dev_mg/atomic.h 提供轻量原子整数,是 struct device.ref 引用计数的基础。

atomic.h 第 6-23 行:

typedef struct {
    int counter;
} atomic_t;

static inline int atomic_add_return(int i, atomic_t *v)
{
    int val;
    CPU_SR_ALLOC();

    CPU_CRITICAL_ENTER();

    val = v->counter;
    v->counter = val += i;

    CPU_CRITICAL_EXIT();

    return val;
}

Source: atomic.h

实现要点:

  • 通过 CPU_CRITICAL_ENTER()/CPU_CRITICAL_EXIT() 关中断保护"读-改-写"序列,避免任务切换或中断打断造成计数丢失;
  • 配套宏在 atomic.h 第 56-73 行:DEFINE_ATOMIC、atomic_read、atomic_inc/dec、atomic_inc_and_test、atomic_dec_and_test、atomic_add_negative 等,语义对齐 Linux 内核原子操作 API;
  • 该头文件同时被 includes.h 全局引用,因此整个 SDK 的共享计数场景都可复用同一套原子语义。

核心流程

sequenceDiagram
    participant App as 应用 / 上层模块
    participant Mg as dev_mg 框架 (dev_mg_lib.a)
    participant Tbl as .device 段注册表
    participant Drv as 设备驱动 (sfc/sd0/udisk0)

    App->>Mg: devices_init_api()
    Mg->>Tbl: 遍历 device_node_begin ~ device_node_end
    Mg->>Drv: ops->init(node, priv_data)
    Drv-->>Mg: 初始化完成
    Mg-->>App: 0 (成功)

    App->>Mg: dev_online("sfc")
    Mg->>Drv: ops->online(node)
    Drv-->>Mg: true
    Mg-->>App: true

    App->>Mg: dev_open("sfc", arg)
    Mg->>Tbl: 按 name 查找 dev_node
    Mg->>Drv: ops->open(name, &device, arg)
    Mg->>Mg: ref++ (atomic_inc)
    Mg-->>App: struct device * (void *)

    App->>Mg: dev_ioctl(device, IOCTL_GET_CAPACITY, &cap)
    Mg->>Drv: ops->ioctl(device, cmd, arg)
    Drv-->>Mg: 容量数据
    Mg-->>App: 0

    App->>Mg: dev_bulk_read(device, buf, sector, num)
    Mg->>Drv: ops->bulk_read(device, buf, len, sector)
    Drv-->>Mg: 数据
    Mg-->>App: 读取字节数

    App->>Mg: dev_close(device)
    Mg->>Mg: ref-- (atomic_dec_and_test)
    Mg->>Drv: ops->close(device) (ref 归零时)
    Mg-->>App: 0

流程要点:

  1. 初始化阶段:devices_init_api() 扫描链接器段注册表,逐个执行 ops->init;此步骤必须早于任何 dev_open,否则注册表尚未就绪;
  2. 探测阶段:上层先用 dev_online(name) 做非阻塞在线检查(例如 SD 卡未插入时直接跳过挂载流程);
  3. 打开阶段:dev_open 在注册表中按名字查找到 dev_node 后,通过 ops->open 得到句柄并递增引用计数——同一个设备可被多个模块同时打开;
  4. 使用阶段:IOCTL 查询几何参数 → 字节/扇区读写;所有调用都经由统一 ops 函数表分发,上层不感知介质差异;
  5. 关闭阶段:dev_close 递减引用计数,仅当计数归零(最后一个使用者退出)时才真正执行 ops->close 释放资源,避免提前关闭导致其他使用者悬空。

使用示例

以下示例均从 dev_mg 公开头文件的实际内容中提取,展示上层如何与框架协作。

示例 1:注册一个设备节点

驱动侧在编译期把节点放入 .device 段。单个设备与设备数组两种写法(框架内置 sfc_dev_ops 即通过该机制暴露):

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

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

extern const struct device_operations sfc_dev_ops;

Source: device.h

驱动实现侧通常写作(示意,基于宏契约):

static const struct dev_node sfc_node = {
    .name = __SFC_NANE,            // "sfc"
    .ops  = &sfc_dev_ops,          // 指向框架导出的操作表
};
REGISTER_DEVICE(sfc_node);

示例 2:按名称打开并查询设备

上层打开设备并查询介质几何参数——这是文件系统挂载前的典型动作:

void *dev = dev_open(__SFC_NANE, NULL);   // 打开 SPI Flash
if (!dev) {
    /* 设备不存在或打开失败 */
    return;
}

u32 capacity = 0;
if (dev_ioctl(dev, IOCTL_GET_CAPACITY, (u32)&capacity) != 0) {
    /* 查询失败 */
}

/* ... 使用 dev_byte_read / dev_bulk_read / dev_bulk_write ... */

dev_close(dev);                           // 释放引用

Source: device.h 与 ioctl_cmds.h

示例 3:扇区级批量读写

面向文件系统的块访问模式(sector 为起始扇区号,sector_num 为扇区数):

int ret = dev_bulk_read(dev, buf, sector_start, sector_count);
if (ret < 0) {
    /* 读失败,依据返回值做重试或报错 */
}

ret = dev_bulk_write(dev, buf, sector_start, sector_count);
if (ret < 0) {
    /* 写失败,注意 Flash 写前通常需先擦除 */
}

Source: device.h

示例 4:Flash 擦除与电源控制

通过 IOCTL 执行介质专属操作——擦除和低功耗控制无法用 read/write 表达,必须走命令面板:

dev_ioctl(dev, IOCTL_ERASE_SECTOR, sector_addr);   // 擦除单个扇区
dev_ioctl(dev, IOCTL_ERASE_CHIP,  0);              // 整片擦除(谨慎使用)
dev_ioctl(dev, IOCTL_SET_POWER_DOWN, 0);           // 进入低功耗
dev_ioctl(dev, IOCTL_RELEASE_POWER_DOWN, 0);       // 退出低功耗

Source: ioctl_cmds.h

配置选项

设备名称常量(上层必须与驱动保持一致)

宏字符串值对应介质说明
__SFC_NANE"sfc"片内 SPI Flash系统主存储,sfc_dev_ops 由框架导出
__SD0_NANE"sd0"SD 卡 0标准 SD/MMC 卡
__SD_CDROM"sd_cdrom"SD 只读镜像类似 CD-ROM 的只读 SD 场景
__SD_ENC"sd_enc"加密 SD 卡带加密逻辑的 SD
__EXT_FLASH_NANE"ext_flsh"外部扩展 Flash外挂 SPI Flash
__OTG"otg"OTG 控制器USB OTG 主机侧设备
__UDISK"udisk0"U 盘USB 大容量存储(__UDISK0 旧名已注释保留)

Source: device.h

IOCTL 命令号分段(配置面板的总览)

编号段分组典型用途
1-23, 30-48配置/控制SPI 时序、检测、IRQ、电源、RTC、CRC、缓存
100-116查询/几何ID、容量、扇区/块大小、LUN、分区、写保护
200-204擦除sector/block/chip/page 擦除、加密结尾
210-211VM 信息VM 分区元数据读写
301数据回调注册数据回调

API 参考

以下签名均取自 device.h 第 61-72 行。

int devices_init_api(void)

设备框架初始化入口:遍历 .device 段注册表并对每个节点执行 ops->init。

返回:0 表示成功;非 0 表示初始化失败(如某个驱动 init 出错)。

注意:devices_init() 为内部函数,头文件明确标注"该接口由 devices_init_api() 调用,不可单独使用"。

bool dev_online(const char *name)

按名称查询设备在线状态。

参数:name — 设备名(建议使用 __SFC_NANE 等宏)。

返回:true 在线;false 离线或设备不存在。

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

打开设备,返回不透明句柄。

参数:

  • name:设备名;
  • arg:打开参数(如访问模式、通道号),无参传 NULL。

返回:struct device * 句柄(以 void * 传递);失败返回 NULL。句柄内部引用计数已递增,使用完必须 dev_close。

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

向设备发送控制命令。

参数:

  • device:dev_open 返回的句柄;
  • cmd:命令号,取 ioctl_cmds.h 定义;
  • arg:命令参数(常为数据缓冲地址或数值,查询类命令通常传入结果缓冲区指针)。

返回:0 成功;非 0 失败(具体语义由驱动定义,如参数非法、介质忙)。

int dev_close(void *device)

关闭设备,递减引用计数;计数归零时执行驱动 ops->close 释放资源。

返回:0 成功;非 0 失败。

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

字节级读写,offset 为介质内字节偏移,len 为长度。

返回:读取/写入的字节数;负值表示错误。

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

扇区级批量读写,sector 为起始扇区号,sector_num 为扇区数量。文件系统块访问的标准路径。

返回:成功处理的扇区数或字节数;负值表示错误。

struct dev_node_mg *set_device_node(struct dev_node *node_start, struct dev_node *node_end)

运行时重设设备注册表范围(多镜像/多分区设备表切换)。

返回:旧的 dev_node_mg *,便于恢复现场。

int device_status_emit(const char *device_name, const u8 status)

上报设备状态事件(热插拔、就绪等),供框架或上层监听处理。

返回:0 成功;非 0 失败。

失败模式、边界情况与并发

打开失败与空句柄

dev_open 在两种典型情况下返回 NULL:注册表中不存在该名字的节点(名字拼写错误或驱动未注册),或驱动 ops->open 失败(介质未就绪、资源不足)。上层必须判空后再使用句柄,否则 dev_ioctl / dev_bulk_read 等接口会解引用空指针。建议一律使用 __SFC_NANE 等宏而非裸字符串,从编译期消除名字不匹配风险。

热插拔与在线检测

dev_online 与 device_status_emit 共同支撑热插拔场景:SD 卡/U 盘随时可能被拔出,正在进行的 dev_bulk_read/write 可能因介质掉线而失败。上层应在批量读写前检查 dev_online,并在读写返回错误后重试或重新挂载。IOCTL_SET_DETECT_MODE/FUNC/TIME_INTERVAL(命令 5-7)允许驱动配置轮询检测周期,平衡响应速度与功耗。

引用计数与关闭时序

struct device.ref 是 atomic_t,dev_open 递增、dev_close 递减,仅当计数归零才真正释放驱动资源。这保证了多个模块(文件系统 + 录音 + VM)同时持有同一设备句柄时,任何一个模块提前关闭都不会破坏其他模块。错误用法:同一个句柄被 dev_close 两次会导致计数过减,可能提前触发资源释放——上层必须保证 open/close 配对。

原子性与中断上下文

atomic.h 的"读-改-写"通过关中断(CPU_CRITICAL_ENTER/EXIT)实现,代价是临界区内不允许执行耗时操作。若在中断服务程序中直接调用依赖原子操作的 dev_mg 接口,需注意临界区长度;IOCTL_REGISTER_IRQ_HANDLER/UNREGISTER_IRQ_HANDLER(命令 17/18)的存在说明框架支持驱动注册自己的中断处理,中断回调里应避免调用可能阻塞的读写路径。

Flash 擦写约束

对 sfc/ext_flsh 等 Flash 介质:写操作前通常需要先擦除(IOCTL_ERASE_SECTOR/BLOCK/CHIP),且擦除粒度(sector/block)可能大于写入粒度;IOCTL_ERASE_CHIP 是全片擦除,误用会销毁整个固件,仅应在烧录/量产流程中使用。加密介质(sd_enc)需注意 IOCTL_SET_ENC_END 的顺序约束。

IOCTL 参数错误

IOCTL 采用 (cmd, u32 arg) 扁平参数模型,查询类命令(如 IOCTL_GET_CAPACITY)要求调用方传入足够大的结果缓冲区地址;arg 传 0 或指针非法会造成驱动写坏内存。使用前应核对命令期望的参数类型(数值 vs 指针)。

并发访问

  • 多任务共享设备:读写路径本身是否线程安全由具体驱动决定;框架层只保证句柄引用计数原子。同一介质的并发写需要上层加锁(如文件系统层的互斥)。
  • 注册表只读:.device 段是只读的静态数据,运行期 set_device_node 可整体切换注册表范围,但不应在并发访问设备时修改,否则查找结果不一致。

性能与运维注意事项

  • 预编译实现:dev_mg 的实现位于 sdk/apps/include_lib/liba/bd49/mbox_flash/dev_mg_lib.a,面向 BD49 平台 mbox_flash 工程。更换平台或功能裁剪时需替换对应库,头文件契约保持不变。
  • 查找复杂度:dev_open 按名字在 .device 段内线性查找。内置设备数量有限(sfc/sd0/sd_enc/ext_flsh/otg/udisk0 等),线性查找的开销可忽略;但若业务自定义大量设备节点,打开热点路径会随节点数线性增长。
  • 批量读写是性能主路径:文件系统的吞吐依赖 dev_bulk_read/write 的扇区连续访问,字节级接口用于小数据场景;IOCTL_SET_READ_USE_CACHE(43)/IOCTL_SET_CACHE_SYNC_ISR_EN(44)可启用读缓存与 ISR 同步,属于性能调优开关,需结合具体驱动文档使用。
  • 电源管理:空闲时可用 IOCTL_SET_POWER_DOWN 让 Flash 进入低功耗,配合 IOCTL_RELEASE_POWER_DOWN 恢复;功耗敏感产品应在挂起/恢复流程中成对调用。
  • 故障定位:所有 API 以 int 返回值报告错误,dev_online 提供在线探测。排障顺序建议:先确认 devices_init_api 成功 → dev_online 为 true → 单步验证 dev_ioctl 查询 → 再进入读写路径。

扩展点

dev_mg 的设计为新增介质与扩展控制命令留出了清晰的扩展路径:

  1. 注册新设备:实现 struct device_operations(可复用 sfc_dev_ops 等现有模式),用 REGISTER_DEVICE/REGISTER_DEVICES 宏把 dev_node 放入 .device 段,无需改动框架与上层代码。框架导出的 sfc_dev_ops 是现成的参考实现。
  2. 新增设备名:在 device.h 的命名区(第 8-15 行)追加 __XXX_NANE 宏,保持命名规范统一。
  3. 扩展 IOCTL:ioctl_cmds.h 已为各分组预留编号空间(如 24-29、117-199、205-209 等未使用区间);新增命令应写入对应分组,避免与现有编号冲突。
  4. 多设备表:通过 set_device_node 在运行期切换注册表范围,可支持多镜像/多分区固件的设备视图切换。
  5. 状态订阅:通过 device_status_emit 上报事件,上层可据此实现热插拔处理与设备状态机。

测试与使用方

dev_mg 被 SDK 中多个关键模块直接引用,其正确性由这些使用方共同验证:

  • flash_wp.h:Flash 写保护模块,基于 dev_mg 访问 Flash 并设置保护区域;
  • vm.h:VM 键值存储,通过 dev_mg 读写 Flash 分区(对应 IOCTL_SET/GET_VM_INFO);
  • usb_host.h / usb_storage.h:USB Host 协议栈,将 U 盘/OTG 设备接入 dev_mg 设备表;
  • includes.h:全局包含 dev_mg/atomic.h,使原子操作成为 SDK 公共基础件。

这些使用方覆盖了"打开 → 查询 → 读写 → 关闭"的完整生命周期,是验证框架行为(引用计数、在线检测、IOCTL 分发)的主要场景。

相关链接

  • 设备管理框架头文件 device.h
  • IOCTL 命令定义 ioctl_cmds.h
  • 原子操作实现 atomic.h
  • dev_mg 预编译库 dev_mg_lib.a
  • 使用方:VM 键值存储(vm.h)、Flash 写保护(flash_wp.h)、USB Host 存储(usb_storage.h)——各自的实现细节见对应目录页面
Prev
文件系统 (FAT / nor_fs / SYDF)