Trond Enger / d7a_1x

Fork of d7a_1x by WizziLab

Revision:
25:aac250164497
Child:
26:9f0b9833cac6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/d7a_fs.cpp	Fri Mar 25 16:48:02 2016 +0000
@@ -0,0 +1,359 @@
+#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  cmd;
+    uint8_t  fid;
+    uint32_t offset;
+    uint32_t length;
+} d7a_fs_com_req_t;
+
+TYPEDEF_STRUCT_PACKED {
+    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;
+    Semaphore*  fs_done;
+    WriteFileFunction write_file;
+    ReadFileFunction read_file;
+    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; // just one for now
+} d7a_fs_ctx_t;
+
+d7a_fs_ctx_t g_fs_ctx;
+
+void d7a_fs_thread(const void *p);
+
+void d7a_fs_open(WriteFileFunction write_file, ReadFileFunction read_file)
+{
+    FPRINT("\r\n");
+    
+    memset(&g_fs_ctx.com, 0, sizeof(g_fs_ctx.com));
+    g_fs_ctx.write_file = write_file;
+    g_fs_ctx.read_file = read_file;
+    g_fs_ctx.fs_done = new Semaphore(1);
+    g_fs_ctx.thread = new Thread(d7a_fs_thread, NULL, osPriorityBelowNormal, DEFAULT_STACK_SIZE*2);
+    
+    // Wait to consume Semaphore
+    g_fs_ctx.fs_done->wait();
+}
+
+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");
+    g_fs_ctx.fs_done->wait(millisec);
+}
+
+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("\r\n");
+    osEvent evt = g_fs_ctx.pkt_queue.get(millisec);
+    return (evt.status == osEventMessage)? (d7a_com_rx_msg_t*)evt.value.p : NULL;
+}
+
+static void d7a_fs_hdr(uint8_t fid, d7a_fs_header_t* hdr)
+{
+    FPRINT("\r\n");
+    g_fs_ctx.read_file(fid, 0, sizeof(d7a_fs_header_t), (uint8_t*)hdr);
+}
+
+uint32_t d7a_fs_get_properties(uint8_t fid, d7a_fs_property_t prop, d7a_fs_properties_t* props)
+{
+    FPRINT("\r\n");
+    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_create(uint8_t fid, d7a_fs_properties_t* props)
+{
+    FPRINT("\r\n");
+    DPRINT("CrD[%d] type 0x%x\n",fid,props->type);
+
+    switch (props->type)
+    {
+        case EEPROM:
+        case RAM:
+        case PFLASH:
+        case HOST:
+            ASSERT(g_fs_ctx.com.req.cmd == FS_OP_NULL,"FS: Concurrent Distant File access\n");
+            // 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){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.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;
+}
+
+void d7a_fs_post_distant_stat(uint8_t fid, int* length)
+{
+    FPRINT("\r\n");
+    // "Distant" Files
+    ASSERT(g_fs_ctx.com.req.cmd ==FS_OP_NULL,"FS: Concurrent Distant File access\n");
+    g_fs_ctx.com.req = (d7a_fs_com_req_t){FS_OP_STAT,fid,0,0};
+    g_fs_ctx.com.data = length;
+    d7a_fs_msg((uint8_t*)&g_fs_ctx.com.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.req.cmd = FS_OP_DSTAT; // XXX
+}
+
+
+
+#define OP_SIZE_RD      10
+#define OP_SIZE_WR      10
+#define OP_SIZE_STAT    2
+#define OP_SIZE_FLUSH   2
+#define OP_SIZE_CREATE  10
+#define OP_SIZE_RETSTAT (2+4)
+#define OP_SIZE_RETDATA (2+4)
+uint8_t k_valid_cmd[] = {FS_OP_RD,  FS_OP_WR,  FS_OP_STAT,  FS_OP_FLUSH,  FS_OP_CREATE};
+uint8_t k_valid_size[]= {OP_SIZE_RD,OP_SIZE_WR,OP_SIZE_STAT,OP_SIZE_FLUSH,OP_SIZE_CREATE};
+
+
+//  TODO: Add a token field for multiple transactions
+/// --------------------------------------------------------
+/// |CMD/RESP|             PAYLOAD                         |
+/// --------------------------------------------------------
+/// |   1B   |            PKT.blen - 1 bytes               |
+/// --------------------------------------------------------
+/// |        |  1B  |   4B   |   4B   | PKT.blen - 10 bytes|
+/// --------------------------------------------------------
+//  | write  | FID  | offset | length | datas...           |
+//  | read   | FID  | offset | length |
+//  | flush  | FID  |
+//  | retstat|status| length | <d7a_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;
+    
+    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);
+                d7a_fs_com_resp_t buf = (d7a_fs_com_resp_t) {.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.read_file(req->fid, req->offset+sizeof(d7a_fs_header_t), req->length, &b->data[0]);
+                    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.write_file(req->fid, req->offset+sizeof(d7a_fs_header_t), 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->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);
+                }
+                break;
+            // ---------------------------------------------------------------------------
+            // Response to our commands
+            // ---------------------------------------------------------------------------
+            case KAL_COM_FLOW_FS_RESP:
+                d7a_fs_com_resp_t* resp = (d7a_fs_com_resp_t*)pkt->buffer;
+                DPRINT("Rfs resp - status:%d bytes:%d\n",resp->status,resp->length);
+                ASSERT(g_fs_ctx.com.req.cmd !=FS_OP_NULL,"FS: Unexpected FS_RESP\n");
+                if (g_fs_ctx.com.req.cmd != FS_OP_STAT &&
+                    g_fs_ctx.com.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.data, &resp->data[0], resp->length);
+                    }
+                    else if (resp->cmd == FS_OP_RETSTAT)
+                    {
+                        if (g_fs_ctx.com.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.req.fid, &hdr);
+                            if (resp->length != hdr.bf.length)
+                            {
+                                //d7a_fs_set_properties(g_fs_ctx.com.req.fid, KAL_FS_PROP_SIZE, resp->length);
+                            }
+                        }
+                    }
+                }
+                else // FS_OP_STAT/DSTAT
+                {
+                    if (g_fs_ctx.com.req.cmd == FS_OP_STAT)
+                    {
+                        d7a_fs_header_t hdr;
+                        d7a_fs_hdr(g_fs_ctx.com.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.data!=NULL) *(uint32_t*)g_fs_ctx.com.data = resp->length;
+                    status = resp->status;
+                }
+                // Free to serve a new request
+                g_fs_ctx.com.req.cmd = FS_OP_NULL;
+                // Inform requester
+                g_fs_ctx.fs_done->release();
+                break;
+            default:
+                EPRINT("FS Unknown Flow ID 0x%02X\r\n", pkt->id);
+                break;
+        }
+        FREE(pkt);
+    }
+}