Newer
Older
mbed-os / targets / TARGET_Samsung / TARGET_SIDK_S5JS100 / modem / shmem_save.cpp
/****************************************************************************
 *
 * Copyright 2020 Samsung Electronics All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed 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 "mbed.h"
#include "shmem_save.h"
#include "modem_io_device.h"
#include "s5js100_pwr.h"
#include "sflash_api.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define FLASH_CS_BASE_OLD    0x40EAA000
#define FLASH_NV_BASE_OLD    0x40E47000
#define FLASH_CAL_BASE_OLD    0x40E87000

#define FLASH_CS_BASE        0x4059B000
#define FLASH_NV_BASE        0x4055B000
#define FLASH_CAL_BASE       0x4001B000

#define FLASH_CS_SIZE            (1024 * 300)
#define FLASH_NV_SIZE            (1024 * 256)
#define FLASH_CAL_SIZE            (1024 * 128)

#define REQ_CS_MSG            0x0000A20B
#define REQ_NV_MSG            0x0000A21B
#define REQ_CAL_MSG            0x0000A22B

#define BUFFER_SIZE            256
#define SFLASH_SECTOR_SIZE        256

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Global Variables
 ****************************************************************************/
static ModemIoDevice *shmem_mio;
static unsigned int cur_save_target;
static unsigned char *save_cached;
/****************************************************************************
 * Private Data
 ****************************************************************************/

/****************************************************************************
 * Public Data
 ****************************************************************************/
extern int nbsleep_req;
unsigned int g_flash_cs_base;
unsigned int g_flash_nv_base;
unsigned int g_flash_cal_base;

unsigned int g_flash_cs_size = FLASH_CS_SIZE;
unsigned int g_flash_nv_size = FLASH_NV_SIZE;
unsigned int g_flash_cal_size = FLASH_CAL_SIZE;

/****************************************************************************
 * Private Functions
 ****************************************************************************/

#define SHMEM_SECTION_MAGIC        (0x06313184)
typedef struct {
    unsigned int magic0;
    unsigned int version;
    unsigned int start;
    unsigned int magic1;
} shmem_section_data;

static shmem_section_data next_shmem_section[3] = {
    {SHMEM_SECTION_MAGIC, 1, g_flash_cs_base + (48 * 1024), SHMEM_SECTION_MAGIC},
    {SHMEM_SECTION_MAGIC, 1, g_flash_nv_base + (48 * 1024), SHMEM_SECTION_MAGIC},
    {SHMEM_SECTION_MAGIC, 1, g_flash_cal_base + (48 * 1024), SHMEM_SECTION_MAGIC},
};

static shmem_section_data *find_section_data(unsigned int start, unsigned int size)
{
    unsigned int a, max;
    shmem_section_data *d, *m;

    max = 0;
    m = NULL;
    for (a = start; a < start + size; a += 0x1000) {
        d = (shmem_section_data *)(a + 0x1000 - sizeof(shmem_section_data));
        if (d->magic0 != 0x06313184 || d->magic1 != 0x06313184) {
            continue;
        }

        if (d->version > max) {
            max = d->version;
            m = d;
        }
    }

    return m;
}

extern uint32_t s5js100_sflash_read_capacity(void);

void shmem_get_data(unsigned int *cs, unsigned int *nv, unsigned int *cal)
{
    struct {
        unsigned int start;
        unsigned int size;
    } section[3];
    unsigned int i;
    shmem_section_data *m;
    unsigned int ret[3] = {0, 0, 0};
    //unsigned int FLASH16MB = 0, oldstart, oldsize;
    //unsigned int psize, poffset;
    //unsigned char *tmpbuf;
    //shmem_section_data *tmpsection;

    //initialize SHMEM section depending on FLASH capacity
    if (s5js100_sflash_read_capacity() == 16 * 1024 * 1024) {
        g_flash_cs_base = FLASH_CS_BASE_OLD;
        g_flash_nv_base = FLASH_NV_BASE_OLD;
        g_flash_cal_base = FLASH_CAL_BASE_OLD;
    } else if (s5js100_sflash_read_capacity() == 8 * 1024 * 1024) {
        g_flash_cs_base = FLASH_CS_BASE;
        g_flash_nv_base = FLASH_NV_BASE;
        g_flash_cal_base = FLASH_CAL_BASE;
    } else {
        mbed_error_printf("no support FLASH size..\n");
    }

    section[0].start = g_flash_cs_base;
    section[1].start = g_flash_nv_base;
    section[2].start = g_flash_cal_base;

    section[0].size = g_flash_cs_size;
    section[1].size = g_flash_nv_size;
    section[2].size = g_flash_cal_size;

    for (i = 0; i < 3; i++) {
        m = find_section_data(section[i].start, section[i].size);

        if (m == NULL) {
            ret[i] = section[i].start;
            next_shmem_section[i].start = section[i].start + (48 * 1024);
        } else {
            ret[i] = m->start;
            next_shmem_section[i].start = (unsigned int)m + sizeof(shmem_section_data);
            next_shmem_section[i].version = m->version + 1;
        }
    }

    *cs = ret[0];
    *nv = ret[1];
    *cal = ret[2];
}

