Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

4.4 DSD——SID分发路由与鉴权

SID到达DSD后发生了什么事

DSL把缓冲区标记为PROVIDED_TO_DSD,调用了DsdDslDataIndication——把收到的UDS消息数据、SID表指针、寻址方式、Tx PDU id等全部打包进一个本地数据结构MsgDataType。在下一个Dcm_MainFunction周期中,DsdMain()读取标志位并调用DsdHandleRequest()——整个UDS消息的路由从这里开始。


DsdHandleRequest —— 路由决策树

void DsdHandleRequest(void) {
    // 步骤0: 从DSL传来的消息数据中拿到SID
    uint8 sid = msgData.pduRxData.SduDataPtr[0];

    // 步骤1: 可选——通知应用层"有诊断请求来了
    ServiceIndication(sid, ...);

    // 步骤2: 在SID表中查找
    const Dcm_DsdServiceType *sidCfg;
    if (!lookupSid(sid, &sidCfg)) {
        // SID不在表中 → NRC 0x11 (serviceNotSupported)
        createAndSendNcr(DCM_E_SERVICE_NOT_SUPPORTED);
        return;
    }

    // 步骤3: 检查会话权限
    if (!DspCheckSessionLevel(sidCfg->DsdSidTabSessionLevelRef)) {
        createAndSendNcr(DCM_E_SERVICE_NOT_SUPPORTED_IN_ACTIVE_SESSION);
        return;
    }

    // 步骤4: 检查安全权限
    if (!DspCheckSecurityLevel(sidCfg->DsdSidTabSecurityLevelRef)) {
        createAndSendNcr(DCM_E_SECURITY_ACCESS_DENIED);
        return;
    }

    // 步骤5: 选择分发路径(内部服务或外部服务)
    if (sidCfg->DspSidTabFnc != NULL_PTR) {
        startExternalServiceProcessing(sidCfg); // 外部服务处理
    } else {
        selectServiceFunction(sidCfg);           // 内部DSP服务
    }
}

lookupSid —— 不是哈希表,是线性搜索

static boolean lookupSid(uint8 sid,
    const Dcm_DsdServiceType **sidPtr)
{
    const Dcm_DsdServiceType *service =
        Dcm_ConfigPtr->Dsd->DsdServiceTable[activeProtocolIndex].DsdService;

    while (service->DsdSidTabServiceId != Arc_EOL) {
        if (service->DsdSidTabServiceId == sid) {
            *sidPtr = service;
            return TRUE;
        }
        service++;  // 下一个行
    }
    return FALSE;
}

Arctic Core用End-of-List标记Arc_EOL)来终止搜索——不是NULL指针也不是计数字段。每一行必须预先写入终止符——这是典型的嵌入式静态数组遍历模式。没有动态内存分配,没有malloc。


runInternalService —— 巨型switch分发

static void runInternalService(uint8 SID) {
    switch (SID) {
        case SID_DIAGNOSTIC_SESSION_CONTROL:  DspUdsDiagnosticSessionControl(...); break;
        case SID_ECU_RESET:                   DspUdsEcuReset(...); break;
        case SID_CLEAR_DIAGNOSTIC_INFORMATION:DspUdsClearDiagnosticInformation(...); break;
        case SID_READ_DTC_INFORMATION:        DspUdsReadDtcInformation(...); break;
        case SID_READ_DATA_BY_IDENTIFIER:     DspUdsReadDataByIdentifier(...); break;
        case SID_SECURITY_ACCESS:             DspUdsSecurityAccess(...); break;
        case SID_COMMUNICATION_CONTROL:       DspUdsCommunicationControl(...); break;
        // ... 共22个case
        default:
            DsdDspProcessingDone(DCM_E_SERVICE_NOT_SUPPORTED);
            break;
    }
}

这个巨型switch比函数指针表更直观、更易于编译器优化。在ARM Cortex-R5上,GCC会把它编译成一个高效的跳转表。


负响应的创建——功能性寻址抑制规则

static void createAndSendNrc(uint8 responseCode) {
    // 功能性寻址时的NRC抑制
    if (msgData.addrType == FUNCTIONAL) {
        switch (responseCode) {
            case DCM_E_SERVICE_NOT_SUPPORTED:
            case DCM_E_SUBFUNCTION_NOT_SUPPORTED:
            case DCM_E_REQUEST_OUT_OF_RANGE:
            case DCM_E_SERVICE_NOT_SUPPORTED_IN_ACTIVE_SESSION:
            case DCM_E_SUBFUNCTION_NOT_SUPPORTED_IN_ACTIVE_SESSION:
                return;  // ← 抑制,不发
        }
    }

    // 构建负响应
    pduTxData->SduDataPtr[0] = 0x7F;        // 负响应SID
    pduTxData->SduDataPtr[1] = sid;          // 回声原SID
    pduTxData->SduDataPtr[2] = responseCode; // NRC
    DslDsdProcessingDone(DSD_TX_NEG_RESPONSE_READY);
}

本篇小结

  1. DSD的路由决策树是绝对的——SID查找→会话鉴权→安全鉴权→分发。任何一步失败立即返回对应NRC。
  2. SID表查找采用线性搜索+EOL终止符——无动态内存分配,适合嵌入式环境。
  3. 功能性寻址时自动抑制五种NRC——防止多ECU广播同一条“我不支持“堵塞总线。
  4. 内外服务分流——DspSidTabFnc为NULL→内部DSP服务;非NULL→外部处理回调。

【下集预告】:SID被正确路由了——现在真正的业务逻辑开始了。DSP是DCM最大的文件——6492行——22个UDS服务的完整C实现。我们从DSP的框架开始——Init怎么初始化所有服务状态机、Main怎么推进异步操作——然后深入到最核心的几个服务实现。