/**************************************************************************** * drivers/modem/alt1250/altcom_pkt.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include "altcom_pkt.h" /**************************************************************************** * Private Types ****************************************************************************/ struct calculated_checksum { uint16_t header_checksum; uint16_t body_checksum; }; /**************************************************************************** * Private Data ****************************************************************************/ static uint8_t g_poweron_cmd[sizeof(struct altcom_cmdhdr_s) + sizeof(struct altcom_cmdfooter_s)]; static uint8_t g_seqid; static uint16_t g_transid; /**************************************************************************** * Private Functions ****************************************************************************/ static uint16_t calc_checksum_v1(FAR uint8_t *ptr, uint16_t len) { uint32_t ret = 0x00; uint16_t calctmp = 0x00; uint16_t i; int is_odd = len & 0x01; for (i = 0; i < (len & 0xfffe); i += sizeof(uint16_t)) { calctmp = *((FAR uint16_t *)(ptr + i)); ret += ntohs(calctmp); } if (is_odd) { ret += *(ptr + i) << 8; } ret = ~((ret & 0xffff) + (ret >> 16)); return (uint16_t)ret; } static uint16_t calc_checksum_v4(FAR uint8_t *ptr, uint16_t len) { uint32_t ret = 0x00; uint32_t calctmp = 0x00; uint16_t i; /* Data accumulating */ for (i = 0; i < len; i++) { calctmp += ptr[i]; } ret = ~((calctmp & 0xffff) + (calctmp >> 16)); return (uint16_t)ret; } static inline void set_header_top(FAR struct altcom_cmdhdr_s *hdr, uint8_t ver, uint16_t cid) { hdr->magic = htonl(ALTCOM_HDR_MAGICNUMBER); hdr->ver = ver; hdr->seqid = g_seqid++; hdr->cmdid = htons(cid); hdr->transid = htons(g_transid++); hdr->datalen = 0; } static struct calculated_checksum convert_ntoh( FAR struct altcom_cmdhdr_s *hdr) { struct calculated_checksum checksum; hdr->cmdid = ntohs(hdr->cmdid); hdr->transid = ntohs(hdr->transid); hdr->datalen = ntohs(hdr->datalen); if (hdr->ver == ALTCOM_VER1) { /* Only V1 has footer */ hdr->v1_options = ntohs(hdr->v1_options); hdr->v1_checksum = ntohs(hdr->v1_checksum); struct altcom_cmdfooter_s *footer = (struct altcom_cmdfooter_s *)&hdr->payload[hdr->datalen]; checksum.body_checksum = calc_checksum_v1(&hdr->payload[0], sizeof(struct altcom_cmdfooter_s)-2 + hdr->datalen); footer->reserve = ntohs(footer->reserve); footer->checksum = ntohs(footer->checksum); } else { hdr->v4_hdr_cksum = ntohs(hdr->v4_hdr_cksum); checksum.body_checksum = calc_checksum_v4(&hdr->payload[0], hdr->datalen); hdr->v4_data_cksum = ntohs(hdr->v4_data_cksum); } return checksum; } static int check_valid_pkt(FAR struct altcom_cmdhdr_s *hdr, uint16_t body_checksum) { int valid_version = ALTCOM_VERX; if (hdr->ver == ALTCOM_VER1) { struct altcom_cmdfooter_s *footer = (struct altcom_cmdfooter_s *)&hdr->payload[hdr->datalen]; if (footer->checksum == body_checksum) { valid_version = ALTCOM_VER1; } else { m_err("[V1] cmdid: 0x%04x cmdlen: %d, " "body checksum: 0x%04x/0x%04x\n", hdr->cmdid, hdr->datalen, footer->checksum, body_checksum); } } else if(hdr->ver == ALTCOM_VER4) { if (hdr->v4_data_cksum == body_checksum) { valid_version = ALTCOM_VER4; } else { m_err("[V4] cmdid: 0x%04x cmdlen: %d, " "body checksum: 0x%04x/0x%04x\n", hdr->cmdid, hdr->datalen, hdr->v4_data_cksum, body_checksum); } } return valid_version; } static int check_valid_reply(FAR struct altcom_cmdhdr_s *hdr, uint16_t body_checksum) { int valid_version = ALTCOM_VERX; valid_version = check_valid_pkt(hdr, body_checksum); if (valid_version == ALTCOM_VER1) { if ((hdr->cmdid == (ALTCOM_CMDID_POWER_ON_V1 | ALTCOM_CMDID_REPLY_BIT)) && (hdr->datalen == ALTCOM_CMD_POWER_ON_REPLY_SIZE)) { valid_version = ALTCOM_VER1; } else { valid_version = ALTCOM_VERX; } } else if(valid_version == ALTCOM_VER4) { if ((hdr->cmdid == (ALTCOM_CMDID_POWER_ON_V4 | ALTCOM_CMDID_REPLY_BIT)) && (hdr->datalen == ALTCOM_CMD_POWER_ON_REPLY_SIZE)) { valid_version = ALTCOM_VER4; } else { valid_version = ALTCOM_VERX; } } return valid_version; } static bool is_header_ok(FAR struct altcom_cmdhdr_s *hdr) { uint16_t checksum; if (hdr->ver == ALTCOM_VER1) { checksum = calc_checksum_v1((FAR uint8_t *)hdr, sizeof(struct altcom_cmdhdr_s) - sizeof(hdr->v1_checksum)); if (ntohs(hdr->v1_checksum) == checksum) { if (ntohs(hdr->datalen) + sizeof(struct altcom_cmdhdr_s) + sizeof(struct altcom_cmdfooter_s) <= ALTCOM_RX_PKT_SIZE_MAX) { return true; } else { m_err("[V1] Data length exceeding the buffer length: %d\n", ntohs(hdr->datalen)); } } else { m_err("[V1] cmdid: 0x%04x cmdlen: %d, " "header checksum: 0x%04x/0x%04x\n", ntohs(hdr->cmdid), ntohs(hdr->datalen), ntohs(hdr->v1_checksum), checksum); } } else if (hdr->ver == ALTCOM_VER4) { checksum = calc_checksum_v4((FAR uint8_t *)hdr, sizeof(struct altcom_cmdhdr_s) - sizeof(hdr->v4_hdr_cksum) - sizeof(hdr->v4_data_cksum)); if (ntohs(hdr->v4_hdr_cksum) == checksum) { if (ntohs(hdr->datalen) + sizeof(struct altcom_cmdhdr_s) <= ALTCOM_RX_PKT_SIZE_MAX) { return true; } else { m_err("[V4] Data length exceeding the buffer length: %d\n", ntohs(hdr->datalen)); } } else { m_err("[V4] cmdid: 0x%04x cmdlen: %d, " "header checksum: 0x%04x/0x%04x\n", ntohs(hdr->cmdid), ntohs(hdr->datalen), ntohs(hdr->v4_hdr_cksum), checksum); } } return false; } /**************************************************************************** * Public Functions ****************************************************************************/ bool altcom_is_v1pkt_ok(FAR struct altcom_cmdhdr_s *cmdhdr) { struct calculated_checksum checksum; if (!is_header_ok(cmdhdr)) { return false; } checksum = convert_ntoh(cmdhdr); return (check_valid_reply(cmdhdr, checksum.body_checksum) == ALTCOM_VER1) && (cmdhdr->payload[0] == LTE_RESULT_OK); } bool altcom_is_v4pkt_ok(FAR struct altcom_cmdhdr_s *cmdhdr) { struct calculated_checksum checksum; if (!is_header_ok(cmdhdr)) { return false; } checksum = convert_ntoh(cmdhdr); return (check_valid_reply(cmdhdr, checksum.body_checksum) == ALTCOM_VER4) && (cmdhdr->payload[0] == LTE_RESULT_OK); } FAR void *altcom_make_poweron_cmd_v1(int *sz) { FAR struct altcom_cmdhdr_s *hdr = (FAR struct altcom_cmdhdr_s *)g_poweron_cmd; FAR struct altcom_cmdfooter_s *footer = (FAR struct altcom_cmdfooter_s *)&hdr->payload[0]; set_header_top(hdr, ALTCOM_VER1, ALTCOM_CMDID_POWER_ON_V1); hdr->v1_options = htons(ALTCOM_CMDOPT_CHECKSUM_EN); hdr->v1_checksum = htons(calc_checksum_v1((FAR uint8_t *)hdr, sizeof(struct altcom_cmdhdr_s) - 2)); footer->reserve = 0; footer->checksum = htons(calc_checksum_v1(&hdr->payload[0], sizeof(struct altcom_cmdfooter_s) - 2)); /* No payload of this altcom command. * So sending size is just header and footer size */ *sz = sizeof(struct altcom_cmdhdr_s) + sizeof(struct altcom_cmdfooter_s); return g_poweron_cmd; } FAR void *altcom_make_poweron_cmd_v4(int *sz) { FAR struct altcom_cmdhdr_s *hdr = (FAR struct altcom_cmdhdr_s *)g_poweron_cmd; set_header_top(hdr, ALTCOM_VER4, ALTCOM_CMDID_POWER_ON_V4); hdr->v4_hdr_cksum = htons(calc_checksum_v4((FAR uint8_t *)hdr, sizeof(struct altcom_cmdhdr_s) - 4)); hdr->v4_data_cksum = htons(calc_checksum_v4(&hdr->payload[0], 0)); /* No payload of this altcom command. So sending size is just header size */ *sz = sizeof(struct altcom_cmdhdr_s); return g_poweron_cmd; } int altcom_is_pkt_ok(FAR uint8_t *pkt, int sz) { struct calculated_checksum checksum; int ver; int ret = OK; FAR struct altcom_cmdhdr_s *hdr = (FAR struct altcom_cmdhdr_s *)pkt; int remlen; if (!is_header_ok(hdr)) { return -EPROTO; } remlen = get_pktlen(hdr->ver, ntohs(hdr->datalen)) - sz; if (remlen > 0) { /* Cases in which fragmented packets are received. */ return remlen; } else if (remlen < 0) { /* The case where the received data length becomes * larger than the payload length set in the header. */ return -EPROTO; } /* Whole packets are received. So check the validity of the packets. */ checksum = convert_ntoh((FAR struct altcom_cmdhdr_s *)pkt); ver = check_valid_pkt((FAR struct altcom_cmdhdr_s *)pkt, checksum.body_checksum); if (ver == ALTCOM_VERX) { ret = -EPROTO; } return ret; } uint16_t altcom_make_header(FAR struct altcom_cmdhdr_s *hdr, uint8_t ver, uint16_t cid, uint16_t sz) { uint16_t tid = g_transid; hdr->magic = htonl(ALTCOM_HDR_MAGICNUMBER); hdr->ver = ver; hdr->seqid = g_seqid++; hdr->cmdid = htons(cid); hdr->transid = htons(g_transid++); hdr->datalen = htons(sz); if (ver == ALTCOM_VER1) { FAR struct altcom_cmdfooter_s *footer = (FAR struct altcom_cmdfooter_s *)&hdr->payload[sz]; hdr->v1_options = htons(ALTCOM_CMDOPT_CHECKSUM_EN); hdr->v1_checksum = htons(calc_checksum_v1((FAR uint8_t *)hdr, sizeof(struct altcom_cmdhdr_s) - 2)); footer->reserve = 0; footer->checksum = htons(calc_checksum_v1(&hdr->payload[0], sizeof(struct altcom_cmdfooter_s) - 2 + sz)); } else if (ver == ALTCOM_VER4) { hdr->v4_hdr_cksum = htons(calc_checksum_v4((FAR uint8_t *)hdr, sizeof(struct altcom_cmdhdr_s)-4)); hdr->v4_data_cksum = htons(calc_checksum_v4(&hdr->payload[0], sz)); } else { DEBUGASSERT(0); } return tid; }