Trond Enger / d7a_1x

Fork of d7a_1x by WizziLab

src/d7a_fs.cpp

Committer:
Jeej
Date:
2016-05-25
Revision:
30:d775c1409849
Parent:
29:8e7c5c1e9aab
Child:
31:ab9bfdbc6b44

File content as of revision 30:d775c1409849:

#include "mbed.h"
#include "rtos.h"
#include "dbg.h"
#include "d7a_com.h"
#include "d7a_common.h"
#include "d7a_fs.h"


#define FS_MAX_FILE_QTY     256
#define FS_MAX_MIRROR_QTY   32
#define FS_MAX_WATCHERS_QTY 32

#define FS_NULL_MAP             0xFF // XXX: this actually removes usage of file 255

TYPEDEF_STRUCT_PACKED {
    uint8_t  id;
    uint8_t  cmd;
    uint8_t  fid;
    uint32_t offset;
    uint32_t length;
} d7a_fs_com_req_t;

TYPEDEF_STRUCT_PACKED {
    uint8_t  id;
    uint8_t  cmd;
    int8_t   status;
    uint32_t length;
    uint8_t  data[1];
} d7a_fs_com_resp_t;

typedef struct {
    d7a_fs_com_req_t req;
    void* data;
} d7a_fs_com_t;

typedef struct {
    Thread* thread;
    Queue<void, 8> fs_done;
    const d7a_fs_callbacks_t* callback;
    Queue<d7a_com_rx_msg_t, 16> pkt_queue;
    uint8_t  hdr_map[FS_MAX_FILE_QTY];
    uint8_t  nb_files;
    d7a_fs_com_t com[FS_MAX_CONCURRENCY];
} d7a_fs_ctx_t;

d7a_fs_ctx_t g_fs_ctx;

void d7a_fs_thread(const void *p);

void d7a_fs_open(const d7a_fs_callbacks_t* config)
{
    FPRINT("\r\n");
    
    memset(&g_fs_ctx.com, 0, sizeof(g_fs_ctx.com));
    g_fs_ctx.callback = config;
    g_fs_ctx.thread = new Thread(d7a_fs_thread, NULL, osPriorityBelowNormal, DEFAULT_STACK_SIZE);
}

static uint8_t d7a_fs_get_req_id(void)
{
    int i;
    for (i=0;i<FS_MAX_CONCURRENCY;i++)
    {
        if (g_fs_ctx.com[i].req.cmd == FS_OP_NULL)
        {
            DPRINT("New req %d\r\n", i);
            return i;
        }
    }
    ASSERT(false, "FS: MAX_CONCURRENCY exceeded\r\n");
    return 0xff;
}


static void d7a_fs_msg(uint8_t* buf, uint8_t len, uint8_t id)
{
    FPRINT("\r\n");
    d7a_com_tx_msg_t msg;
    msg.id = id;
    msg.pbuf = buf;
    msg.plen = len;
    msg.alen = 0;
    d7a_com_send_msg(&msg);
}

void* d7a_fs_wait_done( uint32_t millisec )
{
    FPRINT("\r\n");
    void* ret;
    osEvent evt = g_fs_ctx.fs_done.get(millisec);
    if (evt.status == osEventMessage)
    {
        ret = (void*)evt.value.p;
    }
    else
    {
        ret = (void*)0xFFFFFFFF;
        EPRINT("FS Wait Timeout!\r\n");
    }
    return ret;
}

void d7a_fs_new_pkt(d7a_com_rx_msg_t* pkt)
{
    FPRINT("\r\n");
    ASSERT(g_fs_ctx.pkt_queue.put(pkt) == osOK, "FS queue full!\r\n");
}

d7a_com_rx_msg_t* d7a_fs_wait_pkt( uint32_t millisec )
{
    FPRINT("(millisec:%d)\r\n", millisec);
    osEvent evt = g_fs_ctx.pkt_queue.get(millisec);
    return (evt.status == osEventMessage)? (d7a_com_rx_msg_t*)evt.value.p : NULL;
}

