Exportable version of WizziLab's modem driver.
src/modem_ref.cpp
- Committer:
- Jeej
- Date:
- 2021-01-27
- Revision:
- 56:67e3d9608403
- Parent:
- 51:92667569acc7
- Child:
- 57:5444cfda9889
File content as of revision 56:67e3d9608403:
#include "mbed.h" #include "WizziDebug.h" #include "modem_ref.h" #include "kal_crypto.h" #include "kal_codec.h" #include "d7a_1x_fs.h" #if 0 #define REF_PRINT(...) PRINT(__VA_ARGS__) #define URC_PRINT(...) PRINT(__VA_ARGS__) #else #define REF_PRINT(...); #define URC_PRINT(...); #endif typedef struct { action_callback_t* cb; uint8_t* data; uint8_t* istatus; } modem_ref_user_t; typedef struct { uint8_t* data; uint8_t type; uint8_t length; } modem_ref_istatus_t; typedef struct { fx_serial_send_t* send; modem_ref_callbacks_t* cb; modem_lwan_callbacks_t* lwan_cb; modem_ref_user_t user[MAX_USER_NB]; modem_ref_istatus_t istatus; uint8_t state; uint8_t tx_sequ; s32 action; } modem_ref_ctx_t; static modem_ref_ctx_t g_modem; #define STATE_OPEN 0xab #define STATE_CLOSED 0 // Flows #define WC_FLOWID_CMD 0x10 #define WC_FLOWID_ALP 0x20 #define WC_FLOWID_SYS 0x30 // Sub-Flows #define WC_FLOW_ALP_CMD 0x20 #define WC_FLOW_ALP_RESP 0x21 #define WC_FLOW_ALP_UNS 0x22 #define WC_FLOW_SYS_RST 0x30 #define WC_FLOW_SYS_PING 0x34 #define WC_FLOW_SYS_PONG 0x35 #define WC_FLOW_SYS_CFG 0x36 #define WC_FLOW_SYS_XON 0x39 #define WC_FLOW_SYS_XOFF 0x3a #define WC_FLOW_SYS_XACK 0x3b // Misc CMD... #define WM_BOOT 0x81 static void serial_send(uint8_t flowid, uint8_t* data, int size) { uint8_t len = (uint8_t)size; uint8_t wch[WC_HEADER_SIZE] = { WC_SYNC_BYTE_0, WC_SYNC_BYTE_1, len, g_modem.tx_sequ++, flowid }; ASSERT(size < 256, "serial_send too big for serial protocol (%d/%dmax)", size, 255); g_modem.send(wch, WC_HEADER_SIZE, data, len); } void modem_ref_input(uint8_t flowid, uint8_t* payload, uint8_t size) { int rem =size; uint8_t* p = payload; alp_parsed_chunk_t r; int id = -1; int8_t err = ALP_ERR_NONE; uint8_t eop; uint32_t parsed; g_modem.action = -1; //REF_PRINT("input 0x%x/%d Bytes\n", flowid, size); switch (flowid) { case WC_FLOW_ALP_UNS: case WC_FLOW_ALP_CMD: parsed = alp_parse_chunk(&p, &r); if (!parsed) { // Discard the payload in case of parsing error. REF_PRINT("Parsing error line %d!\r\n", __LINE__); break; } rem -= parsed; g_modem.action++; //REF_PRINT("Rem %d Bytes\n", rem); // This should always be a TAG'ed request in case of FS access... if (r.type == ALP_OPCODE_TAG) { REF_PRINT("ACT %d: TAG[%d]\n", g_modem.action, r.meta.tag.id); id = r.meta.tag.id; while(rem>0) { // Parse File Operation parsed = alp_parse_chunk(&p, &r); if (!parsed) { // Discard the payload in case of parsing error. REF_PRINT("Parsing error line %d!\r\n", __LINE__); break; } rem -= parsed; g_modem.action++; //REF_PRINT("ALP OP[%d]\n", r.type); switch (r.type) { case ALP_OPCODE_F_RD_DATA: REF_PRINT("ACT %d: F_RD_DATA[%d] @%d %d bytes\n", g_modem.action, r.meta.f_data.fid, r.meta.f_data.offset, r.meta.f_data.length); if (g_modem.cb->read) { g_modem.cb->read(r.meta.f_data.fid, r.meta.f_data.offset, r.meta.f_data.length, id); } break; case ALP_OPCODE_F_WR_DATA: REF_PRINT("ACT %d: F_WR_DATA[%d] @%d %d bytes\n", g_modem.action, r.meta.f_data.fid, r.meta.f_data.offset, r.meta.f_data.length); if (g_modem.cb->write) { g_modem.cb->write(r.meta.f_data.fid, r.data, r.meta.f_data.offset, r.meta.f_data.length, id); } break; case ALP_OPCODE_F_RD_PROP: REF_PRINT("ACT %d: F_RD_PROP[%d]\n", g_modem.action, r.meta.f_data.fid); if (g_modem.cb->read_fprop) { g_modem.cb->read_fprop(r.meta.f_data.fid, id); } break; case ALP_OPCODE_F_DELETE: REF_PRINT("ACT %d: F_DELETE[%d]\n", g_modem.action, r.meta.f_data.fid); if (g_modem.cb->remove) { g_modem.cb->remove(r.meta.f_data.fid, id); } break; case ALP_OPCODE_F_FLUSH: REF_PRINT("ACT %d: F_FLUSH[%d]\n", g_modem.action, r.meta.f_data.fid); if (g_modem.cb->flush) { g_modem.cb->flush(r.meta.f_data.fid, id); } break; default: ASSERT(false, "ASSERT: Unsupported ALP File Operation: %d\n", r.type); break; } //REF_PRINT("ALP Parsing done. rem %d\n", rem); } //REF_PRINT("ALP Packet done.\n"); } else { // ... or a 'real' URC (or ISTATUS if enabled on modem) g_modem.istatus = (modem_ref_istatus_t){0}; do { switch (r.type) { case ALP_OPCODE_RSP_URC: if (ALP_URC_TYPE_LQUAL == r.meta.urc.type) { REF_PRINT("ACT %d: RSP_URC: LQUAL[%d] %d\n", g_modem.action, r.meta.urc.ifid, r.meta.urc.per); if (g_modem.cb->lqual) { g_modem.cb->lqual(r.meta.urc.ifid, r.meta.urc.per); } } else if (ALP_URC_TYPE_LDOWN == r.meta.urc.type) { REF_PRINT("ACT %d: RSP_URC: LDOWN[%d]\n", g_modem.action, r.meta.urc.ifid); if (g_modem.cb->ldown) { g_modem.cb->ldown(r.meta.urc.ifid); } } else if (ALP_URC_TYPE_BUSY == r.meta.urc.type) { REF_PRINT("ACT %d: RSP_URC: BUSY[%d]\n", g_modem.action, r.meta.urc.ifid); if (g_modem.cb->busy) { g_modem.cb->busy(r.meta.urc.ifid); } } else if (ALP_URC_TYPE_ITF_BUSY == r.meta.urc.type) { REF_PRINT("ACT %d: RSP_URC: ITF_BUSY[%d] for %d seconds\n", g_modem.action, r.meta.urc.ifid, r.meta.urc.per); if (FID_LWAN_ITF == r.meta.urc.ifid) { if (g_modem.lwan_cb) { if (MAX_U32 == r.meta.urc.per) { if (g_modem.lwan_cb->join_failed) { g_modem.lwan_cb->join_failed(); } } else if (0 == r.meta.urc.per) { if (g_modem.lwan_cb->packet_sent) { g_modem.lwan_cb->packet_sent(); } } else { if (g_modem.lwan_cb->itf_busy) { g_modem.lwan_cb->itf_busy(r.meta.urc.per); } } } } else { if (g_modem.cb->itf_busy) { g_modem.cb->itf_busy(r.meta.urc.ifid, r.meta.urc.per); } } } else { ASSERT(false, "ASSERT: Unsupported ALP URC: %d\n", r.meta.urc.type); } break; default: // This could be anything // Let user deal with the payload if (g_modem.cb->udata) { g_modem.cb->udata(payload, size); } rem = 0; break; } int tem = alp_parse_chunk(&p, &r); if (!tem) { break; } rem -= tem; } while (rem>=0); } break; case WC_FLOW_ALP_RESP: // This should always be a TAG'ed response as we tag our requests parsed = alp_parse_chunk(&p, &r); if (!parsed) { // Discard the payload in case of parsing error. REF_PRINT("Parsing error line %d!\r\n", __LINE__); break; } rem -= parsed; g_modem.action++; //REF_PRINT("Rem %d Bytes\n", rem); ASSERT((r.type == ALP_OPCODE_RSP_TAG), "ASSERT: expecting RESP_TAG got %d\n", r.type); REF_PRINT("ACT %d: TAG[%d]\n", g_modem.action, r.meta.tag.id); id = r.meta.tag.id; eop = r.meta.tag.eop; ASSERT(g_modem.user[id].cb != NULL, "ASSERT: NULL Callback for ID %d\n", id); // Empty response if (rem <= 0) { // TODO: still no info on error... err = r.meta.tag.err ? ALP_ERR_UNKNOWN : ALP_ERR_NONE; if (ALP_ERR_NONE != err) { REF_PRINT("NO INFO ON ERROR.\n"); } else if (eop) { REF_PRINT("EOP.\n"); } g_modem.user[id].cb(eop, err, id); return; } // Actual response(s) while(rem>0) { parsed = alp_parse_chunk(&p, &r); if (!parsed) { // Discard the payload in case of parsing error. REF_PRINT("Parsing error line %d!\r\n", __LINE__); break; } rem -= parsed; g_modem.action++; //REF_PRINT("Rem %d Bytes\n", rem); switch (r.type) { case ALP_OPCODE_RSP_TAG: REF_PRINT("ACT %d: RSP_TAG[%d]\n", g_modem.action, r.meta.tag.id); break; case ALP_OPCODE_RSP_F_DATA: REF_PRINT("ACT %d: RSP_F_DATA[%d]\n", g_modem.action, r.meta.f_data.length); ASSERT(g_modem.user[id].data != NULL, "ASSERT: NULL Data Buffer for RD on ID %d\n", id); memcpy(g_modem.user[id].data, r.data, r.meta.f_data.length); break; case ALP_OPCODE_RSP_F_PROP: REF_PRINT("ACT %d: RSP_F_PROP[%d]\n", g_modem.action, r.meta.f_data.length); ASSERT(g_modem.user[id].data != NULL, "ASSERT: NULL Data Buffer for RD on ID %d\n", id); memcpy(g_modem.user[id].data, r.data, r.meta.f_data.length); break; case ALP_OPCODE_RSP_STATUS: REF_PRINT("ACT %d: RSP_STATUS[%d]\n", g_modem.action, r.meta.status.code); err = r.meta.status.code; break; case ALP_OPCODE_RSP_ISTATUS: REF_PRINT("ACT %d: RSP_ISTATUS[%d]\n", g_modem.action, r.meta.itf.length); ASSERT(g_modem.user[id].istatus != NULL, "ASSERT: NULL ISTAT Buffer for RD on ID %d\n", id); memcpy(g_modem.user[id].istatus, r.data, r.meta.itf.length); break; case ALP_OPCODE_RSP_EOPISTATUS: REF_PRINT("ACT %d: RSP_EOPISTATUS[%02X]\n", g_modem.action, r.meta.istatus.itf); err = r.meta.istatus.err ? ALP_ERR_ITF_START + r.meta.istatus.err : ALP_ERR_NONE; break; default: ASSERT(false, "ASSERT: Unsupported ALP Response: %d\n", r.type); break; } } if(eop) { // This is mainly for debug, catch old pointers //g_modem.user[id].data = NULL; //g_modem.user[id].istatus= NULL; REF_PRINT("EOP\n"); } // User Callback if (g_modem.user[id].cb) { g_modem.user[id].cb(eop, err, id); } break; case WC_FLOW_SYS_RST: // 'Software' reset request if (g_modem.cb->reset) { g_modem.cb->reset(); } break; case WC_FLOWID_CMD: if (*p == WM_BOOT) { uint16_t nb_boot; uint8_t cause; p++; cause = p[0]; nb_boot = HAL_TO_U16(p[3], p[2]); if (g_modem.cb->boot) { g_modem.cb->boot(cause, nb_boot); } } else { REF_PRINT("Unsupported CMD :0x%x\n", *p); } break; default: REF_PRINT("Unsupported WC Flowid:0x%x / %d Bytes\n", flowid, size); break; } } static uint32_t modem_ref_add_root_permission(uint8_t* p, uint8_t* root_key, uint8_t* action, uint32_t action_size) { uint8_t hash[32]; // Calculate hash on action kal_sha256_init(); kal_sha256_update(action, action_size); kal_sha256_update(root_key, D7A_FS_ROOT_KEY_SIZE); kal_sha256_final(hash); // Prepend Permission-request... ALP_ACTION_PERM_REQ(p, false, ALP_PERM_REQ_ROOT, ALP_WIZZILAB_AUTH_PROTOCOL_ID); // ... and hash memcpy(p, hash, ALP_ACTION_PERM_REQ_TOKEN_SIZE); return ALP_ACTION_PERM_REQ_OFFSET; } int modem_ref_get_id(action_callback_t* cb) { int i; // Use rolling ids static int rolling; for(i = 0; i < MAX_USER_NB; i++) { int id = (i + rolling) % MAX_USER_NB; if (g_modem.user[id].cb == NULL) { g_modem.user[id].cb = cb; rolling++; //REF_PRINT("Get ID:%d/0x%08x\n", i, cb); return id; } } ASSERT(i < MAX_USER_NB, "ASSERT: out of users\n"); return -1; } int modem_ref_free_id(int id) { if (id < MAX_USER_NB) { g_modem.user[id].cb = NULL; g_modem.user[id].data = NULL; g_modem.user[id].istatus= NULL; //REF_PRINT("Free ID:%d\n", id); return id; } return -1; } void modem_ref_open(fx_serial_send_t* send, modem_ref_callbacks_t* callbacks) { if (g_modem.state != STATE_OPEN) { g_modem.send= send; g_modem.cb = callbacks; memset(g_modem.user, 0, (MAX_USER_NB * sizeof(modem_ref_user_t))); REF_PRINT("Open Modem - Max users:%d\n", MAX_USER_NB); g_modem.state = STATE_OPEN; } } void modem_ref_close(void) { if (g_modem.state == STATE_OPEN) { g_modem.state = STATE_CLOSED; } } void modem_ref_reset(void) { serial_send(WC_FLOW_SYS_RST, NULL, 0); } void modem_ref_set_lwan_cb(modem_lwan_callbacks_t* callbacks) { g_modem.lwan_cb = callbacks; } static void _modem_ref_alp(void* itf, void* istatus, alp_pub_payload_t* alp_user, int id) { alp_pub_payload_t* alp = NULL; REF_PRINT("ALP %d Bytes\n", alp_user->len); // Always a tag alp = alp_payload_tag(alp, id); // Forward on itf if any if (itf) { alp = alp_payload_forward(alp, itf); } // add nop to get istatus if any if (istatus) { // NOP action to get istats on ACKs alp = alp_payload_nop(alp); } // Add user payload alp = alp_append(alp, alp_user); // Send to modem serial_send(WC_FLOW_ALP_UNS, alp->d, alp->len); FREE(alp); } static void _modem_ref_rsp(alp_pub_payload_t* alp_user, u8 err, int id) { alp_pub_payload_t* alp = NULL; REF_PRINT("RSP %d Bytes\n", alp_user->len); // Always a tag alp = alp_payload_rsp_tag(alp, id, true, err); // Add user payload alp = alp_append(alp, alp_user); // Send to modem serial_send(WC_FLOW_ALP_UNS, alp->d, alp->len); FREE(alp); } static void _modem_ref_read(void* itf, void* istatus , uint8_t fid, void* data, uint32_t offset, uint32_t length, int id) { alp_pub_payload_t* alp = NULL; // Expecting data and istatus g_modem.user[id].data = (uint8_t*)data; g_modem.user[id].istatus = (uint8_t*)istatus; alp = alp_payload_f_rd_data(alp, fid, offset, length, false); _modem_ref_alp(itf, istatus, alp, id); } static void _modem_ref_write(void* itf, void* istatus, uint8_t fid, void* data, uint32_t offset, uint32_t length, int id) { alp_pub_payload_t* alp = NULL; alp = alp_payload_f_wr_data(alp, fid, data, offset, length, false); _modem_ref_alp(itf, istatus, alp, id); } static void _modem_ref_flush(void* itf, void* istatus, uint8_t fid, int id) { alp_pub_payload_t* alp = NULL; alp = alp_payload_f_flush(alp, fid, false); _modem_ref_alp(itf, istatus, alp, id); } void modem_ref_raw_alp(alp_pub_payload_t* alp_user, int id) { _modem_ref_alp(NULL, NULL, alp_user, id); } void modem_ref_remote_raw_alp(void* itf, void* istatus, alp_pub_payload_t* alp_user, int id) { _modem_ref_alp(itf, istatus, alp_user, id); } void modem_ref_read_file(uint8_t fid, void* data, uint32_t offset, uint32_t length, int id) { _modem_ref_read(NULL, NULL, fid, data, offset, length, id); } void modem_ref_remote_read_file(void* itf, void* istatus , uint8_t fid, void* data, uint32_t offset, uint32_t length, int id) { _modem_ref_read(itf, istatus, fid, data, offset, length, id); } void modem_ref_write_file(uint8_t fid, void* data, uint32_t offset, uint32_t length, int id) { _modem_ref_write(NULL, NULL, fid, data, offset, length, id); } void modem_ref_remote_write_file(void* itf, void* istatus , uint8_t fid, void* data, uint32_t offset, uint32_t length, int id) { _modem_ref_write(itf, istatus, fid, data, offset, length, id); } void modem_ref_flush_file(uint8_t fid, int id) { _modem_ref_flush(NULL, NULL, fid, id); } void modem_ref_declare_file(u8 fid, alp_file_header_t* hdr, int id) { alp_pub_payload_t* alp = NULL; alp = alp_payload_f_flush(alp, fid, false); _modem_ref_alp(NULL, NULL, alp, id); } void modem_ref_enable_urc(uint8_t type, uint8_t ifid, uint8_t val, int id) { alp_pub_payload_t* alp = NULL; alp = alp_payload_urcc_en(alp, type, ifid, val); _modem_ref_alp(NULL, NULL, alp, id); } void modem_ref_activate_itf(uint8_t type, uint8_t nb_dev, uint8_t ifid, uint8_t flags, uint8_t enable, int id) { alp_pub_payload_t* alp = NULL; alp = alp_payload_activate_itf(alp, type, nb_dev, ifid, flags, enable); _modem_ref_alp(NULL, NULL, alp, id); } void modem_ref_respond(uint8_t action, int8_t status, int id) { alp_pub_payload_t* alp = NULL; alp = alp_payload_rsp_status(alp, action, status); _modem_ref_rsp(alp, (status < ALP_ERR_NONE)? true: false, id); } void modem_ref_respond_fprop(uint8_t fid, alp_file_header_t* hdr, int id) { alp_pub_payload_t* alp = NULL; alp = alp_payload_rsp_fprop(alp, fid, hdr); _modem_ref_rsp(alp, false, id); } void modem_ref_respond_read(uint8_t fid, void* data, uint32_t offset, uint32_t length, int id) { alp_pub_payload_t* alp = NULL; alp = alp_payload_rsp_f_data(alp, fid, data, offset, length); _modem_ref_rsp(alp, false, id); }