Trond Enger / d7a_1x

Fork of d7a_1x by WizziLab

src/d7a_fs.cpp

Committer:
Jeej
Date:
2016-08-25
Revision:
38:c1e7f97ab396
Parent:
34:1311cc53201a
Child:
41:a924aa709b6f
Child:
43:28202405094d

File content as of revision 38:c1e7f97ab396:

#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;
    int8_t   status;
    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, osPriorityHigh, 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_post_msg(&msg);
}

int8_t d7a_fs_wait_done(const uint8_t id, uint32_t millisec)
{
    FPRINT("(id:%d, to:%d)\r\n", id, millisec);
    
    osEvent evt;
    do
    {
        evt = g_fs_ctx.fs_done.get(millisec);
        ASSERT(evt.status == osEventMessage, "FS Wait Timeout!\r\n");
        if ((uint8_t)evt.value.p != id)
        {
            g_fs_ctx.fs_done.put(evt.value.p);
        }
    } while ((uint8_t)evt.value.p != id);

    // Free to serve a new request
    g_fs_ctx.com[id].req.cmd = FS_OP_NULL;
    
    return g_fs_ctx.com[id].status;
}

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;
}

int d7a_fs_distant_stat(uint8_t fid, int* length)
{
    FPRINT("\r\n");
    // "Distant" Files
    uint8_t id = d7a_fs_get_req_id();
    DPRINT("StatD[%d](%d)\r\n", fid, 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
    
    return (int)d7a_fs_wait_done(id, TO_FS);
}

int d7a_fs_distant_create(uint8_t fid, d7a_fs_properties_t* props)
{
    uint8_t id;
    int status = 0;
    FPRINT("\r\n");
    //DPRINT("CrD[%d] type 0x%x\n",fid,props->type);
    
    
    
    switch (FS_PHY_TYPE(props->type))
    {
        case EEPROM:
        case RAM:
        case PFLASH:
            id = d7a_fs_get_req_id();
            DPRINT("CrD[%d](%d)\r\n", fid, 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);
            status = (int)d7a_fs_wait_done(id, TO_FS);
            break;
        // Wrong type
        default:
            ASSERT(false, "FS:post_distant_create wrong file type %x\n", FS_PHY_TYPE(props->type));
            break;
    }
    
    return status;
}

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;
    int status = 0;
    uint8_t id;

    if (!d7a_fs_hdr(fid, &hdr))
    {
        // If file is not found locally, it is a distant file
        hdr.bf.type |= FS_TYPE_DISTANT;
    }
    
    if(hdr.bf.type & FS_TYPE_DISTANT) // XXX: HOST only
    {
        id = d7a_fs_get_req_id();
        DPRINT("RdP[%d](%d) @:%d %d bytes\r\n", fid, id, offset, length);
        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);
        status = (int)d7a_fs_wait_done(id, TO_FS);
    }
    else
    {
        DPRINT("RdP[%d](-) @:%d %d bytes\r\n", fid, offset, length);
        status = (g_fs_ctx.callback->read_file(fid, offset, length, (uint8_t*)data) == length)?
            FS_STAT_OK : FS_STAT_ERR_UNKNOWN;
    }

    return status;
}



#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](%d) cmd:%d\n", req->fid, req->id, 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 (FS_COM_TYPE(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.
                            props->type += HOST; // HOST, but keep physical type as well
                            //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;
                }
                
                // Inform requester
                g_fs_ctx.com[id].status = status;
                g_fs_ctx.fs_done.put((void*)id);
                
                break;
            default:
                EPRINT("FS Unknown Flow ID 0x%02X\r\n", pkt->id);
                break;
        }
        FREE(pkt);
    }
}