static uint32_t d7a_fs_hdr(uint8_t fid, const d7a_fs_header_t* hdr)
{
    FPRINT("\r\n");
    d7a_fs_header_t* temp_hdr = g_fs_ctx.callback->get_stat(fid);
    if (!temp_hdr)
    {
        return 0;
    }
    memcpy((void*)hdr, (void*)temp_hdr, sizeof(d7a_fs_header_t));
    return sizeof(d7a_fs_header_t);
}

uint32_t d7a_fs_get_properties(uint8_t fid, d7a_fs_property_t prop, d7a_fs_properties_t* props)
{
    FPRINT("(fid:%d, prop:0x%02X)\r\n", fid, prop);
    d7a_fs_header_t hdr;
    d7a_fs_hdr(fid, &hdr);
    
    uint32_t ret = 0;
    if (prop == KAL_FS_PROP_ALL)
    {
        // Fill in the API structure
        //props->addr     = (uint32_t)d7a_fs_get_memory_addr(hdr);
        props->type     = hdr.byte.type;

        props->afid     = hdr.byte.afid;
        props->ifid     = hdr.byte.ifid;
        props->prop     = hdr.byte.prop;
        props->perm     = hdr.byte.perm;

        props->length   = hdr.bf.length;
        props->alloc    = hdr.bf.alloc;
    }
    else
    {
        switch(prop)
        {
            case KAL_FS_PROP_SIZE:  ret = hdr.bf.length; break;
            case KAL_FS_PROP_ALLOC: ret = hdr.bf.alloc; break;
            case KAL_FS_PROP_TYPE:  ret = hdr.byte.type; break;
            //case KAL_FS_PROP_ADDR:  ret = (uint32_t)d7a_fs_get_memory_addr(hdr); break;
            case KAL_FS_PROP_AFID:  ret = hdr.byte.afid; break;
            case KAL_FS_PROP_IFID:  ret = hdr.byte.ifid; break;
            case KAL_FS_PROP_PROP:  ret = hdr.byte.prop; break;
            case KAL_FS_PROP_PERM:  ret = hdr.byte.perm; break;
            default:
                ASSERT(false, "FS:Invalid get prop request %d\n",prop);
        }
    }
    //DPRINT("GetProp[%d](%d): %d\n",fid,prop,ret);
    return ret;
}

void d7a_fs_distant_stat(uint8_t fid, int* length)
{
    FPRINT("\r\n");
    // "Distant" Files
    uint8_t id = d7a_fs_get_req_id();
    g_fs_ctx.com[id].req = (d7a_fs_com_req_t){id,FS_OP_STAT,fid,0,0};
    g_fs_ctx.com[id].data = length;
    d7a_fs_msg((uint8_t*)&g_fs_ctx.com[id].req,sizeof(d7a_fs_com_req_t), KAL_COM_FLOW_FS_CMD);
    // We do a "normal" STAT but we don't want to update our local(true) file-size
    // with the response of the STAT as:
    // - the distant is likely a "Host-file" with hazardous size/alloc
    // - we may not even have the corresponding local file (-> assert when getting props)
    // So flag the cmd to differentiate response treatment
    g_fs_ctx.com[id].req.cmd = FS_OP_DSTAT; // XXX
}

int d7a_fs_distant_create(uint8_t fid, d7a_fs_properties_t* props)
{
    uint8_t id;
    FPRINT("\r\n");
    //DPRINT("CrD[%d] type 0x%x\n",fid,props->type);

    switch (props->type)
    {
        case EEPROM:
        case RAM:
        case PFLASH:
        case HOST:
            id = d7a_fs_get_req_id();
            // Build WR command
            uint8_t* buf = (uint8_t*)MALLOC(sizeof(d7a_fs_com_req_t) + sizeof(d7a_fs_properties_t));
            d7a_fs_com_req_t* buf_req = (d7a_fs_com_req_t*)buf;
            *buf_req     = (d7a_fs_com_req_t){id,FS_OP_CREATE,fid,0,sizeof(d7a_fs_properties_t)};
            memcpy(&buf[sizeof(d7a_fs_com_req_t)],(uint8_t*)props,sizeof(d7a_fs_properties_t));

            // Keep command request for response treatment
            g_fs_ctx.com[id].req.cmd = FS_OP_CREATE;
            d7a_fs_msg(buf,sizeof(d7a_fs_com_req_t) + sizeof(d7a_fs_properties_t), KAL_COM_FLOW_FS_CMD);
            FREE(buf);
            break;
        // Wrong type
        default:
            ASSERT(false, "FS:post_distant_create wrong file type %x\n",props->type);
            break;
    }
    return 0;
}

