Merge pull request #756 from geekbozu/BLE_FS
BLE FS Using adafruits Simple (not fast) BLE FS Api
This commit is contained in:
		
						commit
						bccd77d5c9
					
				@ -487,6 +487,7 @@ list(APPEND SOURCE_FILES
 | 
			
		||||
        components/ble/NavigationService.cpp
 | 
			
		||||
        displayapp/fonts/lv_font_navi_80.c
 | 
			
		||||
        components/ble/BatteryInformationService.cpp
 | 
			
		||||
        components/ble/FSService.cpp
 | 
			
		||||
        components/ble/ImmediateAlertService.cpp
 | 
			
		||||
        components/ble/ServiceDiscovery.cpp
 | 
			
		||||
        components/ble/HeartRateService.cpp
 | 
			
		||||
@ -557,6 +558,7 @@ list(APPEND RECOVERY_SOURCE_FILES
 | 
			
		||||
        components/ble/MusicService.cpp
 | 
			
		||||
        components/ble/weather/WeatherService.cpp
 | 
			
		||||
        components/ble/BatteryInformationService.cpp
 | 
			
		||||
        components/ble/FSService.cpp
 | 
			
		||||
        components/ble/ImmediateAlertService.cpp
 | 
			
		||||
        components/ble/ServiceDiscovery.cpp
 | 
			
		||||
        components/ble/NavigationService.cpp
 | 
			
		||||
@ -669,6 +671,7 @@ set(INCLUDE_FILES
 | 
			
		||||
        components/ble/DfuService.h
 | 
			
		||||
        components/firmwarevalidator/FirmwareValidator.h
 | 
			
		||||
        components/ble/BatteryInformationService.h
 | 
			
		||||
        components/ble/FSService.h
 | 
			
		||||
        components/ble/ImmediateAlertService.h
 | 
			
		||||
        components/ble/ServiceDiscovery.h
 | 
			
		||||
        components/ble/BleClient.h
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										330
									
								
								src/components/ble/FSService.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								src/components/ble/FSService.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,330 @@
 | 
			
		||||
#include <nrf_log.h>
 | 
			
		||||
#include "FSService.h"
 | 
			
		||||
#include "components/ble/BleController.h"
 | 
			
		||||
#include "systemtask/SystemTask.h"
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
constexpr ble_uuid16_t FSService::fsServiceUuid;
 | 
			
		||||
constexpr ble_uuid128_t FSService::fsVersionUuid;
 | 
			
		||||
constexpr ble_uuid128_t FSService::fsTransferUuid;
 | 
			
		||||
 | 
			
		||||
int FSServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
 | 
			
		||||
  auto* fsService = static_cast<FSService*>(arg);
 | 
			
		||||
  return fsService->OnFSServiceRequested(conn_handle, attr_handle, ctxt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FSService::FSService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::FS& fs)
 | 
			
		||||
  : systemTask {systemTask},
 | 
			
		||||
    fs {fs},
 | 
			
		||||
    characteristicDefinition {{.uuid = &fsVersionUuid.u,
 | 
			
		||||
                               .access_cb = FSServiceCallback,
 | 
			
		||||
                               .arg = this,
 | 
			
		||||
                               .flags = BLE_GATT_CHR_F_READ,
 | 
			
		||||
                               .val_handle = &versionCharacteristicHandle},
 | 
			
		||||
                              {
 | 
			
		||||
                                .uuid = &fsTransferUuid.u,
 | 
			
		||||
                                .access_cb = FSServiceCallback,
 | 
			
		||||
                                .arg = this,
 | 
			
		||||
                                .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
 | 
			
		||||
                                .val_handle = &transferCharacteristicHandle,
 | 
			
		||||
                              },
 | 
			
		||||
                              {0}},
 | 
			
		||||
    serviceDefinition {
 | 
			
		||||
      {/* Device Information Service */
 | 
			
		||||
       .type = BLE_GATT_SVC_TYPE_PRIMARY,
 | 
			
		||||
       .uuid = &fsServiceUuid.u,
 | 
			
		||||
       .characteristics = characteristicDefinition},
 | 
			
		||||
      {0},
 | 
			
		||||
    } {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FSService::Init() {
 | 
			
		||||
  int res = 0;
 | 
			
		||||
  res = ble_gatts_count_cfg(serviceDefinition);
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
 | 
			
		||||
  res = ble_gatts_add_svcs(serviceDefinition);
 | 
			
		||||
  ASSERT(res == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int FSService::OnFSServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) {
 | 
			
		||||
  if (attributeHandle == versionCharacteristicHandle) {
 | 
			
		||||
    NRF_LOG_INFO("FS_S : handle = %d", versionCharacteristicHandle);
 | 
			
		||||
    int res = os_mbuf_append(context->om, &fsVersion, sizeof(fsVersion));
 | 
			
		||||
    return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
 | 
			
		||||
  }
 | 
			
		||||
  if (attributeHandle == transferCharacteristicHandle) {
 | 
			
		||||
    return FSCommandHandler(connectionHandle, context->om);
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int FSService::FSCommandHandler(uint16_t connectionHandle, os_mbuf* om) {
 | 
			
		||||
  auto command = static_cast<commands>(om->om_data[0]);
 | 
			
		||||
  NRF_LOG_INFO("[FS_S] -> FSCommandHandler Command %d", command);
 | 
			
		||||
  // Just always make sure we are awake...
 | 
			
		||||
  systemTask.PushMessage(Pinetime::System::Messages::StartFileTransfer);
 | 
			
		||||
  vTaskDelay(10);
 | 
			
		||||
  while (systemTask.IsSleeping()) {
 | 
			
		||||
    vTaskDelay(100); // 50ms
 | 
			
		||||
  }
 | 
			
		||||
  lfs_dir_t dir = {0};
 | 
			
		||||
  lfs_info info = {0};
 | 
			
		||||
  lfs_file f = {0};
 | 
			
		||||
  switch (command) {
 | 
			
		||||
    case commands::READ: {
 | 
			
		||||
      NRF_LOG_INFO("[FS_S] -> Read");
 | 
			
		||||
      auto* header = (ReadHeader*) om->om_data;
 | 
			
		||||
      uint16_t plen = header->pathlen;
 | 
			
		||||
      if (plen > maxpathlen) { //> counts for null term
 | 
			
		||||
        return -1;
 | 
			
		||||
      }
 | 
			
		||||
      memcpy(filepath, header->pathstr, plen);
 | 
			
		||||
      filepath[plen] = 0; // Copy and null teminate string
 | 
			
		||||
      ReadResponse resp;
 | 
			
		||||
      os_mbuf* om;
 | 
			
		||||
      resp.command = commands::READ_DATA;
 | 
			
		||||
      resp.status = 0x01;
 | 
			
		||||
      resp.chunkoff = header->chunkoff;
 | 
			
		||||
      int res = fs.Stat(filepath, &info);
 | 
			
		||||
      if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) {
 | 
			
		||||
        resp.status = (int8_t) res;
 | 
			
		||||
        resp.chunklen = 0;
 | 
			
		||||
        resp.totallen = 0;
 | 
			
		||||
        om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
 | 
			
		||||
      } else {
 | 
			
		||||
        resp.chunklen = std::min(header->chunksize, info.size); // TODO add mtu somehow
 | 
			
		||||
        resp.totallen = info.size;
 | 
			
		||||
        fs.FileOpen(&f, filepath, LFS_O_RDONLY);
 | 
			
		||||
        fs.FileSeek(&f, header->chunkoff);
 | 
			
		||||
        uint8_t fileData[resp.chunklen] = {0};
 | 
			
		||||
        resp.chunklen = fs.FileRead(&f, fileData, resp.chunklen);
 | 
			
		||||
        om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
 | 
			
		||||
        os_mbuf_append(om, fileData, resp.chunklen);
 | 
			
		||||
        fs.FileClose(&f);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case commands::READ_PACING: {
 | 
			
		||||
      NRF_LOG_INFO("[FS_S] -> Readpacing");
 | 
			
		||||
      auto* header = (ReadHeader*) om->om_data;
 | 
			
		||||
      ReadResponse resp;
 | 
			
		||||
      resp.command = commands::READ_DATA;
 | 
			
		||||
      resp.status = 0x01;
 | 
			
		||||
      resp.chunkoff = header->chunkoff;
 | 
			
		||||
      int res = fs.Stat(filepath, &info);
 | 
			
		||||
      if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) {
 | 
			
		||||
        resp.status = (int8_t) res;
 | 
			
		||||
        resp.chunklen = 0;
 | 
			
		||||
        resp.totallen = 0;
 | 
			
		||||
      } else {
 | 
			
		||||
        resp.chunklen = std::min(header->chunksize, info.size); // TODO add mtu somehow
 | 
			
		||||
        resp.totallen = info.size;
 | 
			
		||||
        fs.FileOpen(&f, filepath, LFS_O_RDONLY);
 | 
			
		||||
        fs.FileSeek(&f, header->chunkoff);
 | 
			
		||||
      }
 | 
			
		||||
      os_mbuf* om;
 | 
			
		||||
      if (resp.chunklen > 0) {
 | 
			
		||||
        uint8_t fileData[resp.chunklen] = {0};
 | 
			
		||||
        resp.chunklen = fs.FileRead(&f, fileData, resp.chunklen);
 | 
			
		||||
        om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
 | 
			
		||||
        os_mbuf_append(om, fileData, resp.chunklen);
 | 
			
		||||
      } else {
 | 
			
		||||
        resp.chunklen = 0;
 | 
			
		||||
        om = ble_hs_mbuf_from_flat(&resp, sizeof(ReadResponse));
 | 
			
		||||
      }
 | 
			
		||||
      fs.FileClose(&f);
 | 
			
		||||
      ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case commands::WRITE: {
 | 
			
		||||
      NRF_LOG_INFO("[FS_S] -> Write");
 | 
			
		||||
      auto* header = (WriteHeader*) om->om_data;
 | 
			
		||||
      uint16_t plen = header->pathlen;
 | 
			
		||||
      if (plen > maxpathlen) { //> counts for null term
 | 
			
		||||
        return -1;             // TODO make this actually return a BLE notif
 | 
			
		||||
      }
 | 
			
		||||
      memcpy(filepath, header->pathstr, plen);
 | 
			
		||||
      filepath[plen] = 0; // Copy and null teminate string
 | 
			
		||||
      fileSize = header->totalSize;
 | 
			
		||||
      WriteResponse resp;
 | 
			
		||||
      resp.command = commands::WRITE_PACING;
 | 
			
		||||
      resp.offset = header->offset;
 | 
			
		||||
      resp.modTime = 0;
 | 
			
		||||
 | 
			
		||||
      int res = fs.FileOpen(&f, filepath, LFS_O_RDWR | LFS_O_CREAT);
 | 
			
		||||
      if (res == 0) {
 | 
			
		||||
        fs.FileClose(&f);
 | 
			
		||||
        resp.status = (res == 0) ? 0x01 : (int8_t) res;
 | 
			
		||||
      }
 | 
			
		||||
      resp.freespace = std::min(fs.getSize() - (fs.GetFSSize() * fs.getBlockSize()), fileSize - header->offset);
 | 
			
		||||
      auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(WriteResponse));
 | 
			
		||||
      ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case commands::WRITE_DATA: {
 | 
			
		||||
      NRF_LOG_INFO("[FS_S] -> WriteData");
 | 
			
		||||
      auto* header = (WritePacing*) om->om_data;
 | 
			
		||||
      WriteResponse resp;
 | 
			
		||||
      resp.command = commands::WRITE_PACING;
 | 
			
		||||
      resp.offset = header->offset;
 | 
			
		||||
      int res = 0;
 | 
			
		||||
 | 
			
		||||
      if (!(res = fs.FileOpen(&f, filepath, LFS_O_RDWR | LFS_O_CREAT))) {
 | 
			
		||||
        if ((res = fs.FileSeek(&f, header->offset)) >= 0) {
 | 
			
		||||
          res = fs.FileWrite(&f, header->data, header->dataSize);
 | 
			
		||||
        }
 | 
			
		||||
        fs.FileClose(&f);
 | 
			
		||||
      }
 | 
			
		||||
      if (res < 0) {
 | 
			
		||||
        resp.status = (int8_t) res;
 | 
			
		||||
      }
 | 
			
		||||
      resp.freespace = std::min(fs.getSize() - (fs.GetFSSize() * fs.getBlockSize()), fileSize - header->offset);
 | 
			
		||||
      auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(WriteResponse));
 | 
			
		||||
      ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case commands::DELETE: {
 | 
			
		||||
      NRF_LOG_INFO("[FS_S] -> Delete");
 | 
			
		||||
      auto* header = (DelHeader*) om->om_data;
 | 
			
		||||
      uint16_t plen = header->pathlen;
 | 
			
		||||
      char path[plen + 1] = {0};
 | 
			
		||||
      memcpy(path, header->pathstr, plen);
 | 
			
		||||
      path[plen] = 0; // Copy and null teminate string
 | 
			
		||||
      DelResponse resp {};
 | 
			
		||||
      resp.command = commands::DELETE_STATUS;
 | 
			
		||||
      int res = fs.FileDelete(path);
 | 
			
		||||
      resp.status = (res == 0) ? 0x01 : (int8_t) res;
 | 
			
		||||
      auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(DelResponse));
 | 
			
		||||
      ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case commands::MKDIR: {
 | 
			
		||||
      NRF_LOG_INFO("[FS_S] -> MKDir");
 | 
			
		||||
      auto* header = (MKDirHeader*) om->om_data;
 | 
			
		||||
      uint16_t plen = header->pathlen;
 | 
			
		||||
      char path[plen + 1] = {0};
 | 
			
		||||
      memcpy(path, header->pathstr, plen);
 | 
			
		||||
      path[plen] = 0; // Copy and null teminate string
 | 
			
		||||
      MKDirResponse resp {};
 | 
			
		||||
      resp.command = commands::MKDIR_STATUS;
 | 
			
		||||
      resp.modification_time = 0;
 | 
			
		||||
      int res = fs.DirCreate(path);
 | 
			
		||||
      resp.status = (res == 0) ? 0x01 : (int8_t) res;
 | 
			
		||||
      auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(MKDirResponse));
 | 
			
		||||
      ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case commands::LISTDIR: {
 | 
			
		||||
      NRF_LOG_INFO("[FS_S] -> ListDir");
 | 
			
		||||
      ListDirHeader* header = (ListDirHeader*) om->om_data;
 | 
			
		||||
      uint16_t plen = header->pathlen;
 | 
			
		||||
      char path[plen + 1] = {0};
 | 
			
		||||
      path[plen] = 0; // Copy and null teminate string
 | 
			
		||||
      memcpy(path, header->pathstr, plen);
 | 
			
		||||
 | 
			
		||||
      ListDirResponse resp {};
 | 
			
		||||
 | 
			
		||||
      resp.command = commands::LISTDIR_ENTRY;
 | 
			
		||||
      resp.status = 0x01;
 | 
			
		||||
      resp.totalentries = 0;
 | 
			
		||||
      resp.entry = 0;
 | 
			
		||||
      resp.modification_time = 0;
 | 
			
		||||
      int res = fs.DirOpen(path, &dir);
 | 
			
		||||
      if (res != 0) {
 | 
			
		||||
        resp.status = (int8_t) res;
 | 
			
		||||
        auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
 | 
			
		||||
        ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
 | 
			
		||||
        break;
 | 
			
		||||
      };
 | 
			
		||||
      while (fs.DirRead(&dir, &info)) {
 | 
			
		||||
        resp.totalentries++;
 | 
			
		||||
      }
 | 
			
		||||
      fs.DirRewind(&dir);
 | 
			
		||||
      while (true) {
 | 
			
		||||
        res = fs.DirRead(&dir, &info);
 | 
			
		||||
        if (res <= 0) {
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        switch (info.type) {
 | 
			
		||||
          case LFS_TYPE_REG: {
 | 
			
		||||
            resp.flags = 0;
 | 
			
		||||
            resp.file_size = info.size;
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
          case LFS_TYPE_DIR: {
 | 
			
		||||
            resp.flags = 1;
 | 
			
		||||
            resp.file_size = 0;
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // strcpy(resp.path, info.name);
 | 
			
		||||
        resp.path_length = strlen(info.name);
 | 
			
		||||
        auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
 | 
			
		||||
        os_mbuf_append(om, info.name, resp.path_length);
 | 
			
		||||
        ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
 | 
			
		||||
        /*
 | 
			
		||||
         * Todo Figure out how to know when the previous Notify was TX'd
 | 
			
		||||
         * For now just delay 100ms to make sure that the data went out...
 | 
			
		||||
         */
 | 
			
		||||
        vTaskDelay(100); // Allow stuff to actually go out over the BLE conn
 | 
			
		||||
        resp.entry++;
 | 
			
		||||
      }
 | 
			
		||||
      assert(fs.DirClose(&dir) == 0);
 | 
			
		||||
      resp.file_size = 0;
 | 
			
		||||
      resp.path_length = 0;
 | 
			
		||||
      resp.flags = 0;
 | 
			
		||||
      auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(ListDirResponse));
 | 
			
		||||
      ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case commands::MOVE: {
 | 
			
		||||
      NRF_LOG_INFO("[FS_S] -> Move");
 | 
			
		||||
      MoveHeader* header = (MoveHeader*) om->om_data;
 | 
			
		||||
      uint16_t plen = header->OldPathLength;
 | 
			
		||||
      // Null Terminate string
 | 
			
		||||
      header->pathstr[plen] = 0;
 | 
			
		||||
      char path[header->NewPathLength + 1] = {0};
 | 
			
		||||
      memcpy(path, &header->pathstr[plen + 1], header->NewPathLength);
 | 
			
		||||
      path[header->NewPathLength] = 0; // Copy and null teminate string
 | 
			
		||||
      MoveResponse resp {};
 | 
			
		||||
      resp.command = commands::MOVE_STATUS;
 | 
			
		||||
      int8_t res = (int8_t) fs.Rename(header->pathstr, path);
 | 
			
		||||
      resp.status = (res == 0) ? 1 : res;
 | 
			
		||||
      auto* om = ble_hs_mbuf_from_flat(&resp, sizeof(MoveResponse));
 | 
			
		||||
      ble_gattc_notify_custom(connectionHandle, transferCharacteristicHandle, om);
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  NRF_LOG_INFO("[FS_S] -> done ");
 | 
			
		||||
  systemTask.PushMessage(Pinetime::System::Messages::StopFileTransfer);
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Loads resp with file data given a valid filepath header and resp
 | 
			
		||||
void FSService::prepareReadDataResp(ReadHeader* header, ReadResponse* resp) {
 | 
			
		||||
  // uint16_t plen = header->pathlen;
 | 
			
		||||
  resp->command = commands::READ_DATA;
 | 
			
		||||
  resp->chunkoff = header->chunkoff;
 | 
			
		||||
  resp->status = 0x01;
 | 
			
		||||
  struct lfs_info info = {};
 | 
			
		||||
  int res = fs.Stat(filepath, &info);
 | 
			
		||||
  if (res == LFS_ERR_NOENT && info.type != LFS_TYPE_DIR) {
 | 
			
		||||
    resp->status = 0x03;
 | 
			
		||||
    resp->chunklen = 0;
 | 
			
		||||
    resp->totallen = 0;
 | 
			
		||||
  } else {
 | 
			
		||||
    lfs_file f;
 | 
			
		||||
    resp->chunklen = std::min(header->chunksize, info.size);
 | 
			
		||||
    resp->totallen = info.size;
 | 
			
		||||
    fs.FileOpen(&f, filepath, LFS_O_RDONLY);
 | 
			
		||||
    fs.FileSeek(&f, header->chunkoff);
 | 
			
		||||
    resp->chunklen = fs.FileRead(&f, resp->chunk, resp->chunklen);
 | 
			
		||||
    fs.FileClose(&f);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										191
									
								
								src/components/ble/FSService.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								src/components/ble/FSService.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,191 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
 | 
			
		||||
#define max
 | 
			
		||||
#include <host/ble_gap.h>
 | 
			
		||||
#undef max
 | 
			
		||||
#undef min
 | 
			
		||||
 | 
			
		||||
#include "components/fs/FS.h"
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace System {
 | 
			
		||||
    class SystemTask;
 | 
			
		||||
  }
 | 
			
		||||
  namespace Controllers {
 | 
			
		||||
    class Ble;
 | 
			
		||||
    class FSService {
 | 
			
		||||
    public:
 | 
			
		||||
      FSService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::FS& fs);
 | 
			
		||||
      void Init();
 | 
			
		||||
 | 
			
		||||
      int OnFSServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
 | 
			
		||||
      void NotifyFSRaw(uint16_t connectionHandle);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
      Pinetime::System::SystemTask& systemTask;
 | 
			
		||||
      Pinetime::Controllers::FS& fs;
 | 
			
		||||
      static constexpr uint16_t FSServiceId {0xFEBB};
 | 
			
		||||
      static constexpr uint16_t fsVersionId {0x0100};
 | 
			
		||||
      static constexpr uint16_t fsTransferId {0x0200};
 | 
			
		||||
      uint16_t fsVersion = {0x0004};
 | 
			
		||||
      static constexpr uint16_t maxpathlen = 256;
 | 
			
		||||
      static constexpr ble_uuid16_t fsServiceUuid {
 | 
			
		||||
        .u {.type = BLE_UUID_TYPE_16},
 | 
			
		||||
        .value = {0xFEBB}}; // {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0xBB, 0xFE, 0xAF, 0xAD}};
 | 
			
		||||
 | 
			
		||||
      static constexpr ble_uuid128_t fsVersionUuid {
 | 
			
		||||
        .u {.type = BLE_UUID_TYPE_128},
 | 
			
		||||
        .value = {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0x00, 0x01, 0xAF, 0xAD}};
 | 
			
		||||
 | 
			
		||||
      static constexpr ble_uuid128_t fsTransferUuid {
 | 
			
		||||
        .u {.type = BLE_UUID_TYPE_128},
 | 
			
		||||
        .value = {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0x00, 0x02, 0xAF, 0xAD}};
 | 
			
		||||
 | 
			
		||||
      struct ble_gatt_chr_def characteristicDefinition[3];
 | 
			
		||||
      struct ble_gatt_svc_def serviceDefinition[2];
 | 
			
		||||
      uint16_t versionCharacteristicHandle;
 | 
			
		||||
      uint16_t transferCharacteristicHandle;
 | 
			
		||||
 | 
			
		||||
      enum class commands : uint8_t {
 | 
			
		||||
        INVALID = 0x00,
 | 
			
		||||
        READ = 0x10,
 | 
			
		||||
        READ_DATA = 0x11,
 | 
			
		||||
        READ_PACING = 0x12,
 | 
			
		||||
        WRITE = 0x20,
 | 
			
		||||
        WRITE_PACING = 0x21,
 | 
			
		||||
        WRITE_DATA = 0x22,
 | 
			
		||||
        DELETE = 0x30,
 | 
			
		||||
        DELETE_STATUS = 0x31,
 | 
			
		||||
        MKDIR = 0x40,
 | 
			
		||||
        MKDIR_STATUS = 0x41,
 | 
			
		||||
        LISTDIR = 0x50,
 | 
			
		||||
        LISTDIR_ENTRY = 0x51,
 | 
			
		||||
        MOVE = 0x60,
 | 
			
		||||
        MOVE_STATUS = 0x61
 | 
			
		||||
      };
 | 
			
		||||
      enum class FSState : uint8_t {
 | 
			
		||||
        IDLE = 0x00,
 | 
			
		||||
        READ = 0x01,
 | 
			
		||||
        WRITE = 0x02,
 | 
			
		||||
      };
 | 
			
		||||
      FSState state;
 | 
			
		||||
      char filepath[maxpathlen]; // TODO ..ugh fixed filepath len
 | 
			
		||||
      int fileSize;
 | 
			
		||||
      using ReadHeader = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t padding;
 | 
			
		||||
        uint16_t pathlen;
 | 
			
		||||
        uint32_t chunkoff;
 | 
			
		||||
        uint32_t chunksize;
 | 
			
		||||
        char pathstr[];
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      using ReadResponse = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t status;
 | 
			
		||||
        uint16_t padding;
 | 
			
		||||
        uint32_t chunkoff;
 | 
			
		||||
        uint32_t totallen;
 | 
			
		||||
        uint32_t chunklen;
 | 
			
		||||
        uint8_t chunk[];
 | 
			
		||||
      };
 | 
			
		||||
      using ReadPacing = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t status;
 | 
			
		||||
        uint16_t padding;
 | 
			
		||||
        uint32_t chunkoff;
 | 
			
		||||
        uint32_t chunksize;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      using WriteHeader = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t padding;
 | 
			
		||||
        uint16_t pathlen;
 | 
			
		||||
        uint32_t offset;
 | 
			
		||||
        uint64_t modTime;
 | 
			
		||||
        uint32_t totalSize;
 | 
			
		||||
        char pathstr[];
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      using WriteResponse = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t status;
 | 
			
		||||
        uint16_t padding;
 | 
			
		||||
        uint32_t offset;
 | 
			
		||||
        uint64_t modTime;
 | 
			
		||||
        uint32_t freespace;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      using WritePacing = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t status;
 | 
			
		||||
        uint16_t padding;
 | 
			
		||||
        uint32_t offset;
 | 
			
		||||
        uint32_t dataSize;
 | 
			
		||||
        uint8_t data[];
 | 
			
		||||
      };
 | 
			
		||||
      using ListDirHeader = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t padding;
 | 
			
		||||
        uint16_t pathlen;
 | 
			
		||||
        char pathstr[];
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      using ListDirResponse = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t status;
 | 
			
		||||
        uint16_t path_length;
 | 
			
		||||
        uint32_t entry;
 | 
			
		||||
        uint32_t totalentries;
 | 
			
		||||
        uint32_t flags;
 | 
			
		||||
        uint64_t modification_time;
 | 
			
		||||
        uint32_t file_size;
 | 
			
		||||
        char path[];
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      using MKDirHeader = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t padding;
 | 
			
		||||
        uint16_t pathlen;
 | 
			
		||||
        uint32_t padding2;
 | 
			
		||||
        uint64_t time;
 | 
			
		||||
        char pathstr[];
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      using MKDirResponse = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t status;
 | 
			
		||||
        uint32_t padding1;
 | 
			
		||||
        uint16_t padding2;
 | 
			
		||||
        uint64_t modification_time;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      using DelHeader = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t padding;
 | 
			
		||||
        uint16_t pathlen;
 | 
			
		||||
        char pathstr[];
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      using DelResponse = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t status;
 | 
			
		||||
      };
 | 
			
		||||
      using MoveHeader = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t padding;
 | 
			
		||||
        uint16_t OldPathLength;
 | 
			
		||||
        uint16_t NewPathLength;
 | 
			
		||||
        char pathstr[];
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      using MoveResponse = struct __attribute__((packed)) {
 | 
			
		||||
        commands command;
 | 
			
		||||
        uint8_t status;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      int FSCommandHandler(uint16_t connectionHandle, os_mbuf* om);
 | 
			
		||||
      void prepareReadDataResp(ReadHeader* header, ReadResponse* resp);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -30,7 +30,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
 | 
			
		||||
                                   Pinetime::Drivers::SpiNorFlash& spiNorFlash,
 | 
			
		||||
                                   Controllers::HeartRateController& heartRateController,
 | 
			
		||||
                                   Controllers::MotionController& motionController,
 | 
			
		||||
                                   Pinetime::Controllers::FS& fs)
 | 
			
		||||
                                   Controllers::FS& fs)
 | 
			
		||||
  : systemTask {systemTask},
 | 
			
		||||
    bleController {bleController},
 | 
			
		||||
    dateTimeController {dateTimeController},
 | 
			
		||||
@ -50,6 +50,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
 | 
			
		||||
    immediateAlertService {systemTask, notificationManager},
 | 
			
		||||
    heartRateService {systemTask, heartRateController},
 | 
			
		||||
    motionService {systemTask, motionController},
 | 
			
		||||
    fsService {systemTask, fs},
 | 
			
		||||
    serviceDiscovery({¤tTimeClient, &alertNotificationClient}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -97,6 +98,7 @@ void NimbleController::Init() {
 | 
			
		||||
  immediateAlertService.Init();
 | 
			
		||||
  heartRateService.Init();
 | 
			
		||||
  motionService.Init();
 | 
			
		||||
  fsService.Init();
 | 
			
		||||
 | 
			
		||||
  int rc;
 | 
			
		||||
  rc = ble_hs_util_ensure_addr(0);
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,7 @@
 | 
			
		||||
#include "components/ble/MotionService.h"
 | 
			
		||||
#include "components/ble/weather/WeatherService.h"
 | 
			
		||||
#include "components/fs/FS.h"
 | 
			
		||||
#include "components/ble/FSService.h"
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Drivers {
 | 
			
		||||
@ -110,6 +111,7 @@ namespace Pinetime {
 | 
			
		||||
      HeartRateService heartRateService;
 | 
			
		||||
      MotionService motionService;
 | 
			
		||||
      ServiceDiscovery serviceDiscovery;
 | 
			
		||||
      FSService fsService;
 | 
			
		||||
 | 
			
		||||
      uint8_t addrType;
 | 
			
		||||
      uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
 | 
			
		||||
 | 
			
		||||
@ -5,29 +5,28 @@
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Controllers;
 | 
			
		||||
 | 
			
		||||
FS::FS(Pinetime::Drivers::SpiNorFlash& driver) :
 | 
			
		||||
  flashDriver{ driver },
 | 
			
		||||
  lfsConfig{
 | 
			
		||||
    .context = this,
 | 
			
		||||
    .read = SectorRead,
 | 
			
		||||
    .prog = SectorProg,
 | 
			
		||||
    .erase = SectorErase,
 | 
			
		||||
    .sync = SectorSync,
 | 
			
		||||
FS::FS(Pinetime::Drivers::SpiNorFlash& driver)
 | 
			
		||||
  : flashDriver {driver},
 | 
			
		||||
    lfsConfig {
 | 
			
		||||
      .context = this,
 | 
			
		||||
      .read = SectorRead,
 | 
			
		||||
      .prog = SectorProg,
 | 
			
		||||
      .erase = SectorErase,
 | 
			
		||||
      .sync = SectorSync,
 | 
			
		||||
 | 
			
		||||
    .read_size = 16,
 | 
			
		||||
    .prog_size = 8,
 | 
			
		||||
    .block_size = blockSize,
 | 
			
		||||
    .block_count = size / blockSize,
 | 
			
		||||
    .block_cycles = 1000u,
 | 
			
		||||
      .read_size = 16,
 | 
			
		||||
      .prog_size = 8,
 | 
			
		||||
      .block_size = blockSize,
 | 
			
		||||
      .block_count = size / blockSize,
 | 
			
		||||
      .block_cycles = 1000u,
 | 
			
		||||
 | 
			
		||||
    .cache_size = 16,
 | 
			
		||||
    .lookahead_size = 16,
 | 
			
		||||
 | 
			
		||||
    .name_max = 50,
 | 
			
		||||
    .attr_max = 50,
 | 
			
		||||
  }
 | 
			
		||||
{ }
 | 
			
		||||
      .cache_size = 16,
 | 
			
		||||
      .lookahead_size = 16,
 | 
			
		||||
 | 
			
		||||
      .name_max = 50,
 | 
			
		||||
      .attr_max = 50,
 | 
			
		||||
    } {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FS::Init() {
 | 
			
		||||
 | 
			
		||||
@ -48,7 +47,6 @@ void FS::Init() {
 | 
			
		||||
  VerifyResource();
 | 
			
		||||
  LVGLFileSystemInit();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FS::VerifyResource() {
 | 
			
		||||
@ -56,7 +54,7 @@ void FS::VerifyResource() {
 | 
			
		||||
  resourcesValid = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int  FS::FileOpen(lfs_file_t* file_p, const char* fileName, const int flags) {
 | 
			
		||||
int FS::FileOpen(lfs_file_t* file_p, const char* fileName, const int flags) {
 | 
			
		||||
  return lfs_file_open(&lfs, file_p, fileName, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -80,27 +78,31 @@ int FS::FileDelete(const char* fileName) {
 | 
			
		||||
  return lfs_remove(&lfs, fileName);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int FS::DirOpen(const char* path, lfs_dir_t* lfs_dir) {
 | 
			
		||||
  return lfs_dir_open(&lfs, lfs_dir, path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int FS::DirClose(lfs_dir_t* lfs_dir) {
 | 
			
		||||
  return lfs_dir_close(&lfs, lfs_dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int FS::DirRead(lfs_dir_t* dir, lfs_info* info) {
 | 
			
		||||
  return lfs_dir_read(&lfs, dir, info);
 | 
			
		||||
}
 | 
			
		||||
int FS::DirRewind(lfs_dir_t* dir) {
 | 
			
		||||
  return lfs_dir_rewind(&lfs, dir);
 | 
			
		||||
}
 | 
			
		||||
int FS::DirCreate(const char* path) {
 | 
			
		||||
  return lfs_mkdir(&lfs, path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete directory and all files inside
 | 
			
		||||
int FS::DirDelete(const char* path) {
 | 
			
		||||
 | 
			
		||||
  lfs_dir_t lfs_dir;
 | 
			
		||||
  lfs_info entryInfo;
 | 
			
		||||
 | 
			
		||||
  int err;
 | 
			
		||||
  err = lfs_dir_open(&lfs, &lfs_dir, path);
 | 
			
		||||
  if (err) {
 | 
			
		||||
    return err;
 | 
			
		||||
  }
 | 
			
		||||
  while (lfs_dir_read(&lfs, &lfs_dir, &entryInfo)) {
 | 
			
		||||
    lfs_remove(&lfs, entryInfo.name);
 | 
			
		||||
  }
 | 
			
		||||
  lfs_dir_close(&lfs, &lfs_dir);
 | 
			
		||||
  return LFS_ERR_OK;
 | 
			
		||||
int FS::Rename(const char* oldPath, const char* newPath){
 | 
			
		||||
  return lfs_rename(&lfs,oldPath,newPath);
 | 
			
		||||
}
 | 
			
		||||
int FS::Stat(const char* path, lfs_info* info) {
 | 
			
		||||
  return lfs_stat(&lfs, path, info);
 | 
			
		||||
}
 | 
			
		||||
lfs_ssize_t FS::GetFSSize() {
 | 
			
		||||
  return lfs_fs_size(&lfs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@ -141,17 +143,17 @@ int FS::SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off,
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
  lv_fs_res_t lvglOpen(lv_fs_drv_t* drv, void* file_p, const char* path, lv_fs_mode_t mode) {
 | 
			
		||||
 | 
			
		||||
    lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
 | 
			
		||||
    FS* filesys = static_cast<FS*>(drv->user_data);
 | 
			
		||||
    filesys->FileOpen(file, path, LFS_O_RDONLY);
 | 
			
		||||
 | 
			
		||||
    if (file->type == 0) {
 | 
			
		||||
      return LV_FS_RES_FS_ERR;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      return LV_FS_RES_OK;
 | 
			
		||||
    int res = filesys->FileOpen(file, path, LFS_O_RDONLY);
 | 
			
		||||
    if (res == 0) {
 | 
			
		||||
      if (file->type == 0) {
 | 
			
		||||
        return LV_FS_RES_FS_ERR;
 | 
			
		||||
      } else {
 | 
			
		||||
        return LV_FS_RES_OK;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return LV_FS_RES_NOT_EX;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lv_fs_res_t lvglClose(lv_fs_drv_t* drv, void* file_p) {
 | 
			
		||||
@ -193,5 +195,4 @@ void FS::LVGLFileSystemInit() {
 | 
			
		||||
  fs_drv.user_data = this;
 | 
			
		||||
 | 
			
		||||
  lv_fs_drv_register(&fs_drv);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -21,37 +21,49 @@ namespace Pinetime {
 | 
			
		||||
 | 
			
		||||
      int FileDelete(const char* fileName);
 | 
			
		||||
 | 
			
		||||
      int DirOpen(const char* path, lfs_dir_t* lfs_dir);
 | 
			
		||||
      int DirClose(lfs_dir_t* lfs_dir);
 | 
			
		||||
      int DirRead(lfs_dir_t* dir, lfs_info* info);
 | 
			
		||||
      int DirRewind(lfs_dir_t* dir);
 | 
			
		||||
      int DirCreate(const char* path);
 | 
			
		||||
      int DirDelete(const char* path);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
      lfs_ssize_t GetFSSize();
 | 
			
		||||
      int Rename(const char* oldPath, const char* newPath);
 | 
			
		||||
      int Stat(const char* path, lfs_info* info);
 | 
			
		||||
      void VerifyResource();
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
      static size_t getSize() {
 | 
			
		||||
        return size;
 | 
			
		||||
      }
 | 
			
		||||
      static size_t getBlockSize() {
 | 
			
		||||
        return blockSize;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
      Pinetime::Drivers::SpiNorFlash& flashDriver;
 | 
			
		||||
 | 
			
		||||
      /*
 | 
			
		||||
      * External Flash MAP (4 MBytes)
 | 
			
		||||
      *
 | 
			
		||||
      * 0x000000 +---------------------------------------+
 | 
			
		||||
      *          |  Bootloader Assets                    |
 | 
			
		||||
      *          |  256 KBytes                           |
 | 
			
		||||
      *          |                                       |
 | 
			
		||||
      * 0x040000 +---------------------------------------+
 | 
			
		||||
      *          |  OTA                                  |
 | 
			
		||||
      *          |  464 KBytes                           |
 | 
			
		||||
      *          |                                       |
 | 
			
		||||
      *          |                                       |
 | 
			
		||||
      *          |                                       |
 | 
			
		||||
      * 0x0B4000 +---------------------------------------+
 | 
			
		||||
      *          |  File System                          |
 | 
			
		||||
      *          |                                       |
 | 
			
		||||
      *          |                                       |
 | 
			
		||||
      *          |                                       |
 | 
			
		||||
      *          |                                       |
 | 
			
		||||
      * 0x400000 +---------------------------------------+
 | 
			
		||||
      *
 | 
			
		||||
      */
 | 
			
		||||
       * External Flash MAP (4 MBytes)
 | 
			
		||||
       *
 | 
			
		||||
       * 0x000000 +---------------------------------------+
 | 
			
		||||
       *          |  Bootloader Assets                    |
 | 
			
		||||
       *          |  256 KBytes                           |
 | 
			
		||||
       *          |                                       |
 | 
			
		||||
       * 0x040000 +---------------------------------------+
 | 
			
		||||
       *          |  OTA                                  |
 | 
			
		||||
       *          |  464 KBytes                           |
 | 
			
		||||
       *          |                                       |
 | 
			
		||||
       *          |                                       |
 | 
			
		||||
       *          |                                       |
 | 
			
		||||
       * 0x0B4000 +---------------------------------------+
 | 
			
		||||
       *          |  File System                          |
 | 
			
		||||
       *          |                                       |
 | 
			
		||||
       *          |                                       |
 | 
			
		||||
       *          |                                       |
 | 
			
		||||
       *          |                                       |
 | 
			
		||||
       * 0x400000 +---------------------------------------+
 | 
			
		||||
       *
 | 
			
		||||
       */
 | 
			
		||||
      static constexpr size_t startAddress = 0x0B4000;
 | 
			
		||||
      static constexpr size_t size = 0x34C000;
 | 
			
		||||
      static constexpr size_t blockSize = 4096;
 | 
			
		||||
@ -65,7 +77,6 @@ namespace Pinetime {
 | 
			
		||||
      static int SectorErase(const struct lfs_config* c, lfs_block_t block);
 | 
			
		||||
      static int SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);
 | 
			
		||||
      static int SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,8 @@ namespace Pinetime {
 | 
			
		||||
        StopRinging,
 | 
			
		||||
        MeasureBatteryTimerExpired,
 | 
			
		||||
        BatteryPercentageUpdated,
 | 
			
		||||
        StartFileTransfer,
 | 
			
		||||
        StopFileTransfer,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -342,6 +342,19 @@ void SystemTask::Work() {
 | 
			
		||||
          doNotGoToSleep = false;
 | 
			
		||||
          xTimerStart(dimTimer, 0);
 | 
			
		||||
          break;
 | 
			
		||||
        case Messages::StartFileTransfer:
 | 
			
		||||
          NRF_LOG_INFO("[systemtask] FS Started"); 
 | 
			
		||||
          doNotGoToSleep = true;
 | 
			
		||||
          if (isSleeping && !isWakingUp)
 | 
			
		||||
            GoToRunning();
 | 
			
		||||
          //TODO add intent of fs access icon or something
 | 
			
		||||
          break;
 | 
			
		||||
        case Messages::StopFileTransfer:
 | 
			
		||||
          NRF_LOG_INFO("[systemtask] FS Stopped");
 | 
			
		||||
          doNotGoToSleep = false;
 | 
			
		||||
          xTimerStart(dimTimer, 0);
 | 
			
		||||
           //TODO add intent of fs access icon or something
 | 
			
		||||
           break;
 | 
			
		||||
        case Messages::OnTouchEvent:
 | 
			
		||||
          if (touchHandler.GetNewTouchInfo()) {
 | 
			
		||||
            touchHandler.UpdateLvglTouchPoint();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user