581 lines
19 KiB
C
581 lines
19 KiB
C
/*
|
|
* kl_app.c
|
|
* Application layer: command dispatch, handlers, generic stream sender.
|
|
*
|
|
* All handlers are non-blocking. They prepare response payloads and
|
|
* start TX via the transport layer. KL_App_Service() drives the
|
|
* multi-step response state machines.
|
|
*/
|
|
|
|
#include "kl_app.h"
|
|
#include "kl_transport.h"
|
|
#include "kl_session.h"
|
|
#include "psg_prop.h"
|
|
#include "ee_manager.h"
|
|
#include <string.h>
|
|
|
|
/* s_dfi_code: shared with ee_manager via extern in ee_manager.h */
|
|
int8_t s_dfi_code = 0;
|
|
|
|
/* ================================================================
|
|
* Application context
|
|
* ================================================================ */
|
|
static struct {
|
|
KL_AppState state;
|
|
|
|
/* Single-response payload */
|
|
uint8_t resp_payload[KL_MAX_PACKET_PAYLOAD];
|
|
uint8_t resp_len;
|
|
|
|
/* Stream sender */
|
|
uint8_t stream_buf[KL_STREAM_BUF_SIZE];
|
|
uint8_t stream_len;
|
|
uint8_t stream_offset;
|
|
uint8_t stream_cmd; /* 0xF6 for ASCII, 0xFC for DTC */
|
|
uint8_t stream_chunk_size; /* 13 for ASCII, 15 for DTC */
|
|
uint8_t stream_cur_chunk; /* Current chunk size being sent */
|
|
|
|
/* EEPROM/ROM unlock flags */
|
|
uint8_t eeprom_unlocked_dfi;
|
|
uint8_t eeprom_unlocked_dfi_write;
|
|
uint8_t rom_unlocked_cust;
|
|
} app;
|
|
|
|
/* ================================================================
|
|
* DTC storage
|
|
* ================================================================ */
|
|
static KL_FaultCode s_dtc_list[KL_DTC_TOTAL_RECORDS];
|
|
static size_t s_dtc_count = 0;
|
|
|
|
/* Default test DTCs */
|
|
/*static const KL_FaultCode s_default_dtcs[] = {
|
|
{ 0x5B, 0x20, 0x55 },
|
|
{ 0x50, 0x20, 0x03 },
|
|
{ 0x56, 0x20, 0x56 },
|
|
{ 0x5E, 0x30, 0x60 },
|
|
};*/
|
|
static const KL_FaultCode s_default_dtcs[] = {
|
|
{ 0x00, 0xFF, 0xFF },
|
|
{ 0x00, 0xFF, 0xFF },
|
|
{ 0x00, 0xFF, 0xFF },
|
|
{ 0x00, 0xFF, 0xFF },
|
|
};
|
|
/* ================================================================
|
|
* Password / login bodies (from old kline.c)
|
|
* ================================================================ */
|
|
static const uint8_t kPwdBody_DFI[] = { 0x00, 0x03, 0xFF, 0xFF };
|
|
static const uint8_t kLoginBody_DFI[] = { 0x00, 0x00, 0x00, 0xBF, 0x30, 0x35, 0x30, 0x30, 0x30, 0x31, 0x1C, 0x09, 0x04 };
|
|
static const uint8_t kPwdBody_DFI_WRITE[] = { 0x00, 0x03, 0x2F, 0xFF, 'K','H','T','C','A','8','G','0','E' };
|
|
static const uint8_t kPwdBody_DFI_WRITE_815[] = { 0x00, 0x03, 0x2F, 0xFF, '0','5','0','0','0','1',0x1C,0x09,0x04 };
|
|
|
|
static const uint8_t kPwdBody_CUST[] = { 0x00, 0x00, 0x82, 0x33 };
|
|
static const uint8_t kLoginBody_CUST[] = { 0x00, 0x00, 0x9F, 0xFF, 0x30, 0x35, 0x30, 0x30, 0x30, 0x31, 0x1C, 0x09, 0x04 };
|
|
static const uint8_t kPwdBody_OEM_1[] = { 0x00, 0x02, 0x2E, 0x10 };
|
|
static const uint8_t kLoginBody_OEM_1[] = { 0x9F, 0xD0, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
|
|
|
static const uint8_t kReadIdentAddrBody[] = { 0x02, 0x00, 0xC6 };
|
|
static const uint8_t kReadFahrSoftwareAddress[] = { 0x02, 0x00, 0xA4 };
|
|
static const uint8_t kReadStart55Address[] = { 0x01, 0x00, 0xAE };
|
|
static const uint8_t kReadCompensationValue[] = { 0x02, 0x02, 0xFE };
|
|
|
|
static const uint8_t kActivateSyncOut[] = { 0x88, 0x01, 0x04, 0x06, 0x01 };
|
|
|
|
/* ================================================================
|
|
* Init / Reset
|
|
* ================================================================ */
|
|
void KL_App_Init(void)
|
|
{
|
|
memset(&app, 0, sizeof(app));
|
|
app.state = KL_APP_IDLE;
|
|
|
|
KL_App_SetDTCList(s_default_dtcs, sizeof(s_default_dtcs) / sizeof(s_default_dtcs[0]));
|
|
}
|
|
|
|
void KL_App_ResetLocks(void)
|
|
{
|
|
app.eeprom_unlocked_dfi = 0;
|
|
app.eeprom_unlocked_dfi_write = 0;
|
|
app.rom_unlocked_cust = 0;
|
|
}
|
|
|
|
void KL_App_Reset(void)
|
|
{
|
|
app.state = KL_APP_IDLE;
|
|
app.eeprom_unlocked_dfi = 0;
|
|
app.eeprom_unlocked_dfi_write = 0;
|
|
app.rom_unlocked_cust = 0;
|
|
}
|
|
|
|
int KL_App_Busy(void)
|
|
{
|
|
return (app.state != KL_APP_IDLE && app.state != KL_APP_DONE) ? 1 : 0;
|
|
}
|
|
|
|
int KL_App_Done(void)
|
|
{
|
|
return (app.state == KL_APP_DONE || app.state == KL_APP_IDLE) ? 1 : 0;
|
|
}
|
|
|
|
/* ================================================================
|
|
* DTC management
|
|
* ================================================================ */
|
|
void KL_App_SetDTCList(const KL_FaultCode *list, size_t count)
|
|
{
|
|
if (!list) { s_dtc_count = 0; return; }
|
|
if (count > KL_DTC_TOTAL_RECORDS) count = KL_DTC_TOTAL_RECORDS;
|
|
for (size_t i = 0; i < count; ++i) s_dtc_list[i] = list[i];
|
|
s_dtc_count = count;
|
|
}
|
|
|
|
/* ================================================================
|
|
* DTC stream builder
|
|
* ================================================================ */
|
|
static size_t build_dtc_stream(uint8_t *out)
|
|
{
|
|
size_t w = 0;
|
|
for (size_t i = 0; i < KL_DTC_TOTAL_RECORDS; ++i) {
|
|
if (i < s_dtc_count) {
|
|
out[w++] = s_dtc_list[i].dtc;
|
|
out[w++] = s_dtc_list[i].status;
|
|
out[w++] = s_dtc_list[i].extra;
|
|
} else {
|
|
out[w++] = 0x00;
|
|
out[w++] = 0xFF;
|
|
out[w++] = 0xFF;
|
|
}
|
|
/* Pad to 8-byte record */
|
|
for (int j = 0; j < 5; j++) out[w++] = 0xFF;
|
|
}
|
|
return w; /* 64 */
|
|
}
|
|
|
|
/* ================================================================
|
|
* Ident stream builder (for ReadIdent command)
|
|
* ================================================================ */
|
|
static size_t app_build_ident(uint8_t *out, size_t maxlen)
|
|
{
|
|
size_t w = 0;
|
|
const char *strs[] = {
|
|
PSG_KUNDENNUMMER_STR, PSG_DATENSATZ_STR,
|
|
PSG_SOFTWARE_VER_STR, PSG_SOFTWARE_VER2_STR,
|
|
PSG_STEUERGERAET_STR, PSG_UNKNOWN_STR
|
|
};
|
|
const size_t lens[] = { 12, 10, 10, 10, 10, 6 };
|
|
for (int s = 0; s < 6; s++) {
|
|
for (size_t i = 0; i < lens[s] && strs[s][i] != '\0' && w < maxlen; i++) {
|
|
out[w++] = (uint8_t)strs[s][i];
|
|
}
|
|
}
|
|
return w;
|
|
}
|
|
|
|
/* ================================================================
|
|
* Helper: start a single-response send
|
|
* ================================================================ */
|
|
static void start_response(const uint8_t *payload, uint8_t len)
|
|
{
|
|
memcpy(app.resp_payload, payload, len);
|
|
app.resp_len = len;
|
|
KL_Transport_SendPacket(app.resp_payload, app.resp_len);
|
|
app.state = KL_APP_RESP_WAIT_TX;
|
|
}
|
|
|
|
/* ================================================================
|
|
* Helper: start a stream send
|
|
* ================================================================ */
|
|
static void start_stream(uint8_t cmd, uint8_t chunk_size, const uint8_t *data, uint8_t len)
|
|
{
|
|
memcpy(app.stream_buf, data, len);
|
|
app.stream_len = len;
|
|
app.stream_offset = 0;
|
|
app.stream_cmd = cmd;
|
|
app.stream_chunk_size = chunk_size;
|
|
app.state = KL_APP_STREAM_SEND;
|
|
}
|
|
|
|
/* ================================================================
|
|
* Command handlers
|
|
* ================================================================ */
|
|
static void handle_ack(void)
|
|
{
|
|
KL_Transport_SendAck();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
}
|
|
|
|
static void handle_read_ident(void)
|
|
{
|
|
uint8_t buf[KL_STREAM_BUF_SIZE];
|
|
size_t n = app_build_ident(buf, sizeof(buf));
|
|
start_stream((uint8_t)KL_CMD_AsciiData, KL_ASCII_CHUNK_SIZE, buf, (uint8_t)n);
|
|
}
|
|
|
|
static void handle_fault_codes_read(void)
|
|
{
|
|
uint8_t buf[KL_DTC_TOTAL_RECORDS * KL_DTC_RECORD_SIZE];
|
|
size_t n = build_dtc_stream(buf);
|
|
start_stream((uint8_t)KL_CMD_FaultCodesResponse, KL_DTC_CHUNK_SIZE, buf, (uint8_t)n);
|
|
}
|
|
|
|
static void handle_read_eeprom(const KL_Packet *cmd)
|
|
{
|
|
if (cmd->data_len < 3U) { KL_Transport_SendNak(); app.state = KL_APP_SIMPLE_WAIT_TX; return; }
|
|
|
|
uint8_t len_req = cmd->data[0];
|
|
uint16_t addr = ((uint16_t)cmd->data[1] << 8) | cmd->data[2];
|
|
|
|
if (addr == 0x0044 && !app.eeprom_unlocked_dfi) {
|
|
KL_Transport_SendNak();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
return;
|
|
}
|
|
|
|
uint8_t data[16] = {0};
|
|
if (!PSG_EEPROM_GetData(addr, len_req, data)) {
|
|
KL_Transport_SendNak();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
return;
|
|
}
|
|
|
|
uint8_t payload[1 + 16];
|
|
payload[0] = (uint8_t)KL_CMD_ReadEepromResponse;
|
|
memcpy(&payload[1], data, len_req);
|
|
start_response(payload, (uint8_t)(1U + len_req));
|
|
}
|
|
|
|
static void handle_read_rom_eeprom(const KL_Packet *cmd)
|
|
{
|
|
if (cmd->data_len < 3U) { KL_Transport_SendNak(); app.state = KL_APP_SIMPLE_WAIT_TX; return; }
|
|
|
|
uint8_t len_req = cmd->data[0];
|
|
uint16_t addr = ((uint16_t)cmd->data[1] << 8) | cmd->data[2];
|
|
|
|
if ((addr == 0x9FFE || addr == (uint16_t)(PSG_CUST_CHANGE_ADDR + 3U)) && !app.rom_unlocked_cust) {
|
|
KL_Transport_SendNak();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
return;
|
|
}
|
|
|
|
uint8_t data[10];
|
|
if (!PSG_ROM_GetData(addr, len_req, data)) {
|
|
KL_Transport_SendNak();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
return;
|
|
}
|
|
|
|
uint8_t payload[1 + 10];
|
|
payload[0] = (uint8_t)KL_CMD_ReadRomResponse;
|
|
memcpy(&payload[1], data, len_req);
|
|
start_response(payload, (uint8_t)(1U + len_req));
|
|
}
|
|
|
|
static void handle_login_eeprom(const KL_Packet *cmd)
|
|
{
|
|
const uint8_t *body = cmd->data;
|
|
uint8_t body_len = cmd->data_len;
|
|
|
|
const uint8_t *login_body = NULL;
|
|
size_t login_len = 0;
|
|
|
|
if (body_len == sizeof(kPwdBody_DFI) && memcmp(body, kPwdBody_DFI, sizeof(kPwdBody_DFI)) == 0) {
|
|
app.eeprom_unlocked_dfi = 1;
|
|
login_body = kLoginBody_DFI;
|
|
login_len = sizeof(kLoginBody_DFI);
|
|
} else if (body_len == sizeof(kPwdBody_CUST) && memcmp(body, kPwdBody_CUST, sizeof(kPwdBody_CUST)) == 0) {
|
|
app.rom_unlocked_cust = 1;
|
|
login_body = kLoginBody_CUST;
|
|
login_len = sizeof(kLoginBody_CUST);
|
|
} else if (body_len == sizeof(kPwdBody_OEM_1) && memcmp(body, kPwdBody_OEM_1, sizeof(kPwdBody_OEM_1)) == 0) {
|
|
login_body = kLoginBody_OEM_1;
|
|
login_len = sizeof(kLoginBody_OEM_1);
|
|
} else if (body_len == sizeof(kPwdBody_DFI_WRITE) && memcmp(body, kPwdBody_DFI_WRITE, sizeof(kPwdBody_DFI_WRITE)) == 0) {
|
|
app.eeprom_unlocked_dfi_write = 1;
|
|
login_body = kLoginBody_DFI;
|
|
login_len = sizeof(kLoginBody_DFI);
|
|
}else if (body_len == sizeof(kPwdBody_DFI_WRITE_815) && memcmp(body, kPwdBody_DFI_WRITE_815, sizeof(kPwdBody_DFI_WRITE_815)) == 0) {
|
|
app.eeprom_unlocked_dfi_write = 1;
|
|
login_body = kLoginBody_DFI;
|
|
login_len = sizeof(kLoginBody_DFI);
|
|
} else {
|
|
/* Wrong password -- clear all locks */
|
|
app.eeprom_unlocked_dfi = 0;
|
|
app.eeprom_unlocked_dfi_write = 0;
|
|
app.rom_unlocked_cust = 0;
|
|
KL_Transport_SendNak();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
return;
|
|
}
|
|
|
|
uint8_t payload[1 + 16];
|
|
payload[0] = (uint8_t)KL_CMD_EepromLoginResponse;
|
|
memcpy(&payload[1], login_body, login_len);
|
|
start_response(payload, (uint8_t)(1U + login_len));
|
|
}
|
|
|
|
static void handle_write_eeprom(const KL_Packet *cmd)
|
|
{
|
|
if (cmd->data_len < 5U) { KL_Transport_SendNak(); app.state = KL_APP_SIMPLE_WAIT_TX; return; }
|
|
|
|
uint8_t len_req = cmd->data[0];
|
|
uint16_t addr = ((uint16_t)cmd->data[1] << 8) | cmd->data[2];
|
|
uint8_t value = cmd->data[3];
|
|
uint8_t csum = cmd->data[4];
|
|
|
|
if (!app.eeprom_unlocked_dfi_write) {
|
|
KL_Transport_SendNak();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
return;
|
|
}
|
|
|
|
if (addr != 0x0044 || len_req != 0x02) {
|
|
KL_Transport_SendNak();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
return;
|
|
}
|
|
|
|
if ((uint8_t)(value + csum) != 0) {
|
|
KL_Transport_SendNak();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
return;
|
|
}
|
|
|
|
s_dfi_code = (int8_t)value;
|
|
memWrite = 1;
|
|
|
|
uint8_t payload[5];
|
|
payload[0] = (uint8_t)KL_CMD_WriteEepromResponse;
|
|
payload[1] = (uint8_t)len_req;
|
|
payload[2] = (uint8_t)((addr >> 8) & 0xFF);
|
|
payload[3] = (uint8_t)(addr & 0xFF);
|
|
payload[4] = 0xFF;
|
|
start_response(payload, 5);
|
|
}
|
|
|
|
uint8_t SYNC_PULSE_OUT;
|
|
|
|
static void handle_read_ram(const KL_Packet *cmd)
|
|
{
|
|
if (cmd->data_len >= sizeof(kReadIdentAddrBody) &&
|
|
memcmp(cmd->data, kReadIdentAddrBody, sizeof(kReadIdentAddrBody)) == 0) {
|
|
uint8_t payload[3];
|
|
payload[0] = 0xFE;
|
|
payload[1] = (uint8_t)(PSG_IDENT_ADDR & 0xFF);
|
|
payload[2] = (uint8_t)((PSG_IDENT_ADDR >> 8) & 0xFF);
|
|
start_response(payload, 3);
|
|
return;
|
|
}
|
|
if (cmd->data_len >= sizeof(kReadFahrSoftwareAddress) &&
|
|
memcmp(cmd->data, kReadFahrSoftwareAddress, sizeof(kReadFahrSoftwareAddress)) == 0) {
|
|
uint8_t payload[3];
|
|
payload[0] = 0xFE;
|
|
payload[1] = (uint8_t)(PSG_FAHRSOFTWARE_ADDR & 0xFF);
|
|
payload[2] = (uint8_t)((PSG_FAHRSOFTWARE_ADDR >> 8) & 0xFF);
|
|
start_response(payload, 3);
|
|
return;
|
|
}
|
|
if (cmd->data_len >= sizeof(kReadStart55Address) &&
|
|
memcmp(cmd->data, kReadStart55Address, sizeof(kReadStart55Address)) == 0) {
|
|
uint8_t payload[2];
|
|
payload[0] = 0xFE;
|
|
payload[1] = (uint8_t)(PSG_START55_ADDR);
|
|
start_response(payload, 2);
|
|
return;
|
|
}
|
|
if (cmd->data_len >= sizeof(kReadCompensationValue) &&
|
|
memcmp(cmd->data, kReadCompensationValue, sizeof(kReadCompensationValue)) == 0) {
|
|
uint8_t payload[3];
|
|
payload[0] = 0xFE;
|
|
payload[1] = (uint8_t)(PSG_CALVALUE_ADDR & 0xFF);
|
|
payload[2] = (uint8_t)((PSG_CALVALUE_ADDR >> 8) & 0xFF);
|
|
start_response(payload, 2);
|
|
return;
|
|
}
|
|
/*else{//fallback
|
|
uint8_t payload[cmd->data_len-1];
|
|
payload[0] = 0xFE;
|
|
//payload[1] = (uint8_t)(PSG_START55_ADDR);
|
|
//fill with
|
|
start_response(payload, 2);
|
|
}*/
|
|
|
|
KL_Transport_SendNak();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
}
|
|
|
|
static void handle_write_ram(const KL_Packet *cmd)
|
|
{
|
|
if (cmd->data_len >= sizeof(kActivateSyncOut) &&
|
|
memcmp(cmd->data, kActivateSyncOut, sizeof(kActivateSyncOut)) == 0) {
|
|
//SYNC_PULSE_OUT = 1;
|
|
SYNC_Pulse_RequestSyncout(1);
|
|
KL_Transport_SendAck();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
return;
|
|
}
|
|
|
|
//KL_Transport_SendNak();
|
|
KL_Transport_SendAck();
|
|
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
}
|
|
|
|
static void handle_end(void)
|
|
{
|
|
KL_Transport_SendAck();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
/* Session layer will check end_requested after we're done */
|
|
KL_Session_RequestEnd();
|
|
}
|
|
|
|
/* ================================================================
|
|
* Command dispatch
|
|
* ================================================================ */
|
|
void KL_App_Dispatch(const KL_Packet *cmd)
|
|
{
|
|
app.state = KL_APP_IDLE;
|
|
KL_Transport_TxReset();
|
|
|
|
switch (cmd->command) {
|
|
case KL_CMD_ACK:
|
|
handle_ack();
|
|
break;
|
|
case KL_CMD_ReadIdent:
|
|
handle_read_ident();
|
|
break;
|
|
case KL_CMD_FaultCodesRead:
|
|
handle_fault_codes_read();
|
|
break;
|
|
case KL_CMD_ReadEeprom:
|
|
handle_read_eeprom(cmd);
|
|
break;
|
|
case KL_CMD_ReadRomEeprom:
|
|
handle_read_rom_eeprom(cmd);
|
|
break;
|
|
case KL_CMD_LoginEeprom:
|
|
handle_login_eeprom(cmd);
|
|
break;
|
|
case KL_CMD_WriteEeprom:
|
|
handle_write_eeprom(cmd);
|
|
break;
|
|
case KL_CMD_ReadRam:
|
|
handle_read_ram(cmd);
|
|
break;
|
|
case KL_CMD_WriteRam:
|
|
handle_write_ram(cmd);
|
|
break;
|
|
case KL_CMD_End:
|
|
handle_end();
|
|
break;
|
|
default:
|
|
KL_Transport_SendNak();
|
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* ================================================================
|
|
* Service: drive response state machines
|
|
* ================================================================ */
|
|
void KL_App_Service(void)
|
|
{
|
|
switch (app.state) {
|
|
|
|
case KL_APP_IDLE:
|
|
case KL_APP_DONE:
|
|
break;
|
|
|
|
/* ---- Simple send (ACK/NAK, no follow-up ACK exchange) ---- */
|
|
case KL_APP_SIMPLE_WAIT_TX:
|
|
if (KL_Transport_TxDone()) {
|
|
KL_Transport_TxReset();
|
|
app.state = KL_APP_DONE;
|
|
} else if (KL_Transport_TxError()) {
|
|
KL_Transport_TxReset();
|
|
app.state = KL_APP_DONE;
|
|
}
|
|
break;
|
|
|
|
/* ---- Single response: send and return to ready ---- */
|
|
case KL_APP_RESP_WAIT_TX:
|
|
if (KL_Transport_TxDone()) {
|
|
KL_Transport_TxReset();
|
|
KL_Session_Kick();
|
|
app.state = KL_APP_DONE;
|
|
} else if (KL_Transport_TxError()) {
|
|
KL_Transport_TxReset();
|
|
app.state = KL_APP_DONE;
|
|
}
|
|
break;
|
|
|
|
/* ---- Stream: send chunks -> wait ACK each -> final ACK ---- */
|
|
case KL_APP_STREAM_SEND:
|
|
{
|
|
uint8_t remaining = app.stream_len - app.stream_offset;
|
|
app.stream_cur_chunk = (remaining > app.stream_chunk_size)
|
|
? app.stream_chunk_size : remaining;
|
|
|
|
uint8_t payload[1 + KL_DTC_CHUNK_SIZE]; /* max chunk size */
|
|
payload[0] = app.stream_cmd;
|
|
memcpy(&payload[1], &app.stream_buf[app.stream_offset], app.stream_cur_chunk);
|
|
|
|
if (KL_Transport_SendPacket(payload, (uint8_t)(1 + app.stream_cur_chunk))) {
|
|
app.state = KL_APP_STREAM_WAIT_TX;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case KL_APP_STREAM_WAIT_TX:
|
|
if (KL_Transport_TxDone()) {
|
|
KL_Transport_TxReset();
|
|
app.stream_offset += app.stream_cur_chunk;
|
|
if (app.stream_offset >= app.stream_len) {
|
|
/* Last chunk sent -- return to ready state.
|
|
* No ACK is expected after the final chunk.
|
|
* The master's next packet (keepalive ACK or command)
|
|
* will be handled normally by KL_App_Dispatch. */
|
|
KL_Session_Kick();
|
|
app.state = KL_APP_DONE;
|
|
} else {
|
|
/* Intermediate chunk: wait for ACK before sending next */
|
|
KL_Transport_StartReceive(400);
|
|
app.state = KL_APP_STREAM_WAIT_ACK;
|
|
}
|
|
} else if (KL_Transport_TxError()) {
|
|
KL_Transport_TxReset();
|
|
app.state = KL_APP_DONE;
|
|
}
|
|
break;
|
|
|
|
case KL_APP_STREAM_WAIT_ACK:
|
|
if (KL_Transport_RxDone()) {
|
|
const KL_Packet *ack = KL_Transport_GetRxPacket();
|
|
KL_Transport_RxReset();
|
|
if (ack->command == (uint8_t)KL_CMD_ACK) {
|
|
KL_Session_Kick();
|
|
app.state = KL_APP_STREAM_SEND;
|
|
} else {
|
|
app.state = KL_APP_DONE;
|
|
}
|
|
} else if (KL_Transport_RxError()) {
|
|
KL_Transport_RxReset();
|
|
app.state = KL_APP_DONE;
|
|
}
|
|
break;
|
|
|
|
case KL_APP_STREAM_FINAL_WAIT:
|
|
/* No longer used, but kept for safety */
|
|
if (KL_Transport_TxDone()) {
|
|
KL_Transport_TxReset();
|
|
KL_Session_Kick();
|
|
app.state = KL_APP_DONE;
|
|
} else if (KL_Transport_TxError()) {
|
|
KL_Transport_TxReset();
|
|
app.state = KL_APP_DONE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
app.state = KL_APP_DONE;
|
|
break;
|
|
}
|
|
}
|