int d7a_fs_read(uint8_t fid, void *data, uint32_t offset, uint32_t length)
{
    //DPRINT("RdP[%d] @:%d %d bytes\n", fid, offset, length);
    d7a_fs_header_t hdr;
    uint8_t local = 0;
    uint8_t id;

    if (!d7a_fs_hdr(fid, &hdr))
    {
        // If file is not found locally, it is a distant file
        hdr.bf.type = HOST;
    }
    
    switch (hdr.bf.type)
    {
        // "Local" files are also supported through "posted" API
        case EEPROM:
        case RAM:
        case PFLASH:
            // "Local" file, respond right away
            d7a_fs_read(fid,data,offset,length);
            g_fs_ctx.callback->read_file(fid, offset, length, (uint8_t*)data);
            // Inform requester
            g_fs_ctx.fs_done.put((void*)fid);
            local = 1;
            break;
        // "Distant" Files
        case HOST:
            id = d7a_fs_get_req_id();
            g_fs_ctx.com[id].req = (d7a_fs_com_req_t){id,FS_OP_RD,fid,offset,length};
            g_fs_ctx.com[id].data = data;
            d7a_fs_msg((uint8_t*)&g_fs_ctx.com[id].req, sizeof(d7a_fs_com_req_t), KAL_COM_FLOW_FS_CMD);
            break;
        // Wrong type
        default:
            ASSERT(false, "FS:post_read wrong file type %x\n", hdr.bf.type);
    }
    return local;
}



#define OP_SIZE_RD      11
#define OP_SIZE_WR      11
#define OP_SIZE_TOUCH   11
#define OP_SIZE_STAT    3
#define OP_SIZE_FLUSH   3
#define OP_SIZE_CREATE  11
#define OP_SIZE_RETSTAT (3+4)
#define OP_SIZE_RETDATA (3+4)
uint8_t k_valid_cmd[] = {FS_OP_RD,  FS_OP_WR,  FS_OP_TOUCH  ,FS_OP_STAT,  FS_OP_FLUSH,  FS_OP_CREATE};
uint8_t k_valid_size[]= {OP_SIZE_RD,OP_SIZE_WR,OP_SIZE_TOUCH,OP_SIZE_STAT,OP_SIZE_FLUSH,OP_SIZE_CREATE};