int shmem_data_save(unsigned int msg, uint8_t *buffer, uint16_t real_len, uint32_t t_size, uint32_t s_size)
{
    unsigned int flash_start_addr;
    unsigned int offset;
    unsigned int poffset;
    unsigned int psize;
    unsigned char *buf;
    unsigned int cached_size;
    unsigned int wlen;
    unsigned int wsize;
    unsigned int section_idx;

    switch (msg) {
        case REQ_CS_MSG:
            section_idx = 0;
            if (next_shmem_section[0].start + t_size + sizeof(shmem_section_data) < g_flash_cs_base + FLASH_CS_SIZE) {
                flash_start_addr = next_shmem_section[0].start;
            } else {
                flash_start_addr = g_flash_cs_base;
            }
            next_shmem_section[0].start = flash_start_addr;
            break;
        case REQ_NV_MSG:
            section_idx = 1;
            if (next_shmem_section[1].start + t_size + sizeof(shmem_section_data) < g_flash_nv_base + FLASH_NV_SIZE) {
                flash_start_addr = next_shmem_section[1].start;
            } else {
                flash_start_addr = g_flash_nv_base;
            }
            next_shmem_section[1].start = flash_start_addr;
            break;
        case REQ_CAL_MSG:
            section_idx = 2;
            if (next_shmem_section[2].start + t_size + sizeof(shmem_section_data) < g_flash_cal_base + FLASH_CAL_SIZE) {
                flash_start_addr = next_shmem_section[2].start;
            } else {
                flash_start_addr = g_flash_cal_base;
            }
            next_shmem_section[2].start = flash_start_addr;
            break;
        default:
            return -1;
    }

    psize = up_progmem_blocksize();
    offset = s_size;
    cached_size = s_size & (psize - 1);
    wlen = 0;

    while (1) {
        wsize = (real_len - wlen  + cached_size > psize) ? psize - cached_size : real_len - wlen;

        if (wsize != psize) {
            memcpy(save_cached + cached_size, buffer + wlen, wsize);
            buf = save_cached;
        } else {
            buf = buffer + wlen;
        }
        cached_size += wsize;
        wlen += wsize;
        poffset = (offset / psize) * psize;
        offset += wsize;

        if (cached_size != psize) {
            break;
        }

        cached_size = 0;
        sflash_erase(flash_start_addr + poffset);
        sflash_write(flash_start_addr + poffset, buf, psize);
    }

    if (real_len + s_size >= t_size) {
        poffset = (offset / psize) * psize;
        if (cached_size + sizeof(shmem_section_data) <= psize) {
            memcpy(save_cached + 0xFF0, (unsigned char *)(&(next_shmem_section[section_idx])), 0x10);
            sflash_erase(flash_start_addr + poffset);
            sflash_write(flash_start_addr + poffset, save_cached, psize);
        } else {
            sflash_erase(flash_start_addr + poffset);
            sflash_write(flash_start_addr + poffset, save_cached, cached_size);
            memcpy(save_cached + 0xFF0, (unsigned char *)(&(next_shmem_section[section_idx])), 0x10);
            sflash_erase(flash_start_addr + poffset + psize);
            sflash_write(flash_start_addr + poffset, save_cached, psize);
        }
    }

    return 0;
}

void shmem_factory_reset(void)
{
    unsigned int flash_start_addr;
    unsigned int poffset = 0;
    /* erase NV */
    flash_start_addr = g_flash_nv_base;
    for (poffset = 0; poffset < 64; poffset++) {
        sflash_erase(flash_start_addr + poffset * 4096);
    }

    /* erase CS */
    flash_start_addr = g_flash_cs_base;
    for (poffset = 0; poffset < 75; poffset++) {
        sflash_erase(flash_start_addr + poffset * 4096);
    }
}

void shmem_save_read_cb(mio_buf *buf, void *data);
void shmem_initiate_cb(mio_buf *buf, void *data)
{
    save_cached = (unsigned char *)malloc(4096);

    shmem_mio->register_ReadCb(shmem_save_read_cb, NULL);
    shmem_mio->write((char *)(&cur_save_target), 4);
    free_mio_buf(buf);
}

void shmem_save_read_cb(mio_buf *buf, void *data)
{
    uint16_t msg_len;
    uint32_t remain_len, real_len;
    uint8_t *buffer = (uint8_t *)(buf->data);
    uint32_t total_size, sent_size;
    unsigned long long nbsleep_sec;

    msg_len = *(uint16_t *)(buffer + 2);

    if (msg_len != 0) {
        real_len = msg_len - 8;
        total_size = *(uint32_t *)(buffer + 4);
        sent_size = *(uint32_t *)(buffer + 8);
        remain_len = *(uint32_t *)(buffer + 4) - *(uint32_t *)(buffer + 8);

        shmem_data_save(cur_save_target, buffer + 12, real_len, total_size, sent_size);
    } else {
        remain_len  = 0;
        real_len = 0;
    }

    free_mio_buf(buf);

    if (remain_len == real_len) {
        if (cur_save_target == REQ_CAL_MSG) {
            free(save_cached);
            cur_save_target = REQ_CS_MSG;
            shmem_mio->register_ReadCb(shmem_initiate_cb, NULL);
            nbsleep_req = 1;
            nbsleep_sec = ((((unsigned long long)(getreg32(0x81000000 + 0xE4))) << 32) + getreg32(0x81000000 + 0xE8)) / 32768;
        }

        cur_save_target += 0x10;
        if (cur_save_target > REQ_CAL_MSG) {
            cur_save_target = REQ_CS_MSG;
            shmem_mio->register_ReadCb(shmem_initiate_cb, NULL);
        }
    }

    shmem_mio->write((char *)(&cur_save_target), 4);
}

extern "C" {
    void shmem_save_init(void)
    {
        char mio_name[32] = "shmem_save";

        cur_save_target = REQ_CS_MSG;
        shmem_mio = getModemIoDeviceByName(mio_name);
        shmem_mio->register_ReadCb(shmem_initiate_cb, NULL);
    }

    void shmem_save_stop(void)
    {

    }
}