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);
}
本篇小结
- DSD的路由决策树是绝对的——SID查找→会话鉴权→安全鉴权→分发。任何一步失败立即返回对应NRC。
- SID表查找采用线性搜索+EOL终止符——无动态内存分配,适合嵌入式环境。
- 功能性寻址时自动抑制五种NRC——防止多ECU广播同一条“我不支持“堵塞总线。
- 内外服务分流——
DspSidTabFnc为NULL→内部DSP服务;非NULL→外部处理回调。
【下集预告】:SID被正确路由了——现在真正的业务逻辑开始了。DSP是DCM最大的文件——6492行——22个UDS服务的完整C实现。我们从DSP的框架开始——Init怎么初始化所有服务状态机、Main怎么推进异步操作——然后深入到最核心的几个服务实现。