/// ------------------------------------------------------------------
/// |TOKEN|CMD/RESP|             PAYLOAD                         |
/// --------------------------------------------------------------
/// | 1B  |   1B   |            PKT.blen - 1 bytes               |
/// --------------------------------------------------------------
/// |     |        |  1B  |   4B   |   4B   | PKT.blen - 11 bytes|
/// --------------------------------------------------------------
//  |     | write  | FID  | offset | length | datas...           |
//  |     | read   | FID  | offset | length |
//  |     | touch  | FID  | offset | length |
//  |     | exist  | FID  |
//  |     | flush  | FID  |
//  |     | retstat|status| length | <kal_fs_properties_t>
//  |     | retdat |status| length | datas...                    |
void d7a_fs_thread(const void *p)
{
    FPRINT("\r\n");
    d7a_com_rx_msg_t* pkt;
    int8_t status = FS_STAT_OK;
    
    while (true)
    {
        status = FS_STAT_OK;
        // TODO: For now only one outgoing-request can be pending
        //       An incoming-request response can be served simultaneously
        // TODO: handle segmentation. For now MTU = 255-10 = 245
        pkt = d7a_fs_wait_pkt();
        ASSERT(pkt != NULL, "FS NULL pkt\r\n");
        // ---------------------------------------------------------------------------
        // Peer request
        // ---------------------------------------------------------------------------
        switch (pkt->id)
        {
            case KAL_COM_FLOW_FS_CMD:
                d7a_fs_com_req_t* req = (d7a_fs_com_req_t*)pkt->buffer;
                DPRINT("Rfs[%d] cmd:%d\n",req->fid,req->cmd);
                //dbg_print_data("%02X ", (uint8_t*)req, sizeof(d7a_fs_com_req_t));
                //DPRINT("\r\n");
                d7a_fs_com_resp_t buf = (d7a_fs_com_resp_t) {.id = req->id, .cmd = FS_OP_RETSTAT};
    
                // Error management makes sense here as we face peer-requests,
                // and we don't want to assert because of the peer.
                //status = d7a_fs_check_req(req, pkt->blen);
                //buf.status = status;
                buf.status = FS_STAT_OK;
    
                // From here we assume that the request is valid
                // TODO: for now we consider that COM-FS request are targetting
                // FIDs that are "local" to the Host, but we can extend this
                // to "distant" files as well.(i.e. access SPI-Flash of an Host)
                if (status != FS_STAT_OK)
                {
                    buf.length = 0;
                    // Here we respond to request errors and OP_STAT
                    d7a_fs_msg((uint8_t*)&buf,OP_SIZE_RETSTAT, KAL_COM_FLOW_FS_RESP);
                }
                else if (req->cmd == FS_OP_RD)
                {
                    d7a_fs_com_resp_t* b = (d7a_fs_com_resp_t*)MALLOC(OP_SIZE_RETDATA + req->length);
                    uint32_t len = g_fs_ctx.callback->read_file(req->fid, req->offset, req->length, &b->data[0]);
                    b->id     = req->id;
                    b->length = len;
                    b->cmd    = FS_OP_RETDATA;
                    b->status = FS_STAT_OK; // TODO
                    d7a_fs_msg((uint8_t*)b, OP_SIZE_RETDATA + req->length, KAL_COM_FLOW_FS_RESP);
                    FREE(b);
                }
                else if (req->cmd == FS_OP_WR)
                {
                    buf.length = g_fs_ctx.callback->write_file(req->fid, req->offset, req->length, &pkt->buffer[OP_SIZE_WR]);
                    d7a_fs_msg((uint8_t*)&buf, OP_SIZE_RETSTAT, KAL_COM_FLOW_FS_RESP);
                }
                else if (req->cmd == FS_OP_STAT)
                {
                    // If file doesn't exist, FS_OP_STAT response is already handled
                    // by the common d7a_fs_check_req error response.
                    d7a_fs_com_resp_t* b = (d7a_fs_com_resp_t*)MALLOC(OP_SIZE_RETSTAT + sizeof(d7a_fs_properties_t));
                    d7a_fs_get_properties(req->fid, KAL_FS_PROP_ALL, (d7a_fs_properties_t*) &b->data[0]);
                    d7a_fs_properties_t* props = (d7a_fs_properties_t*)&b->data[0];
                    b->id     = req->id;
                    b->length = props->length;
                    b->cmd    = FS_OP_RETSTAT;
                    b->status = FS_STAT_OK;
                    d7a_fs_msg((uint8_t*)b, OP_SIZE_RETSTAT + sizeof(d7a_fs_properties_t), KAL_COM_FLOW_FS_RESP);
                    FREE(b);
                }
                else if (req->cmd == FS_OP_FLUSH)
                {
                    //d7a_fs_flush(req->fid);
                    //d7a_fs_msg((uint8_t*)&buf, OP_SIZE_RETSTAT, KAL_COM_FLOW_FS_RESP);
                }
                else if (req->cmd == FS_OP_CREATE)
                {
                    //d7a_fs_create(req->fid,(d7a_fs_properties_t*)&pkt->buffer[OP_SIZE_CREATE]);
                    //d7a_fs_msg((uint8_t*)&buf, OP_SIZE_RETSTAT, KAL_COM_FLOW_FS_RESP);
                }
                else if (req->cmd == FS_OP_TOUCH)
                {
                    buf.length = g_fs_ctx.callback->touch_file(req->fid, req->offset, req->length);
                    d7a_fs_msg((uint8_t*)&buf, OP_SIZE_RETSTAT, KAL_COM_FLOW_FS_RESP);
                }
                else
                {
                    ASSERT(false, "FS: Unknown cmd %d\r\n", req->cmd);
                }
                break;
            // ---------------------------------------------------------------------------
            // Response to our commands
            // ---------------------------------------------------------------------------
            case KAL_COM_FLOW_FS_RESP:
                d7a_fs_com_resp_t* resp = (d7a_fs_com_resp_t*)pkt->buffer;
                uint8_t id = resp->id;
                DPRINT("Rfs(%d) resp - cmd:%d status:%d bytes:%d\n", id, resp->cmd, resp->status, resp->length);
                //dbg_print_data("%02X ", (uint8_t*)resp, sizeof(d7a_fs_com_resp_t));
                //DPRINT("\r\n");
                ASSERT(g_fs_ctx.com[id].req.cmd != FS_OP_NULL, "FS: Unexpected FS_RESP ID:%d CMD:%d status:%d LEN:%d\n", id, resp->cmd, resp->status, resp->length);
                if (g_fs_ctx.com[id].req.cmd != FS_OP_STAT &&
                    g_fs_ctx.com[id].req.cmd != FS_OP_DSTAT )
                {
                    // No pity for errors here, handled-ones should be avoided at
                    // upper level, the others diserve to assert anyway.
                    ASSERT(resp->status == FS_STAT_OK, "FS: Peer error:%d\n", resp->status);
    
                    // Fill read buffer
                    if (resp->cmd == FS_OP_RETDATA)
                    {
                        memcpy(g_fs_ctx.com[id].data, &resp->data[0], resp->length);
                    }
                    else if (resp->cmd == FS_OP_RETSTAT)
                    {
                        if (g_fs_ctx.com[id].req.cmd == FS_OP_WR)
                        {
                            // We did a successful WR, update size if needed
                            // TODO:This is not really enforcing header coherency. It will do the
                            // job for Hosted NVM storage, provided the Host doesn't use the file for
                            // writing.
                            d7a_fs_header_t hdr;
                            d7a_fs_hdr(g_fs_ctx.com[id].req.fid, &hdr);
                            if (resp->length != hdr.bf.length)
                            {
                                hdr.bf.length = resp->length;
                                g_fs_ctx.callback->set_stat(g_fs_ctx.com[id].req.fid, &hdr);
                            }
                        }
                    }
                }
                else // FS_OP_STAT/DSTAT
                {
                    if (g_fs_ctx.com[id].req.cmd == FS_OP_STAT)
                    {
                        d7a_fs_header_t hdr;
                        d7a_fs_hdr(g_fs_ctx.com[id].req.fid, &hdr);
                        // Try to keep HOST-files coherency as much as we can:
                        // STAT returns the full header so we can update all fields.
                        if (hdr.byte.type == HOST)
                        {
                            d7a_fs_properties_t* props = (d7a_fs_properties_t*)&resp->data[0];
                            // XXX d7a_fs_set_properties_p is for now keeping our file remote,
                            // as it doesn't change addr,type,mirror etc.
                            //d7a_fs_set_properties(hdr, (uint32_t)props);
                        }
                    }
                    // TODO: for now only keep length info for upper layer. (normal for a STAT)
                    if (g_fs_ctx.com[id].data != NULL)
                    {
                         *(uint32_t*)g_fs_ctx.com[id].data = resp->length;
                    }
                    status = resp->status;
                }
                // Free to serve a new request
                g_fs_ctx.com[id].req.cmd = FS_OP_NULL;
                // Inform requester
                g_fs_ctx.fs_done.put((void*)status);
                break;
            default:
                EPRINT("FS Unknown Flow ID 0x%02X\r\n", pkt->id);
                break;
        }
        FREE(pkt);
    }
}