From d5234633ba671ee926890ba3e0f234280034e73d Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Jul 2016 23:06:21 -0700 Subject: [PATCH 001/100] FatFs: Update to R0.12a. --- .../ext/fatfs/doc/00index_e.html | 56 +- .../ext/fatfs/doc/00index_j.html | 40 +- .../ext/fatfs/doc/en/appnote.html | 189 +- .../ext/fatfs/doc/en/chmod.html | 2 +- .../ext/fatfs/doc/en/close.html | 2 +- .../ext/fatfs/doc/en/closedir.html | 2 +- .../ext/fatfs/doc/en/config.html | 92 +- .../ext/fatfs/doc/en/dinit.html | 2 +- .../ext/fatfs/doc/en/dioctl.html | 7 +- .../ext/fatfs/doc/en/dread.html | 10 +- .../ext/fatfs/doc/en/dstat.html | 6 +- .../ext/fatfs/doc/en/dwrite.html | 6 +- .../ext/fatfs/doc/en/eof.html | 2 +- .../ext/fatfs/doc/en/error.html | 4 +- .../ext/fatfs/doc/en/expand.html | 116 + .../ext/fatfs/doc/en/fdisk.html | 25 +- .../ext/fatfs/doc/en/filename.html | 31 +- .../ext/fatfs/doc/en/findfirst.html | 25 +- .../ext/fatfs/doc/en/forward.html | 4 +- .../ext/fatfs/doc/en/getcwd.html | 1 + .../ext/fatfs/doc/en/getlabel.html | 4 +- .../ext/fatfs/doc/en/lseek.html | 22 +- .../ext/fatfs/doc/en/mkfs.html | 54 +- .../ext/fatfs/doc/en/mount.html | 8 +- .../ext/fatfs/doc/en/open.html | 10 +- .../ext/fatfs/doc/en/rc.html | 29 +- .../ext/fatfs/doc/en/read.html | 4 +- .../ext/fatfs/doc/en/readdir.html | 62 +- .../ext/fatfs/doc/en/sdir.html | 13 +- .../ext/fatfs/doc/en/setlabel.html | 11 +- .../ext/fatfs/doc/en/sfatfs.html | 12 +- .../ext/fatfs/doc/en/sfile.html | 12 +- .../ext/fatfs/doc/en/sfileinfo.html | 31 +- .../ext/fatfs/doc/en/size.html | 6 +- .../ext/fatfs/doc/en/stat.html | 18 +- .../ext/fatfs/doc/en/tell.html | 4 +- .../ext/fatfs/doc/en/truncate.html | 1 + .../ext/fatfs/doc/en/unlink.html | 2 +- .../ext/fatfs/doc/en/utime.html | 2 +- .../ext/fatfs/doc/en/write.html | 4 +- .../ext/fatfs/doc/img/f7.png | Bin 11388 -> 0 bytes .../ext/fatfs/doc/img/funcs.png | Bin 19491 -> 0 bytes .../ext/fatfs/doc/img/modules.png | Bin 7932 -> 0 bytes .../ext/fatfs/doc/img/rwtest.png | Bin 19068 -> 0 bytes .../ext/fatfs/doc/ja/appnote.html | 180 +- .../ext/fatfs/doc/ja/config.html | 50 +- .../ext/fatfs/doc/ja/dioctl.html | 3 + .../ext/fatfs/doc/ja/dread.html | 6 +- .../ext/fatfs/doc/ja/dwrite.html | 8 +- .../ext/fatfs/doc/ja/expand.html | 116 + .../ext/fatfs/doc/ja/fdisk.html | 33 +- .../ext/fatfs/doc/ja/filename.html | 15 +- .../ext/fatfs/doc/ja/findfirst.html | 19 +- .../ext/fatfs/doc/ja/findnext.html | 2 +- .../ext/fatfs/doc/ja/forward.html | 4 +- .../ext/fatfs/doc/ja/getcwd.html | 1 + .../ext/fatfs/doc/ja/getfree.html | 2 +- .../ext/fatfs/doc/ja/getlabel.html | 4 +- .../ext/fatfs/doc/ja/lseek.html | 10 +- .../ext/fatfs/doc/ja/mkfs.html | 58 +- .../ext/fatfs/doc/ja/mount.html | 2 +- .../ext/fatfs/doc/ja/open.html | 7 +- .../ext/fatfs/doc/ja/rc.html | 11 +- .../ext/fatfs/doc/ja/readdir.html | 61 +- .../ext/fatfs/doc/ja/sdir.html | 11 +- .../ext/fatfs/doc/ja/setlabel.html | 7 +- .../ext/fatfs/doc/ja/sfatfs.html | 12 +- .../ext/fatfs/doc/ja/sfile.html | 9 +- .../ext/fatfs/doc/ja/sfileinfo.html | 27 +- .../ext/fatfs/doc/ja/size.html | 4 +- .../ext/fatfs/doc/ja/stat.html | 18 +- .../ext/fatfs/doc/ja/tell.html | 2 +- .../ext/fatfs/doc/{img => res}/app1.c | 1 + .../ext/fatfs/doc/{img => res}/app2.c | 0 .../ext/fatfs/doc/{img => res}/app3.c | 3 +- .../ext/fatfs/doc/{img => res}/app4.c | 0 .../ext/fatfs/doc/{img => res}/f1.png | Bin .../ext/fatfs/doc/{img => res}/f2.png | Bin .../ext/fatfs/doc/{img => res}/f3.png | Bin .../ext/fatfs/doc/{img => res}/f4.png | Bin .../ext/fatfs/doc/{img => res}/f5.png | Bin .../ext/fatfs/doc/{img => res}/f6.png | Bin .../ext/fatfs/doc/res/f7.png | Bin 0 -> 30461 bytes .../ext/fatfs/doc/res/funcs.png | Bin 0 -> 22722 bytes .../ext/fatfs/doc/{img => res}/layers.png | Bin .../ext/fatfs/doc/{img => res}/layers1.png | Bin .../ext/fatfs/doc/{img => res}/layers2.png | Bin .../ext/fatfs/doc/{img => res}/layers3.png | Bin .../ext/fatfs/doc/{img => res}/mkfatimg.zip | Bin .../ext/fatfs/doc/res/mkfs.xls | Bin 0 -> 3238912 bytes .../ext/fatfs/doc/res/modules.png | Bin 0 -> 17469 bytes .../ext/fatfs/doc/res/rwtest1.png | Bin 0 -> 27860 bytes .../ext/fatfs/doc/{img => res}/rwtest2.png | Bin .../ext/fatfs/doc/{img => res}/rwtest3.png | Bin .../ext/fatfs/src/00history.txt | 26 +- .../ext/fatfs/src/00readme.txt | 2 +- .../chibios-portapack/ext/fatfs/src/diskio.c | 61 +- .../chibios-portapack/ext/fatfs/src/diskio.h | 6 +- firmware/chibios-portapack/ext/fatfs/src/ff.c | 4773 +++++++++++------ firmware/chibios-portapack/ext/fatfs/src/ff.h | 228 +- .../ext/fatfs/src/ffconf_template.h | 143 +- .../chibios-portapack/ext/fatfs/src/integer.h | 15 +- .../ext/fatfs/src/option/cc932.c | 109 +- .../ext/fatfs/src/option/cc936.c | 109 +- .../ext/fatfs/src/option/cc949.c | 110 +- .../ext/fatfs/src/option/cc950.c | 110 +- .../ext/fatfs/src/option/ccsbcs.c | 108 +- .../ext/fatfs/src/option/syscall.c | 10 +- 108 files changed, 4627 insertions(+), 2800 deletions(-) create mode 100644 firmware/chibios-portapack/ext/fatfs/doc/en/expand.html delete mode 100644 firmware/chibios-portapack/ext/fatfs/doc/img/f7.png delete mode 100644 firmware/chibios-portapack/ext/fatfs/doc/img/funcs.png delete mode 100644 firmware/chibios-portapack/ext/fatfs/doc/img/modules.png delete mode 100644 firmware/chibios-portapack/ext/fatfs/doc/img/rwtest.png create mode 100644 firmware/chibios-portapack/ext/fatfs/doc/ja/expand.html rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/app1.c (90%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/app2.c (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/app3.c (94%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/app4.c (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/f1.png (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/f2.png (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/f3.png (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/f4.png (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/f5.png (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/f6.png (100%) create mode 100644 firmware/chibios-portapack/ext/fatfs/doc/res/f7.png create mode 100644 firmware/chibios-portapack/ext/fatfs/doc/res/funcs.png rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/layers.png (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/layers1.png (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/layers2.png (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/layers3.png (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/mkfatimg.zip (100%) create mode 100644 firmware/chibios-portapack/ext/fatfs/doc/res/mkfs.xls create mode 100644 firmware/chibios-portapack/ext/fatfs/doc/res/modules.png create mode 100644 firmware/chibios-portapack/ext/fatfs/doc/res/rwtest1.png rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/rwtest2.png (100%) rename firmware/chibios-portapack/ext/fatfs/doc/{img => res}/rwtest3.png (100%) diff --git a/firmware/chibios-portapack/ext/fatfs/doc/00index_e.html b/firmware/chibios-portapack/ext/fatfs/doc/00index_e.html index 2eef02ee..9427acbe 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/00index_e.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/00index_e.html @@ -18,22 +18,23 @@
-layer -

FatFs is a generic FAT file system module for small embedded systems. The FatFs module is written in compliance with ANSI C (C89) and completely separated from the disk I/O layer. Therefore it is independent of the platform. It can be incorporated into small microcontrollers with limited resource, such as 8051, PIC, AVR, ARM, Z80, 78K and etc. Also Petit FatFs module for tiny microcontrollers is available here.

+layer +

FatFs is a generic FAT/exFAT file system module for small embedded systems. The FatFs module is written in compliance with ANSI C (C89) and completely separated from the disk I/O layer. Therefore it is independent of the platform. It can be incorporated into small microcontrollers with limited resource, such as 8051, PIC, AVR, ARM, Z80, 78K and etc. Also Petit FatFs module for tiny microcontrollers is available here.

Features

@@ -42,7 +43,7 @@

Application Interface

-layer +layer

-

Return

+

Latest Information

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/00index_j.html b/firmware/chibios-portapack/ext/fatfs/doc/00index_j.html index fc8444e1..3b881f53 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/00index_j.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/00index_j.html @@ -18,20 +18,21 @@
-layer -

FatFsは小規模な組み込みシステム向けの汎用FATファイルシステム モジュールです。ANSI C(C89)準拠でハードウェア アーキテクチャには依存しないので、必要なワーク エリアが確保できれば、8051, PIC, AVR, SH, Z80, 68k, H8, ARMなど安価なマイコンでも使用可能です。このほか、FatFsを極小マイコン向けにシュリンクしたぷちFatFsもあります。

+layer +

FatFsは小規模な組み込みシステム向けの汎用FAT/exFATファイルシステム モジュールです。ANSI C(C89)準拠でハードウェア アーキテクチャには依存しないので、必要なワーク エリアが確保できれば、8051, PIC, AVR, SH, Z80, 68k, H8, ARMなど安価なマイコンでも使用可能です。このほか、FatFsを極小マイコン向けにシュリンクしたぷちFatFsもあります。

FatFsモジュールの特徴

    -
  • Windows互換 FATファイル システム
  • +
  • Windows互換 FAT/exFATファイルシステム
  • プラットフォーム非依存
  • コンパクトなコードとRAM使用量
  • 多くの構成オプション:
      -
    • 複数のボリューム(物理ドライブ・区画)
    • +
    • ボリューム構成(物理ドライブ数・区画)
    • DBCSを含む複数のANSI/OEMコード ページの選択
    • 長いファイル名(LFN)への対応
    • -
    • マルチタスク関連
    • -
    • マルチ セクタ サイズ
    • +
    • exFATファイルシステムへの対応
    • +
    • RTOS環境への対応
    • +
    • セクタ サイズ(固定/可変)
    • リード オンリー構成、一部APIの削除、バッファ構成、その他…
  • @@ -41,7 +42,7 @@

    上位レイヤ インターフェース

    -layer +layer
    • ファイル アクセス
        @@ -53,6 +54,7 @@
      • f_truncate - サイズの切り詰め
      • f_sync - キャッシュされたデータのフラッシュ
      • f_forward - データをストリーム関数に転送
      • +
      • f_expand - 連続領域の割り当て
      • f_gets - 文字列の読み出し
      • f_putc - 文字の書き込み
      • f_puts - 文字列の書き込み
      • @@ -101,8 +103,8 @@

        下位レイヤ インターフェース

        -layer -

        FatFsモジュールは、単なるファイル システム レイヤなので、ストレージ デバイス制御レイヤは含まれません。使用するプラットフォームやストレージ デバイスに対応した制御関数は、インプリメンタによって提供される必要があります。FatFsモジュールは、下位レイヤに対し標準的には次のインターフェースを要求します。一部の拡張機能、たとえばOS関連機能を有効にしたときは、加えてプロセス/メモリ操作関数なども必要になります。サンプル プロジェクトに下位レイヤの実装例を示します。

        +layer +

        FatFsモジュールは、単なるファイルシステム レイヤなので、その下位のストレージ デバイス制御レイヤはそれに含まれません。それぞれのプラットフォームやストレージ デバイスに対応した制御レイヤは、インプリメンタによって提供される必要があります。FatFsモジュールは、下位レイヤに対し標準的には次のインターフェースを要求します。一部の拡張機能、たとえばOS関連機能を有効にしたときは、加えてプロセス/メモリ操作関数なども必要になります。サンプル プロジェクトに下位レイヤの実装例を示します。


        -

        戻る

        +

        FatFs Home Page

        diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/appnote.html b/firmware/chibios-portapack/ext/fatfs/doc/en/appnote.html index aa989320..2344b3a6 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/appnote.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/appnote.html @@ -15,9 +15,10 @@
      • How to Port
      • Limits
      • Memory Usage
      • -
      • Module Size Reduction
      • +
      • Reducing Module Size
      • Long File Name
      • Unicode API
      • +
      • exFAT File System
      • Re-entrancy
      • Duplicated File Access
      • Performance Effective File Access
      • @@ -36,14 +37,14 @@
      • ANSI C
        The FatFs module is a middleware written in ANSI C (C89). There is no platform dependence, so long as the compiler is in compliance with ANSI C.
      • Size of integer types
        -The FatFs module assumes that size of char/short/long are 8/16/32 bit and int is 16 or 32 bit. These correspondence are defined in integer.h. This will not be a problem on most compilers. When any conflict with existing definitions is occured, you must resolve it with care.
      • +The FatFs module assumes that size of char/short/long are 8/16/32 bit and int is 16 or 32 bit. These correspondence are defined in integer.h. This will not be a problem on most compilers. When any conflict with existing definitions is occured, you must resolve it with care.

      System organizations

      -

      The dependency diagram shown below is a typical configuration of the embedded system with FatFs module.

      -

      dependency diagram

      +

      The dependency diagram shown below is a typical but not specific configuration of the embedded system with FatFs module.

      +

      dependency diagram

      (a) If a working disk module with FatFs API is provided, no additional function is needed. (b) To attach existing disk drivers with different API, glue functions are needed to translate the APIs between FatFs and the drivers.

      -

      functional diagram

      +

      functional diagram

      Which function is required?

      You need to provide only low level disk I/O functions that required by FatFs module and nothing else. If a working disk module for the target system is already existing, you need to write only glue functions to attach it to the FatFs module. If not, you need to port any other disk module or write it from scratch. Most of defined functions are not that always required. For example, disk write function is not required at read-only configuration. Following table shows which function is required depends on the configuration options.

      @@ -63,13 +64,13 @@ The FatFs module assumes that size of char/short/long are 8/16/32 bit and int is

      Limits

        -
      • FAT sub-types: FAT12, FAT16 and FAT32.
      • +
      • File system type: FAT12, FAT16, FAT32(r0.0) and exFAT(r1.0).
      • Number of open files: Unlimited. (depends on available memory)
      • Number of volumes: Upto 10.
      • -
      • File size: Upto 4G-1 bytes. (by FAT specs.)
      • -
      • Volume size: Upto 2T bytes at 512 bytes/sector. (by FAT specs.)
      • -
      • Cluster size: Upto 64K bytes at 512 bytes/sector. (by FAT specs.)
      • -
      • Sector size: 512, 1024, 2048 and 4096 bytes. (by FAT specs.)
      • +
      • Volume size: Upto 2 TiB at 512 bytes/sector.
      • +
      • File size: Upto 4 GiB - 1 on FAT volume and virtually unlimited on exFAT volume.
      • +
      • Cluster size: Upto 128 sectors on FAT volume and upto 16 MiB on exFAT volume.
      • +
      • Sector size: 512, 1024, 2048 and 4096 bytes.
      @@ -78,73 +79,74 @@ The FatFs module assumes that size of char/short/long are 8/16/32 bit and int is

      The memory usage varies depends on the configuration options.

      - - - - - - - - - + + + + + + + + +
      ARM7
      32bit
      ARM7
      Thumb
      CM3
      Thumb-2
      AVRH8/300HPIC24RL78V850ESSH-2ARX600IA-32
      CompilerGCCGCCGCCGCCCH38C30CC78K0RCA850SHCRXCVC6
      text (Full, R/W)10.6k7.1k6.5k13.3k10.9k11.7k13.3k8.1k9.0k6.0k7.9k
      text (Min, R/W) 6.7k4.6k4.2k 8.6k 7.3k 7.7k 9.1k5.3k5.8k3.9k5.2k
      text (Full, R/O) 4.8k3.2k2.9k 6.2k 5.2k 5.5k 6.5k3.8k4.0k2.9k3.7k
      text (Min, R/O) 3.6k2.5k2.3k 4.6k 4.1k 4.3k 5.0k3.0k3.1k2.2k2.9k
      bssV*4 + 2V*4 + 2V*4 + 2V*2 + 2V*4 + 2V*2 + 2V*2 + 2V*4 + 2V*4 + 2V*4 + 2V*4 + 2
      Work area
      (_FS_TINY == 0)
      V*560
      + F*550
      V*560
      + F*550
      V*560
      + F*550
      V*560
      + F*544
      V*560
      + F*550
      V*560
      + F*544
      V*560
      + F*544
      V*560
      + F*544
      V*560
      + F*550
      V*560
      + F*550
      V*560
      + F*550
      Work area
      (_FS_TINY == 1)
      V*560
      + F*36
      V*560
      + F*36
      V*560
      + F*36
      V*560
      + F*32
      V*560
      + F*36
      V*560
      + F*32
      V*560
      + F*32
      V*560
      + F*36
      V*560
      + F*36
      V*560
      + F*36
      V*560
      + F*36
      CompilerGCCGCCGCCGCCCH38C30CC78K0RCA850SHCRXCMSC
      text (Full, R/W)10.1k6.6k6.2k12.1k10.5k11.2k12.6k8.5k8.7k6.3k8.4k
      text (Min, R/W) 6.6k4.5k4.2k 7.9k 7.0k 7.6k 8.8k5.9k5.8k4.3k5.8k
      text (Full, R/O) 4.8k3.1k2.9k 5.8k 5.1k 5.5k 6.4k4.1k4.0k3.1k4.0k
      text (Min, R/O) 3.5k2.4k2.3k 4.4k 3.9k 4.2k 5.0k3.3k3.1k2.4k3.1k
      bssV*4 + 2V*4 + 2V*4 + 2V*2 + 2V*4 + 2V*2 + 2V*2 + 2V*4 + 2V*4 + 2V*4 + 2V*4 + 2
      Work area
      (_FS_TINY == 0)
      V*564
      + F*552
      V*564
      + F*552
      V*564
      + F*552
      V*560
      + F*546
      V*560
      + F*546
      V*560
      + F*546
      V*560
      + F*546
      V*564
      + F*552
      V*564
      + F*552
      V*564
      + F*552
      V*564
      + F*552
      Work area
      (_FS_TINY == 1)
      V*564
      + F*40
      V*564
      + F*40
      V*564
      + F*40
      V*560
      + F*34
      V*560
      + F*34
      V*560
      + F*34
      V*560
      + F*34
      V*564
      + F*40
      V*564
      + F*40
      V*564
      + F*40
      V*564
      + F*40
      -

      These are the memory usage on some target systems with following condition. The memory sizes are in unit of byte, V denotes number of volumes and F denotes number of open files. All samples are optimezed in code size.

      +

      These are the memory usage on some target systems with following condition. The memory sizes are in unit of byte, V denotes option _VOLUMES and F denotes number of open files. All samples here are optimezed in code size.

      -FatFs R0.11 options:
      +FatFs R0.12 options:
       _FS_READONLY   0 (R/W) or 1 (R/O)
      -_FS_MINIMIZE   0 (Full basic function) or 3 (Minimized function)
      -_VOLUMES       V (Number of logical drives to be used)
      -_WORD_ACCESS   0 or 1 (Set for 1 if possible)
      -(Any other options are left not changed from default setting)
      +_FS_MINIMIZE   0 (Full, with all basic functions) or 3 (Min, with fully minimized)
      +_FS_TINY       0 (Default) or 1 (Tiny file object)
      +And any other options are left unchanged from original setting.
       
    -

    Module Size Reduction

    +

    Reducing Modle Size

    Follwing table shows which API function is removed by configuration options for the module size reduction. To use any API function, the row of the function must be clear.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Function_FS_MINIMIZE_FS_READONLY_USE_STRFUNC_FS_RPATH_USE_FIND_USE_LABEL_USE_MKFS_USE_FORWARD_MULTI_PARTITION
    0123010  1/20120101010101
    f_mount
    f_open
    f_close
    f_read
    f_writex
    f_syncx
    f_lseekx
    f_opendirxx
    f_closedirxx
    f_readdirxx
    f_findfirstxxx
    f_findnextxxx
    f_statxxx
    f_getfreexxxx
    f_truncatexxxx
    f_unlinkxxxx
    f_mkdirxxxx
    f_chmodxxxx
    f_utimexxxx
    f_renamexxxx
    f_chdirx
    f_chdrivex
    f_getcwdxx
    f_getlabelx
    f_setlabelxx
    f_forwardx
    f_mkfsxx
    f_fdiskxxx
    f_putcxx
    f_putsxx
    f_printfxx
    f_getsx
    Function_FS_
    MINIMIZE
    _FS_
    READONLY
    _USE_
    STRFUNC
    _FS_
    RPATH
    _USE_
    FIND
    _USE_
    CHMOD
    _USE_
    EXPAND
    _USE_
    LABEL
    _USE_
    MKFS
    _USE_
    FORWARD
    _MULTI_
    PARTITION
    0123010101201010101010101
    f_mount
    f_open
    f_close
    f_read
    f_write x
    f_sync x
    f_lseek x
    f_opendir xx
    f_closedir xx
    f_readdir xx
    f_findfirst xx x
    f_findnext xx x
    f_stat xxx
    f_getfree xxx x
    f_truncate xxx x
    f_unlink xxx x
    f_mkdir xxx x
    f_rename xxx x
    f_chdir x
    f_chdrive x
    f_getcwd xx
    f_chmod x x
    f_utime x x
    f_getlabel x
    f_setlabel x x
    f_expand x x
    f_forward x
    f_mkfs x x
    f_fdisk x x x
    f_putc xx
    f_puts xx
    f_printf xx
    f_gets x

    Long File Name

    -

    FatFs module supports LFN (long file name). The two different file names, SFN (short file name) and LFN, of a file is transparent on the API except for f_readdir function. The LFN feature is disabled by default. To enable it, set _USE_LFN to 1, 2 or 3, and add option/unicode.c to the project. The LFN feature requiers a certain working buffer in addition. The buffer size can be configured by _MAX_LFN according to the available memory. The length of an LFN will reach up to 255 characters, so that the _MAX_LFN should be set to 255 for full featured LFN operation. If the size of working buffer is insufficient for the input file name, the file function fails with FR_INVALID_NAME. When use any re-entry to the API with the LFN feature, _USE_LFN must be set to 2 or 3. In this case, the file function allocates the working buffer on the stack or heap. The working buffer occupies (_MAX_LFN + 1) * 2 bytes.

    +

    FatFs module supports long file name (LFN). The two different file names, short file name (SFN) and LFN, of a file is transparent on the API except for f_readdir function. The support for LFN is disabled by default. To enable the LFN, set _USE_LFN to 1, 2 or 3, and add option/unicode.c to the project. The LFN requiers a certain working buffer in addition. The buffer size can be configured by _MAX_LFN according to the available memory. The length of an LFN will be up to 255 characters, so that the _MAX_LFN should be set to 255 for all file names. If the size of working buffer is insufficient for the input file name, the file function fails with FR_INVALID_NAME. When use any re-entry to the API with LFN is enabled, _USE_LFN must be set to 2 or 3. In this case, the file function allocates the working buffer on the stack or heap. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and additional 608 bytes when exFAT enabled.

    - + @@ -152,20 +154,27 @@ _WORD_ACCESS 0 or 1 (Set for 1 if possible)
    LFN cfg. at CM3/gccWith LFN at CM3+gcc
    _CODE_PAGECode size
    SBCS+2.8K
    932(Japanese)+62.6k
    949(Korean)+139k
    950(Traditional Chinese)+111k
    -

    When the LFN feature is enabled, the module size will be increased depends on the configured code page. Right table shows how much code size increased when LFN feature is enabled with some code pages. Especially, in the CJK region, tens of thousands of characters are being used. Unfortunately, it requires a huge OEM-Unicode bidirectional conversion table and the module size will be drastically increased as shown in the table. As the result, the FatFs with LFN feature with those code pages will not able to be implemented to most 8-bit microcontrollers.

    -

    Note that the LFN feature on the FAT file system is a patent of Microsoft Corporation. This is not the case on FAT32 but most FAT32 drivers come with the LFN feature. FatFs can swich the LFN feature on/off by configuration option. When enable LFN feature on the commercial products, a license from Microsoft may be required depends on the final destination.

    +

    When the LFN is enabled, the module size will be increased depends on the configured code page. Right table shows how much code size increased when LFN is enabled with some code pages. Especially, in the CJK region, tens of thousands of characters are being used. Unfortunately, it requires a huge OEM-Unicode bidirectional conversion table and the module size will be drastically increased as shown in the table. As the result, the FatFs with LFN enebled with those code pages will not able to be ported on the most 8-bit MCU systems.

    +

    Note that the support for LFN on the FAT volume is a patent of Microsoft Corporation. This is not the case on FAT32 but most FAT32 drivers come with the support for LFN. FatFs can switch the LFN on/off by configuration option. When enable LFN on the commercial products, you will need to be licensed by Microsoft depends on the final destination of the products.

    Unicode API

    -

    By default, FatFs uses ANSI/OEM code set on the API even at LFN configuration. FatFs can also switch the character encoding on the API to Unicode by _LFN_UNICODE option. This means that the FatFs supports the True-LFN feature. For more information, refer to the description in the file name.

    +

    By default, FatFs uses ANSI/OEM code set on the API even at LFN configuration. FatFs can also switch the character encoding on the API to Unicode by configuration option _LFN_UNICODE. This means that FatFs supports the full featured LFN specification. The data type TCHAR specifies any string on the API is an alias of char(ANSI) or WCHAR(UTF-16). For more information, refer to the description in the file name.

    +
    +

    exFAT File System

    +

    The exFAT (Microsoft's Extended File Allocation Table) file system is a replacement of the FAT file system which has been widely used in the embedded systems and consumer devices. It is adopted by SDA (SD Association) as a recommended file system for high capacity SD cards (>32GB) and they are being shipped with this format, so that the exFAT will soon become one of the standard file systems for removable media.

    +

    The exFAT file system allows the file size larger than 4 GiB limit what FAT file system allows upto and some file system overhead, especially file allocation delay, are reduced as well. This feature improves the write throughput to the file. However a problem on the current implementation of FatFs is that write throughput at writing to the growing edge of the fragmented file gets less than the throughput on the FAT volume. Pre-allocating a contiguous block with f_expand function may be a workaround of this problem.

    +

    Note that the exFAT is a patent of Microsoft Corporation. The exFAT function of FatFs is an implementation based on US. Pat. App. Pub. No. 2009/0164440 A1. FatFs module can swich the exFAT on or off by configuration option. When enable the exFAT on the commercial products, you will need to be licensed by Microsoft depends on the final destination of the products.

    +

    Remark: Enabling exFAT discards C89 compatibility because of need for 64-bit integer type.

    +

    Re-entrancy

    -

    The file operations to the different volume is always re-entrant regardless of configurations and it can work simultaneously without any mutual exclusion.

    -

    The file operations to the same volume is not re-entrant but it can also be configured to thread-safe by _FS_REENTRANT option. It enables to control exclusive use of each file system object. In this case, also the OS dependent synchronization object control functions, ff_cre_syncobj/ff_del_syncobj/ff_req_grant/ff_rel_grant, needed to be added to the project. There are some examples in the option/syscall.c.

    -

    When a file function is called while the volume is being accessed by any other task, the file function to the volume will be suspended until that task leaves the file function. If the wait time exceeded a period defined by _TIMEOUT, the file function will abort with FR_TIMEOUT. The timeout feature might not be supported on the some RTOSs.

    +

    The file operations to the different volume is always re-entrant regardless of configurations except when LFN enabled with static working buffer. It can work simultaneously without any mutual exclusion.

    +

    The file operations to the same volume is not re-entrant but it can also be configured thread-safe by option _FS_REENTRANT. It enables to control exclusive use of each file system object. In this case, also the OS dependent synchronization object control functions, ff_cre_syncobj/ff_del_syncobj/ff_req_grant/ff_rel_grant, needed to be added to the project. There are some examples in the option/syscall.c.

    +

    When a file function is called while the volume is being accessed by any other task, the file function to the volume will be suspended until that task leaves the file function. If the wait time exceeded a period defined by _TIMEOUT, the file function will abort with FR_TIMEOUT. The timeout function might not be supported on the some RTOSs.

    There is an exception on the re-entrancy for f_mount/f_mkfs/f_fdisk function. These volume management functions are not re-entrant to the same volume and corresponding physical drive. When use these functions, any other tasks need to avoid to access the volume.

    Note that this section describes on the re-entrancy of the FatFs module itself. The _FS_REENTRANT controls only exclusive use of each file system object and it does not that prevent to re-enter the low level disk functions. For example, only disk_status function can be re-entered at single volume system and any disk function can be re-entered at multiple volume system. Thus the low level disk I/O layer must be always thread-safe when any FatFs API is re-entered by two or more tasks.

    @@ -180,15 +189,15 @@ _WORD_ACCESS 0 or 1 (Set for 1 if possible)

    Performance Effective File Access

    For good read/write throughput on the small embedded systems with limited size of memory, application programmer should consider what process is done in the FatFs module. The file data on the volume is transferred in following sequence by f_read function.

    Figure 1. Sector misaligned read (short)
    - +

    Figure 2. Sector misaligned read (long)
    - +

    Figure 3. Fully sector aligned read
    - +

    -

    The file I/O buffer is a sector buffer to read/write a partial data on the sector. The sector buffer is either file private sector buffer on each file object or shared sector buffer in the file system object. The buffer configuration option _FS_TINY determins which sector buffer is used for the file data transfer. When tiny buffer configuration (1) is selected, data memory consumption is reduced _MAX_SS bytes each file object. In this case, FatFs module uses only a sector buffer in the file system object for file data transfer and FAT/directory access. The disadvantage of the tiny buffer configuration is: the FAT data cached in the sector buffer will be lost by file data transfer and it must be reloaded at every cluster boundary. However it will be suitable for most application from view point of the decent performance and low memory comsumption.

    +

    The file I/O buffer is a sector buffer to read/write a part of data on the sector. The sector buffer is either file private sector buffer on each file object or shared sector buffer in the file system object. The buffer configuration option _FS_TINY determins which sector buffer is used for the file data transfer. When tiny buffer configuration (1) is selected, data memory consumption is reduced _MAX_SS bytes each file object. In this case, FatFs module uses only a sector buffer in the file system object for file data transfer and FAT/directory access. The disadvantage of the tiny buffer configuration is: the FAT data cached in the sector buffer will be lost by file data transfer and it must be reloaded at every cluster boundary. However it will be suitable for most application from view point of the decent performance and low memory comsumption.

    Figure 1 shows that a partial sector, sector misaligned part of the file, is transferred via the file I/O buffer. At long data transfer shown in Figure 2, middle of transfer data that covers one or more sector is transferred to the application buffer directly. Figure 3 shows that the case of entier transfer data is aligned to the sector boundary. In this case, file I/O buffer is not used. On the direct transfer, the maximum extent of sectors are read with disk_read function at a time but the multiple sector transfer is divided at cluster boundary even if it is contiguous.

    Therefore taking effort to sector aligned read/write accesss eliminates buffered data transfer and the read/write performance will be improved. Besides the effect, cached FAT data will not be flushed by file data transfer at the tiny configuration, so that it can achieve same performance as non-tiny configuration with small memory footprint.

@@ -199,12 +208,12 @@ _WORD_ACCESS 0 or 1 (Set for 1 if possible)

Using Mutiple-Sector Write

Figure 6. Comparison between Multiple/Single Sector Write
-fig.6 +fig.6
-

The write throughput of the flash memory media becomes the worst at single sector write transaction. The write throughput increases as the number of sectors per a write transaction as shown in Figure 6. This effect more appers at faster interface speed and the performance ratio often becomes grater than ten. This graph is clearly explaining how fast is multiple block write (W:16K, 32 sectors) than single block write (W:100, 1 sector), and also larger card tends to be slow at single block write. Number of write transactions also affects life time of the flash memory media. When compared at same amount of write data, the single sector write in Figure 6 above wears flash memory media 16 times more than multiple sector write in Figure 6 below. Single sector write is pretty pain for the flash memory media.

-

Therefore the application program should write the data in large block as possible. The ideal write chunk size and alighment is size of sector, and size of cluster is the best. Of course all layers between the application and the storage device must have consideration on multiple sector write, however most of open-source memory card drivers lack it. Do not split a multiple sector write request into single sector write transactions or the write throughput gets poor. Note that FatFs module and its sample disk drivers supprt multiple sector read/write feature.

+

The write throughput of the flash memory media becomes the worst at single sector write transaction. The write throughput increases as the number of sectors per a write transaction as shown in Figure 6. This effect more appers at faster interface speed and the performance ratio often becomes grater than ten. This graph is clearly explaining how fast is multiple block write (W:16K, 32 sectors) than single block write (W:100, 1 sector), and also larger card tends to be slow at single block write. Number of write transactions also affects life time of the flash memory media. When compared at same amount of write data, the single sector write in Figure 6 above wears flash memory media 16 times more than multiple sector write in Figure 6 below. Single sector write is pretty pain for the flash memory media.

+

Therefore the application program should write the data in large block as possible. The ideal write chunk size and alighment is size of sector, and size of cluster is the best. Of course all layers between the application and the storage device must have consideration on multiple sector write, however most of open-source memory card drivers lack it. Do not split a multiple sector write request into single sector write transactions or the write throughput gets poor. Note that FatFs module and its sample disk drivers supprt multiple sector read/write operation.

Forcing Memory Erase

-

When remove a file with f_remove function, the data clusters occupied by the file are marked 'free' on the FAT. But the data sectors containing the file data are not that applied any process, so that the file data left occupies a part of the flash memory array as 'live block'. If the file data is forced erased on removing the file, those data blocks will be turned in to the free block pool. This may skip internal block erase operation to the data block on next write operation. As the result the write performance might be improved. FatFs can manage this feature by setting _USE_TRIM to 1. Note that this is an expectation of internal process of the flash memory storage and not that always effective. Also f_remove function will take a time when remove a large file. Most applications will not need this feature.

+

When remove a file with f_unlink function, the data clusters occupied by the file are marked 'free' on the FAT. But the data sectors containing the file data are not that applied any process, so that the file data left occupies a part of the flash memory array as 'live block'. If the file data can be erased on removing the file, those data blocks will be turned into the free block pool. This may skip internal block erase operation to the data block on next write operation. As the result the write performance might be improved. FatFs can manage this function by setting _USE_TRIM to 1. Note that this is an expectation of internal process of the storage device and not that always effective. Most applications will not need this function. Also f_unlink function can take a time when remove a large file.

@@ -212,11 +221,11 @@ Figure 6. Comparison between Multiple/Single Sector Write

If a write operation to the FAT volume is interrupted due to any accidental failure, such as sudden blackout, incorrect media removal and unrecoverable disk error, the FAT structure on the volume can be broken. Following images shows the critical section of the FatFs module.

Figure 4. Long critical section
-fig.4 +fig.4
Figure 5. Minimized critical section
-fig.5 +fig.5

An interruption in the red section can cause a cross link; as a result, the object being changed can be lost. If an interruption in the yellow section is occured, there is one or more possibility listed below.

@@ -234,26 +243,28 @@ Figure 5. Minimized critical section

Extended Use of FatFs API

These are examples of extended use of FatFs APIs. New item will be added whenever a useful code is found.

    -
  1. Open or create a file for append
  2. -
  3. Empty a directory
  4. -
  5. Allocate contiguous area to the file
  6. -
  7. Function/Compatible checker for low level disk I/O module
  8. -
  9. FAT image creator
  10. +
  11. Open or create a file for append (for R0.12 and older)
  12. +
  13. Empty a directory
  14. +
  15. Allocate contiguous area to the file (for R0.11a and older)
  16. +
  17. Function/compatibility checker for low level disk I/O module
  18. +
  19. FAT image creator

About FatFs License

-

FatFs has being developped as a personal project of author, ChaN. It is free from the code anyone else wrote. Following code block shows a copy of the FatFs license document that included in the source files.

+

FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that included in the source files.

 /*----------------------------------------------------------------------------/
-/  FatFs - FAT file system module  R0.11                 (C)ChaN, 2015
+/  FatFs - Generic FAT file system module  R0.12a                             /
 /-----------------------------------------------------------------------------/
-/ FatFs module is a free software that opened under license policy of
-/ following conditions.
 /
-/ Copyright (C) 2015, ChaN, all right reserved.
+/ Copyright (C) 2016, ChaN, all right reserved.
 /
+/ FatFs module is an open source software. Redistribution and use of FatFs in
+/ source and binary forms, with or without modification, are permitted provided
+/ that the following condition is met:
+
 / 1. Redistributions of source code must retain the above copyright notice,
 /    this condition and the following disclaimer.
 /
@@ -263,7 +274,7 @@ Figure 5. Minimized critical section
/ by use of this software. /----------------------------------------------------------------------------*/
-

Therefore FatFs license is one of the BSD-style licenses but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code or any forms without source code, does not need to explain about use of FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most open source software licenses including GNU GPL. When you redistribute the FatFs source code with any modification or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software licenses that not conflict with FatFs license.

+

Therefore FatFs license is one of the BSD-style licenses but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, does not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most open source software licenses including GNU GPL. When you redistribute the FatFs source code with any changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software licenses that not conflict with FatFs license.

Return Home

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/chmod.html b/firmware/chibios-portapack/ext/fatfs/doc/en/chmod.html index 8604873c..6a3ffd00 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/chmod.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/chmod.html @@ -72,7 +72,7 @@ FRESULT f_chmod (

QuickInfo

-

Available when _FS_READONLY == 0 and _FS_MINIMIZE == 0.

+

Available when _FS_READONLY == 0 and _USE_CHMOD == 1.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/close.html b/firmware/chibios-portapack/ext/fatfs/doc/en/close.html index e7ac2fd1..c16dfa6f 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/close.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/close.html @@ -45,7 +45,7 @@ FRESULT f_close (

Description

The f_close function closes an open file object. If any data has been written to the file, the cached information of the file is written back to the volume. After the function succeeded, the file object is no longer valid and it can be discarded.

-

Note that if the file object is in read-only mode and _FS_LOCK option is not enabled, the file object can also be discarded without this process. However this is not recommended for future compatibility.

+

Note that if the file object is in read-only mode and _FS_LOCK is not enabled, the file object can also be discarded without this process. However this is not recommended for future compatibility.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/closedir.html b/firmware/chibios-portapack/ext/fatfs/doc/en/closedir.html index 1494d7e4..253d67a6 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/closedir.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/closedir.html @@ -44,7 +44,7 @@ FRESULT f_closedir (

Description

The f_closedir function closes an open directory object. After the function succeeded, the directory object is no longer valid and it can be discarded.

-

Note that the directory object can also be discarded without this process if _FS_LOCK option is not enabled. However this is not recommended for future compatibility.

+

Note that the directory object can also be discarded without this process when option _FS_LOCK is not enabled. However this is not recommended for future compatibility.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/config.html b/firmware/chibios-portapack/ext/fatfs/doc/en/config.html index 82e25c6c..880c3250 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/config.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/config.html @@ -17,7 +17,7 @@

Function Configurations

_FS_READONLY

-

Read/Write(0) or Read-only(1). Read-only configuration removes writing API functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename, f_truncate, f_getfree and optional writing functions as well.

+

Read/Write (0) or Read-only (1). Read-only configuration removes writing API functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename, f_truncate, f_getfree and optional writing functions as well.

_FS_MINIMIZE

This option defines minimization level to remove some basic API functions as follows:

@@ -39,19 +39,25 @@

_USE_FIND

-

Disable(0) or Enable(1). This option switches filtered directory read feature and related functions, f_findfirst and f_findnext.

+

Disable (0) or Enable (1) filtered directory read functions, f_findfirst and f_findnext. Also _FS_MINIMIZE needs to be 0 or 1.

_USE_MKFS

-

Disable(0) or Enable(1). This option switches f_mkfs function.

+

Disable (0) or Enable (1) f_mkfs function.

_USE_FASTSEEK

-

Disable(0) or Enable(1). This option switches fast seek feature to enable accelerated mode of f_lseek, f_read and f_write function. For more information, read here.

+

Disable (0) or Enable (1) fast seek function to enable accelerated mode of f_lseek, f_read and f_write function. For more information, read here.

+ +

_USE_EXPAND

+

Disable (0) or Enable (1) , f_enpand function. Also _FS_READONLY needs to be 0.

+ +

_USE_CHMOD

+

Disable (0) or Enable (1) metadata manipulation functions, f_chmod and f_utime. Also _FS_READONLY needs to be 0.

_USE_LABEL

-

Disable(0) or Enable(1). This option switches volume label functions, f_getlabel and f_setlabel.

+

Disable (0) or Enable (1) volume label functions, f_getlabel and f_setlabel.

_USE_FORWARD

-

Disable(0) or Enable(1). This option switches f_forward function. also _FS_TINY needs to be set to 1.

+

Disable (0) or Enable (1) f_forward function.

@@ -88,23 +94,23 @@

_USE_LFN

-

This option switches the LFN feature. When enable the LFN feature, Unicode handling functions option/unicode.c must be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. When use stack for the working buffer, take care on stack overflow. When use heap memory for the working buffer, memory management functions, ff_memalloc and ff_memfree, must be added to the project.

+

This option switches the support for long file name (LFN). When enable the LFN, Unicode support functions option/unicode.c need to be added to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and additional 608 bytes when exFAT enabled. When use stack for the working buffer, take care on stack overflow. When use heap memory for the working buffer, memory management functions, ff_memalloc and ff_memfree, need to be added to the project.

- +
ValueDescription
0Disable LFN feature. Only 8.3 format can be used.
0Disable LFN. Only 8.3 format can be used.
1Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
2Enable LFN with dynamic working buffer on the STACK.
3Enable LFN with dynamic working buffer on the HEAP.

_MAX_LFN

-

This option defines the size of LFN working buffer from 12 to 255 in unit of character. This option has no effect when LFN feature is disabled.

+

This option defines the size of LFN working buffer from 12 to 255 in unit of character. This option has no effect when LFN is disabled.

_LFN_UNICODE

-

ANSI/OEM(0) or Unicode(1). This option switches character encoding on the API. To use Unicode (UTF16) string for the path name, enable LFN feature and set this option to 1. This option also affects behavior of string I/O functions. When LFN feature is disabled, this option must be 0. For more information, read here.

+

ANSI/OEM (0) or Unicode (1). This option switches character encoding on the API. To use Unicode (UTF16) string for the path name, enable LFN and set this option to 1. This option also affects behavior of string I/O functions. When LFN is disabled, this option must be 0. For more information, read here.

_STRF_ENCODE

-

When Unicode API is selected by _LFN_UNICODE, this option defines the assumption of character encoding on the file to be read/written via string I/O functions, f_gets, f_putc, f_puts and f_printf. This option has no effect when _LFN_UNICODE == 0.

+

When Unicode API is selected by setting _LFN_UNICODE 1, this option defines the assumption of character encoding on the file to be read/written via string I/O functions, f_gets, f_putc, f_puts and f_printf. This option has no effect when _LFN_UNICODE = 0.

@@ -114,11 +120,11 @@
ValueDescription
0ANSI/OEM

_FS_RPATH

-

This option configures relative path feature. Note that directory items read via directory functions are affected by this option. For more information, read here.

+

This option configures relative path function. For more information, read here.

- - + +
ValueDescription
0Disable relative path feature and remove related functions.
1Enable relative path feature. f_chdir and f_chdrive function is available.
0Disable relative path function and remove related functions.
1Enable relative path function. f_chdir and f_chdrive function is available.
2f_getcwd function is available in addition to 1
@@ -129,22 +135,22 @@

Volume/Drive Configurations

_VOLUMES

-

This option configures number of volumes (logical drives, from 1 to 9) to be used.

+

This option configures number of volumes (logical drives, from 1 to 10) to be used.

_STR_VOLUME_ID

-

Disable(0) or Enable(1). This option switches string volume ID feature. When enabled, also pre-defined strings in _VOLUME_STRS can be used as drive identifier in the path name.

+

Disable (0) or Enable (1). This option switches the support for string volume ID. When enabled, also pre-defined strings in _VOLUME_STRS can be used as drive identifier in the path name.

_VOLUME_STRS

-

This option defines the drive ID strings for each logical drives. Number of items must be equal to _VOLUMES. Valid characters for the drive ID strings are: A-Z and 0-9.

+

This option defines the drive ID strings for each logical drives. Number of items must not be less than _VOLUMES. Valid characters for the drive ID string are: A-Z and 0-9.

_MULTI_PARTITION

-

Disable(0) or Enable(1). This option switches multi-partition feature. By default (0), each logical drive number is bound to the same physical drive number and only an FAT volume each physical drive are mounted. When enabled, each logical drive is bound to the partition on the physical drive listed in the user defined partition resolution table VolToPart[]. Also f_fdisk funciton will be enabled. For more information, read here.

+

Disable (0) or Enable (1). This option switches multi-partition function. By default (0), each logical drive number is bound to the same physical drive number and only an FAT volume in the physical drive is mounted. When enabled, each logical drive is bound to the partition on the physical drive listed in the user defined partition resolution table VolToPart[]. Also f_fdisk funciton will be available. For more information, read here.

_MIN_SS, _MAX_SS

-

This set of options defines size of sector on the low level disk I/O interface, disk_read and disk_write function. Valid numbers are 512, 1024, 2048 and 4096. _MIN_SS defines minimum size of sector and _MAX_SS defines the maximum size of sector. Always set both 512 for all type of memory cards and harddisk. But a larger value may be required for on-board flash memory and some type of optical media. When _MAX_SS > _MIN_SS, FatFs is configured to variable sector size and GET_SECTOR_SIZE command must be implemented to the disk_ioctl function.

+

This set of options defines the size of sector on low level disk I/O interface, disk_read and disk_write function. Valid values are 512, 1024, 2048 and 4096. _MIN_SS defines minimum sector size and _MAX_SS defines the maximum sector size. Always set both 512 for any type of memory card and harddisk. But a larger value may be required for on-board flash memory and some type of optical media. When _MAX_SS > _MIN_SS, FatFs is configured to variable sector size and GET_SECTOR_SIZE command must be implemented to the disk_ioctl function.

_USE_TRIM

-

Disable(0) or Enable(1). This option switches ATA-TRIM feature. To enable Trim feature, also CTRL_TRIM command should be implemented to the disk_ioctl function.

+

Disable (0) or Enable (1). This option switches ATA-TRIM function. To enable Trim function, also CTRL_TRIM command should be implemented to the disk_ioctl function.

_FS_NOFSINFO

0 to 3. If you need to know correct free space on the FAT32 volume, set bit 0 of this option, and f_getfree function at first time after volume mount will force a full FAT scan. Bit 1 controls the use of last allocated cluster number.

@@ -163,55 +169,33 @@

System Configurations

_FS_TINY

-

Normal(0) or Tiny(1). At the tiny configuration, size of the file object FIL is reduced _MAX_SS bytes. Instead of private data buffer eliminated from the file object, common sector buffer in the file system object FATFS is used for the file data transfer.

+

Normal (0) or Tiny (1). At the tiny configuration, size of the file object FIL is reduced _MAX_SS bytes. Instead of private data buffer eliminated from the file object, common sector buffer in the file system object FATFS is used for the file data transfer.

+ +

_FS_EXFAT

+

This option switches support for the exFAT file system in addition to the FAT file system, Enabled(1) or Disabled(1). To enable this feature, also LFN must be enabled and configureing _LFN_UNICODE = 1 and _MAX_LFN = 255 is recommended for full-featured exFAT function. Note that enabling exFAT discards C89 compatibility because of need for 64-bit integer type.

_FS_NORTC

-

Use RTC(0) or Do not use RTC(1). This option controls timestamp feature. If the system does not have an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable the timestamp feature. Any object modified by FatFs will have a fixed timestamp value defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR. To use the timestamp feature, set _FS_NORTC to 0 and add get_fattime function to the project to get the current time form RTC. This option has no effect at read-only configuration.

+

Use RTC (0) or Do not use RTC (1). This option controls timestamp function. If the system does not have an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable the timestamp function. Any object modified by FatFs will have a fixed timestamp value defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR. To use the timestamp function, set _FS_NORTC = 0 and add get_fattime function to the project to get the current time form real-time clock. This option has no effect at read-only configuration.

_NORTC_MON, _NORTC_MDAY, _NORTC_YEAR

-

This set of options defines default timestamp to be used at no RTC systems. This option has no effect at read-only configuration or _FS_NORTC == 0.

+

This set of options defines the time to be used at no RTC systems. This option has no effect at read-only configuration or _FS_NORTC = 0.

_FS_LOCK

-

This option switches file lock feature to control duplicated file open and illegal operations to open objects. Note that the file lock feature is independent of re-entrancy. This option must be 0 at read-only configuration.

+

This option switches file lock function to control duplicated file open and illegal operations to open objects. Note that the file lock function is independent of re-entrancy. This option must be 0 at read-only configuration.

- - + +
ValueDescription
0Disable file lock feature. To avoid volume corruption, application program should avoid illegal open, remove and rename to the open objects.
>0Enable file lock feature. The value defines how many files/sub-directories can be opened simultaneously under file lock control. Illigal operations to the open object will be rejected with FR_LOCKED.
0Disable file lock function. To avoid volume corruption, application program should avoid illegal open, remove and rename to the open objects.
>0Enable file lock function. The value defines how many files/sub-directories can be opened simultaneously under file lock control. Illigal operations to the open object will be rejected with FR_LOCKED.

_FS_REENTRANT

-

Disable(0) or Enable(1). This option switches the re-entrancy (thread safe) of the FatFs module itself. Note that file/directory access to the different volume is always re-entrant and it can work simultaneously regardless of this option but volume control functions, f_mount, f_mkfs and f_fdisk, are always not re-entrant. Only file/directory access to the same volume, in other words, exclusive use of each file system object, is under control of this feature. To enable this feature, also user provided synchronization handlers, ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj, must be added to the project. Samples are available in option/syscall.c.

+

Disable (0) or Enable (1). This option switches the re-entrancy (thread safe) of the FatFs module itself. Note that file/directory access to the different volume is always re-entrant and it can work simultaneously regardless of this option but volume control functions, f_mount, f_mkfs and f_fdisk, are always not re-entrant. Only file/directory access to the same volume, in other words, exclusive use of each file system object, is under control of this function. To enable this feature, also user provided synchronization handlers, ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj, need to be added to the project. Sample code is available in option/syscall.c.

_FS_TIMEOUT

-

Number of time ticks to abort the file function with FR_TIMEOUT when wait time is too long. This option has no effect when _FS_REENTRANT == 0.

+

Number of time ticks to abort the file function with FR_TIMEOUT when wait time is too long. This option has no effect when _FS_REENTRANT = 0.

_SYNC_t

-

This option defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, SemaphoreHandle_t and etc. A header file for O/S definitions needs to be included somewhere in the scope of ff.c. This option has no effect when _FS_REENTRANT == 0.

- -

_WORD_ACCESS

-

This is an only platform dependent option. It defines which access method is used to the word data on the FAT volume.

- - - - -
ValueDescription
0Byte-by-byte access. Always compatible with all platforms.
1Word access. Code size will be slightly reduced but do not choose this unless under both the following conditions.
-* Unaligned memory access is always allowed to ALL instructions.
-* Byte order on the memory is little-endian.
-

Following table shows an example of allowable settings of some type of processors.

-
-   ARM7TDMI   0   *2         ColdFire   0   *1         V850E      0   *2
-   Cortex-M3  0   *3         Z80        0/1            V850ES     0/1
-   Cortex-M0  0   *2         x86        0/1            TLCS-870   0/1
-   AVR        0/1            RX600(LE)  0/1            TLCS-900   0/1
-   AVR32      0   *1         RL78       0   *2         R32C       0   *2
-   PIC18      0/1            SH-2       0   *1         M16C       0/1
-   PIC24      0   *2         H8S        0   *1         MSP430     0   *2
-   PIC32      0   *1         H8/300H    0   *1         8051       0/1
-
-   *1:Big-endian.
-   *2:Unaligned memory access is not supported.
-   *3:Some compilers generate LDM/STM for mem_cpy function.
-
+

This option defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, SemaphoreHandle_t and etc. A header file for O/S definitions needs to be included somewhere in the scope of ff.c. This option has no effect when _FS_REENTRANT = 0.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/dinit.html b/firmware/chibios-portapack/ext/fatfs/doc/en/dinit.html index d66a0a19..4a003e70 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/dinit.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/dinit.html @@ -38,7 +38,7 @@ DSTATUS disk_initialize (

Description

This function initializes the storage device and put it ready to generic read/write. When the function succeeded, STA_NOINIT flag in the return value is cleared.

-

This function is under control of FatFs module. Application program MUST NOT call this function, or FAT structure on the volume can be broken. To re-initialize the file system, use f_mount function instead.

+

Remarks: This function needs to be under control of FatFs module. Application program MUST NOT call this function, or FAT structure on the volume can be broken. To re-initialize the file system, use f_mount function instead.

Return

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/dioctl.html b/firmware/chibios-portapack/ext/fatfs/doc/en/dioctl.html index e05b80af..7c692511 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/dioctl.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/dioctl.html @@ -13,7 +13,7 @@

disk_ioctl

-

The disk_ioctl function cntrols device specific features and miscellaneous functions other than generic read/write.

+

The disk_ioctl function controls device specific features and miscellaneous functions other than generic read/write.

 DRESULT disk_ioctl (
   BYTE pdrv,     /* [IN] Drive number */
@@ -61,7 +61,7 @@ DRESULT disk_ioctl (
 GET_SECTOR_COUNTReturns number of available sectors on the drive into the DWORD variable pointed by buff. This command is used by only f_mkfs and f_fdisk function to determine the volume/partition size to be created. Required at _USE_MKFS == 1 or _MULTI_PARTITION == 1.
 GET_SECTOR_SIZEReturns sector size of the media into the WORD variable pointed by buff. Valid return values of this command are 512, 1024, 2048 and 4096. This command is required only at variable sector size configuration, _MAX_SS > _MIN_SS. At fixed sector size configuration, _MAX_SS == _MIN_SS, this command is not used and the device must work at that sector size.
 GET_BLOCK_SIZEReturns erase block size of the flash memory media in unit of sector into the DWORD variable pointed by buff. The allowable value is from 1 to 32768 in power of 2. Return 1 if the erase block size is unknown or non flash memory media. This command is used by only f_mkfs function and it attempts to align data area to the erase block boundary. Required at _USE_MKFS == 1.
-CTRL_TRIMInforms the device the data on the block of sectors that specified by DWORD array {<start sector>, <end sector>} pointed by buff is no longer needed and it may be erased. This is an identical command to Trim of ATA device. When this feature is not supported or not a flash memory device, nothing to do for this command. The FatFs does not check the result code and the file function is not affected even if the sector block was not erased well. This command is called on removing a cluster chain and f_mkfs function. Required at _USE_TRIM == 1.
+CTRL_TRIMInforms the device the data on the block of sectors that specified by DWORD array {<start sector>, <end sector>} pointed by buff is no longer needed and it may be erased. This is an identical command to Trim of ATA device. Nothing to do for this command if this funcion is not supported or not a flash memory device. The FatFs does not check the result code and the file function is not affected even if the sector block was not erased well. This command is called on remove a cluster chain and in the f_mkfs function. Required at _USE_TRIM == 1.
 
 
 

FatFs never uses any device dependent command nor user defined command. Following table shows an example of non-standard commands usable for some applications.

@@ -82,6 +82,9 @@ DRESULT disk_ioctl ( ATA_GET_REVGet the revision string into a 16-byte buffer pointed by buff. (ATA/CFC specific command) ATA_GET_MODELGet the model string into a 40-byte buffer pointed by buff. (ATA/CFC specific command) ATA_GET_SNGet the serial number string into a 20-byte buffer pointed by buff. (ATA/CFC specific command) +ISDIO_READRead a block of iSDIO registers specified by command structure pointed by buff. (FlashAir specific command) +ISDIO_WRITEWrite a block of data to iSDIO registers specified by command structure pointed by buff. (FlashAir specific command) +ISDIO_MRITEChange bits in an iSDIO register specified by command structure pointed by buff. (FlashAir specific command)
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/dread.html b/firmware/chibios-portapack/ext/fatfs/doc/en/dread.html index ba31d034..14227de6 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/dread.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/dread.html @@ -34,7 +34,7 @@ DRESULT disk_read (
sector
Start sector number in 32-bit LBA.
count
-
Number of sectors to read. FatFs specifies the value in range of from 1 to 128.
+
Number of sectors to read.
@@ -56,12 +56,12 @@ DRESULT disk_read (

Description

-

The data read/write operation to the storage devices is done in unit of sector. FatFs supports the sector size in range of from 512 to 4096 bytes. When FatFs is configured to fixed sector size (_MIN_SS == MAX_SS, this will be the most case), the read/write function must work at that sector size. When FatFs is configured to variable sector size (_MIN_SS < MAX_SS), sector size is inquired with disk_ioctl function following disk_initialize function. -

The memory address specified by buff is not that always aligned to word boundary because the argument is defined as BYTE*. The misaligned read/write request can occure at direct transfer. If the bus architecture, especially DMA controller, does not allow misaligned memory access, it should be solved in this function. There are some workarounds described below to avoid this issue.

+

The data read/write operation to the storage devices is done in unit of sector. FatFs supports the sector size in range of from 512 to 4096 bytes. When FatFs is configured to fixed sector size (_MIN_SS == MAX_SS, this will be the most case), the read/write function must work at that sector size. When FatFs is configured to variable sector size (_MIN_SS != MAX_SS), sector size is inquired with disk_ioctl function following disk_initialize function.

+

The memory address specified by buff is not that always aligned to word boundary because the argument is defined as BYTE*. The unaligned read/write request can occure at direct transfer. If the bus architecture, especially DMA controller, does not allow unaligned memory access, it should be solved in this function. There are some workarounds described below to avoid this issue.

Generally, a multiple sector transfer request must not be split into single sector transactions to the storage device, or you will not get good read throughput.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/dstat.html b/firmware/chibios-portapack/ext/fatfs/doc/en/dstat.html index cadcd41d..07514609 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/dstat.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/dstat.html @@ -35,11 +35,11 @@ DSTATUS disk_status (

The current drive status is returned in combination of status flags described below. FatFs refers only STA_NOINIT and STA_PROTECT.

STA_NOINIT
-
Indicates that the device is not initialized. This flag is set on system reset, media removal or failure of disk_initialize function. It is cleared on disk_initialize function succeeded. Media change that occurs asynchronously must be captured and reflect it to the status flags, or auto-mount feature will not work correctly. If the system does not support media change detect feature, application program needs to force de-initialize the file system object with f_mount function after the media change.
+
Indicates that the device is not initialized and not ready to work. This flag is set on system reset, media removal or failure of disk_initialize function. It is cleared on disk_initialize function succeeded. Any media change that occurs asynchronously must be captured and reflect it to the status flags, or auto-mount function will not work correctly. If the system does not support media change detection, application program needs to force de-initialize the file system object and re-mount the volume with f_mount function after each media change.
STA_NODISK
-
Indicates that no medium in the drive. This is always cleared on fixed disk drive. Note that FatFs does not refer this flag.
+
Indicates that no medium in the drive. This is always cleared at fixed disk drive. Note that FatFs does not refer this flag.
STA_PROTECT
-
Indicates that the medium is write protected. This is always cleared on the drives without write protect feature. Not valid if no medium in the drive.
+
Indicates that the medium is write protected. This is always cleared at the drives without write protect function. Not valid if no medium in the drive.
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/dwrite.html b/firmware/chibios-portapack/ext/fatfs/doc/en/dwrite.html index b29b6f34..4ee6d09a 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/dwrite.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/dwrite.html @@ -34,7 +34,7 @@ DRESULT disk_write (
sector
Start sector number in 32-bit LBA.
count
-
Number of sectors to write. FatFs specifies the value in range of from 1 to 128.
+
Number of sectors to write.
@@ -60,8 +60,8 @@ DRESULT disk_write (

Description

The specified memory address is not that always aligned to word boundary because the type of pointer is defined as BYTE*. For more information, refer to the description of disk_read function.

Generally, a multiple sector transfer request must not be split into single sector transactions to the storage device, or you will never get good write throughput.

-

FatFs expects delayed write feature of the disk control layer. The write operation to the media need not to be completed due to write operation is in progress or data is stored into the write-back cache when return from this function. But write data on the buff is invalid after return from this function. The write completion request is done by CTRL_SYNC command of disk_ioctl function. Therefore, if a delayed write feature is implemented, the write throughput will be improved.

-

Application program MUST NOT call this function, or FAT structure on the volume can be collapsed.

+

FatFs expects delayed write function of the disk control layer. The write operation to the media does not need to be completed when return from this function by what write operation is in progress or data is only stored into the write-back cache. But write data on the buff is invalid after return from this function. The write completion request is done by CTRL_SYNC command of disk_ioctl function. Therefore, if a delayed write function is implemented, the write throughput will be improved.

+

Remarks: Application program MUST NOT call this function, or FAT structure on the volume can be collapsed.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/eof.html b/firmware/chibios-portapack/ext/fatfs/doc/en/eof.html index 40944c3c..2e875062 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/eof.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/eof.html @@ -39,7 +39,7 @@ int f_eof (

Description

-

In this revision, this function is implemented as a macro. It has not any validation and mutual exclusion.

+

In this revision, this function is implemented as a macro. It does not have any validation and mutual exclusion.

 #define f_eof(fp) ((int)((fp)->fptr == (fp)->fsize))
 
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/error.html b/firmware/chibios-portapack/ext/fatfs/doc/en/error.html index b8ceef5e..3476f55c 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/error.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/error.html @@ -39,9 +39,9 @@ int f_error (

Description

-

In this revision, this function is implemented as a macro. It has not any validation and mutual exclusion.

+

In this revision, this function is implemented as a macro. It does not have any validation and mutual exclusion.

-#define f_error(fp) ((fp)->flag)
+#define f_error(fp) ((fp)->err)
 
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/expand.html b/firmware/chibios-portapack/ext/fatfs/doc/en/expand.html new file mode 100644 index 00000000..434580cf --- /dev/null +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/expand.html @@ -0,0 +1,116 @@ + + + + + + + + +FatFs - f_expand + + + + +
+

f_expand

+

The f_expand function prepares or allocates a contiguous data area to the file.

+ +
+FRESULT f_expand (
+  FIL*    fp,  /* [IN] File object */
+  FSIZE_t fsz, /* [IN] File size expanded to */
+  BYTE    opt  /* [IN] Operation mode */
+);
+
+
+ +
+

Parameters

+
+
fp
+
Pointer to the open file object.
+
fsz
+
Number of bytes in size to prepare or allocate for the file. The data type FSIZE_t is an alias of either DWORD(32-bit) or QWORD(64-bit) depends on the configuration option _FS_EXFAT.
+
opt
+
Operation mode. Prepare only (0) or Allocate now (1).
+
+
+ + + + + +
+

Description

+

The f_expand function prepares or allocates a contiguous data area to the file. When opt is 1, the function allocates a contiguous data area to the file. Unlike expansion of file by f_lseek function, the file must be truncated prior to use this function and read/write pointer of the file stays at top of the file after the function. The file content allocated with this function is undefined because no data is written to the file in this process. The function can fail with FR_DENIED due to some reasons below.

+
    +
  • No free contiguous space was found.
  • +
  • Size of the file was not zero.
  • +
  • The file has been opened in read-only mode.
  • +
  • Not allowable file size. (>= 4GiB on FAT volume)
  • +
+

When opt is 0, the function finds a contiguous data area and set it as suggested point for next allocation instead of allocating it to the file. The next cluster allocation is started at top of the contiguous area found by this function. Thus the write file is guaranteed be contiguous and no allocation delay until the size reaches that size at least unless any other operation to the volume with changes of FAT is performed.

+

The contiguous file would have an advantage at time-critical read/write operations. It reduces some overheads in the file system and the storage media caused by random access due to fragmented file data. Especially, at the exFAT volume, any FAT access for the contiguous file is completely eliminated and storage media will be accessed sequentially.

+

Also the contiguous file data can be easily accessed directly via low-level disk functions but it is not recommended in consideration for future compatibility.

+
+ +
+

QuickInfo

+

Available when _USE_EXPAND == 1 and _FS_READONLY == 0.

+
+ + +
+

Example

+
+    /* Creating a contiguous file */
+
+    /* Create a new file */
+    res = f_open(fp = malloc(sizeof (FIL)), "file.dat", FA_WRITE|FA_CREATE_ALWAYS);
+    if (res) { /* Check if the file has been opened */
+        free(fp);
+        ...
+    }
+
+    /* Alloacte a 100 MiB of contiguous area to the file */
+    res = f_expand(fp, 104857600, 1);
+    if (res) { /* Check if the file has been expanded */
+        ...
+        free(fp);
+        ...
+    }
+    /* Now you have a contiguous file accessible with fp */
+
+
+
+    /* Accessing the file data directly via low-level disk functions */
+
+    /* Get physical location of the file data */
+    drv = fp->obj.fs->drv;
+    sect = fp->obj.fs->database + fp->obj.fs->csize * (fp->obj.sclust - 2);
+
+    /* Write 2048 sectors from top of the file at a time */
+    res = disk_write(drv, buffer, sect, 2048);
+
+
+
+ + +
+

See Also

+

f_open, f_lseek, FIL

+
+ +

Return

+ + diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/fdisk.html b/firmware/chibios-portapack/ext/fatfs/doc/en/fdisk.html index 828bbe55..6581d87c 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/fdisk.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/fdisk.html @@ -16,9 +16,9 @@

The f_fdisk fucntion divides a physical drive.

 FRESULT f_fdisk (
-  BYTE  pdrv,         /* [IN] Physical drive number */
-  const DWORD part[], /* [IN] Partition size */
-  void* work          /* [IN] Work area */
+  BYTE  pdrv,        /* [IN] Physical drive number */
+  const DWORD* szt,  /* [IN] Partition map table */
+  void* work         /* [IN] Work area */
 );
 
@@ -27,9 +27,9 @@ FRESULT f_fdisk (

Parameters

pdrv
-
Specifies the physical drive to be divided.
-
part[]
-
Partition map table. It must have four items.
+
Specifies the physical drive to be divided. This is not the logical drive number but the drive identifier passed to the low level disk functions.
+
szt
+
Pointer to the first item of the partition map table.
work
Pointer to the function work area. The size must be at least _MAX_SS bytes.
@@ -48,7 +48,7 @@ FRESULT f_fdisk (

Description

-

The f_fdisk function creates a partition table into the MBR of the physical drive. The partitioning rule is in generic FDISK format, so that it can create upto four primary partitions. Logical volumes in the extended partition is not supported. The part[] with four items specifies how to divide the physical drive. The first item specifies the size of first primary partition and fourth item specifies the fourth primary partition. If the value is less than or equal to 100, it specifies percentage of the partition in the entire disk space. If it is larger than 100, it specifies the partition size in unit of sector.

+

The f_fdisk function creates partitions on the physical drive. The partitioning format is in generic FDISK format, so that it can create upto four primary partitions. Logical volumes in the extended partition is not supported. The partition map table with four items specifies how to divide the physical drive. The first item specifies the size of first primary partition and fourth item specifies the fourth primary partition. If the value is less than or equal to 100, it specifies the partition size in percentage of the entire drive space. If it is larger than 100, it specifies the partition size in unit of sector. The partitions are located on the drive in order of from first item.

@@ -74,15 +74,10 @@ FRESULT f_fdisk ( DWORD plist[] = {50, 50, 0, 0}; /* Divide drive into two partitions */ BYTE work[_MAX_SS]; - f_fdisk(0, plist, work); /* Divide physical drive 0 */ + f_fdisk(0, plist, work); /* Divide physical drive 0 */ - f_mount(&fs, "0:", 0); /* Register work area to the logical drive 0 */ - f_mkfs("0:", 0, 0); /* Create FAT volume on the logical drive 0. 2nd argument is ignored. */ - f_mount(0, "0:", 0); /* Unregister work area from the logical drive 0 */ - - f_mount(&fs, "1:", 0); /* Register a work area to the logical drive 1 */ - f_mkfs("1:", 0, 0); /* Create FAT volume on the logical drive 1. 2nd argument is ignored. */ - f_mount(0, "1:", 0); /* Unregister work area from the logical drive 1 */ + f_mkfs("0:", FMT_ANY, work, sizeof work); /* Create FAT volume on the logical drive 0 */ + f_mkfs("1:", FMT_ANY, work, sizeof work); /* Create FAT volume on the logical drive 1 */
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/filename.html b/firmware/chibios-portapack/ext/fatfs/doc/en/filename.html index 1faafdf9..23e6c49c 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/filename.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/filename.html @@ -16,10 +16,10 @@

Format of the path names

The format of path name on the FatFs module is similer to the filename specs of DOS/Windos as follows:

"[drive:][/]directory/file"
-

The FatFs module supports long file name (LFN) and 8.3 format file name (SFN). The LFN can be used when LFN feature is enabled (_USE_LFN > 0). The sub directories are separated with a \ or / in the same way as DOS/Windows API. Duplicated separators are skipped and ignored. Only a difference is that the logical drive is specified in a numeral with a colon. When drive number is omitted, the drive number is assumed as default drive (drive 0 or current drive).

+

The FatFs module supports long file name (LFN) and 8.3 format file name (SFN). The LFN can be used when (_USE_LFN != 0). The sub directories are separated with a \ or / in the same way as DOS/Windows API. Duplicated separators are skipped and ignored. Only a difference is that the logical drive is specified in a numeral with a colon. When drive number is omitted, the drive number is assumed as default drive (drive 0 or current drive).

Control characters ('\0' to '\x1F') are recognized as end of the path name. Leading/embedded spaces in the path name are valid as a part of the name at LFN configuration but the space is recognized as end of the path name at non-LFN configuration. Trailing spaces and dots are ignored at both configurations.

-

In default configuration (_FS_RPATH >= 0), it does not have a concept of current directory like OS oriented file system. All objects on the volume are always specified in full path name that follows from the root directory. Dot directory names (".", "..") are not allowed. Heading separator is ignored and it can be exist or omitted. The default drive is fixed to drive 0.

-

When relative path feature is enabled (_FS_RPATH >= 1), specified path is followed from the root directory if a heading separator is exist. If not, it is followed from the current directory of the drive set by f_chdir function. Dot names are also allowed for the path names. The default drive is the current drive set by f_chdrive function.

+

In default configuration (_FS_RPATH == 0), it does not have a concept of current directory like OS oriented file system. All objects on the volume are always specified in full path name that follows from the root directory. Dot directory names (".", "..") are not allowed. Heading separator is ignored and it can be exist or omitted. The default drive is fixed to drive 0.

+

When relative path is enabled (_FS_RPATH >= 1), specified path is followed from the root directory if a heading separator is exist. If not, it is followed from the current directory of the drive set by f_chdir function. Dot names are also allowed for the path names. The default drive is the current drive set by f_chdrive function.

@@ -31,11 +31,12 @@ - +
Path name_FS_RPATH == 0_FS_RPATH >= 1
file.txtA file in the root directory of the drive 0A file in the current directory of the current drive
2:file.txtA file in the root directory of the drive 2A file in the current directory of the drive 2
../file.txtInvalid nameA file in the parent directory
.Invalid nameThis directory
..Invalid nameParent directory of the current directory
..Invalid nameParent directory of the current directory (*)
dir1/..Invalid nameThe current directory
/..Invalid nameThe root directory (sticks the top level)
-

When option _STR_VOLUME_ID is specified, also pre-defined strings can be used as drive identifier in the path name instead of a numeral. e.g. "sd:file1.txt" or "ram:swapfile.dat".

+

When option _STR_VOLUME_ID is specified, also pre-defined strings can be used as drive identifier in the path name instead of a numeral. e.g. "sd:file1.txt", "ram:swapfile.dat" and DOS/Windows style drive letter, of course.

+

Remark: In this revision, R0.12, double dot name ".." cannot follow the parent directory on the exFAT volume. It will work as "." and stay there.

@@ -47,7 +48,7 @@

Unicode API

-

The path names are input/output in either ANSI/OEM code or Unicode depends on the configuration options. The type of arguments which specify the path names are defined as TCHAR. It is an alias of char by default. The code set used to the path name string is ANSI/OEM specifid by _CODE_PAGE. When _LFN_UNICODE is set to 1, the type of the TCHAR is switched to WCHAR to support Unicode (UTF-16 encoding). In this case, the LFN feature is fully supported and the Unicode specific characters, such as ✝☪✡☸☭, can also be used for the path name. It also affects data types and encoding of the string I/O functions. To define literal strings, _T(s) and _TEXT(s) macro are available to select either ANSI/OEM or Unicode automatically. The code shown below is an example to define the literal strings.

+

The path names are input/output in either ANSI/OEM code or Unicode depends on the configuration options. The type of arguments which specify the path names are defined as TCHAR. It is an alias of char by default. The code set used to the path name string is ANSI/OEM specifid by _CODE_PAGE. When _LFN_UNICODE is set to 1, the type of the TCHAR is switched to WCHAR to support Unicode (UTF-16 encoding). In this case, the full-featured LFN specification is supported and the Unicode specific characters, such as ✝☪✡☸☭, can also be used for the path name. It also affects data types and encoding of the string I/O functions. To define literal strings, _T(s) and _TEXT(s) macro are available to select either ANSI/OEM or Unicode automatically. The code shown below is an example to define the literal strings.

  f_open(fp, "filename.txt", FA_READ);      /* ANSI/OEM string */
  f_open(fp, L"filename.txt", FA_READ);     /* Unicode string */
@@ -57,25 +58,25 @@
 
 

Volume Management

-

The FatFs module needs dynamic work area called file system object for each volume (logical drive). It is registered to the FatFs module by f_mount function. By default, each logical drive is bound to the physical drive with the same drive number and an FAT volume on the drive is serched by auto detect feature. It loads boot sectors and checks it if it is an FAT boot sector in order of sector 0 as SFD format, 1st partition, 2nd partition, 3rd partition and 4th partition as FDISK format.

+

FatFs module needs dynamic work area, file system object, for each volume (logical drive). It is registered/unregistered to the FatFs module by f_mount function. By default, each logical drive is bound to the physical drive with the same drive number and an FAT volume on the drive is serched by the volume mount process. It reads boot sectors and checks it if it is an FAT boot sector in order of sector 0 as SFD format, 1st partition, 2nd partition, 3rd partition and 4th partition as FDISK format.

When _MULTI_PARTITION == 1 is specified by configuration option, each individual logical drive is bound to the partition on the physical drive specified by volume management table. The volume management table must be defined by user to resolve the relationship between logical drives and partitions. Following code is an example of a volume management table.

-Example: Logical drive 0-2 are tied to three pri-partitions on the physical drive 0 (fixed disk)
-         Logical drive 3 is tied to an FAT volume on the physical drive 1 (removable disk)
+Example: Logical drive 0-2 are tied to three pri-partitions on the physical drive 0 (fixed drive)
+         Logical drive 3 is tied to an FAT volume on the physical drive 1 (removable drive)
 
 PARTITION VolToPart[] = {
-    {0, 1},     /* Logical drive 0 ==> Physical drive 0, 1st partition */
-    {0, 2},     /* Logical drive 1 ==> Physical drive 0, 2nd partition */
-    {0, 3},     /* Logical drive 2 ==> Physical drive 0, 3rd partition */
-    {1, 0}      /* Logical drive 3 ==> Physical drive 1 (auto detection) */
+    {0, 1},     /* "0:" ==> Physical drive 0, 1st partition */
+    {0, 2},     /* "1:" ==> Physical drive 0, 2nd partition */
+    {0, 3},     /* "2:" ==> Physical drive 0, 3rd partition */
+    {1, 0}      /* "3:" ==> Physical drive 1, auto detection */
 };
 
-
relationship between logical drive and physical drive
+
relationship between logical drive and physical drive

There are some considerations on using multi-partition configuration.

  • The physical drive that has two or more mounted partitions must be non-removable. Media change while a system operation is prohibited.
  • Only four primary partitions can be specified. Extended partition is not supported.
  • -
  • Windows does not support multiple volumes on the removable storage. Only first parition will be mounted.
  • +
  • Windows does not support multiple volumes on the removable storage. Only first parition will be recognized.
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/findfirst.html b/firmware/chibios-portapack/ext/fatfs/doc/en/findfirst.html index 66130386..5b4b1fbc 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/findfirst.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/findfirst.html @@ -34,7 +34,7 @@ FRESULT f_findfirst (
path
Pointer to the null-terminated string that specifies the directory name to be opened.
pattern
-
Pointer to the null-terminated string that specifies the name matching pattern to be searched for. It is referred by also subsequent f_findnext function, so that the string must be valid while the successive function calls.
+
Pointer to the nul-terminated string that specifies the name matching pattern to be searched for. It is referred by also subsequent f_findnext function, so that the string must be valid while the successive function calls.
@@ -61,11 +61,11 @@ FRESULT f_findfirst (

Description

-

After the directory specified by path could be opened, it starts to search the directory for the item with a name specified by pattern. If found, the information about the object is stored into the file information structure. For more information about file information structure, refer to f_readdir function.

-

The matching pattern can contain wildcard characters (? and *). A ? matches an any character and an * matches an any string in length of zero or longer. At LFN configuration, both names of the item, SFN and LFN (if exist), are tested. In this revision, there are some differences listed below between FatFs and standard systems in matching condition.

+

After the directory specified by path could be opened, it starts to search the directory for the items with a name specified by pattern. If found, the information about the object is stored into the file information structure. For more information about file information structure, refer to f_readdir function.

+

The matching pattern can contain wildcard characters (? and *). A ? matches an any character and an * matches an any string in length of zero or longer. When support of long file name is enabled, only fname[] is tested at _USE_FIND == 1 and also altname[] is tested at _USE_FIND == 2. In this revision, there are some differences listed below between FatFs and standard systems in matching condition.

    -
  • "*.*" never matches any name without extension. (It matches all names at the standard systems)
  • -
  • Any pattern terminated with a period never matches any name. (It matches the names without extensiton at the standard systems)
  • +
  • "*.*" never matches any name without extension while it matches any names at the standard systems.
  • +
  • Any patterns terminated with a period never matches any name while it matches any names without extensiton at the standard systems.
  • DBCS extended characters are compared in case-sensitive at LFN with non-Unicode configuration.
@@ -73,7 +73,7 @@ FRESULT f_findfirst (

QuickInfo

-

This is a wrapper function of f_opendir and f_readdir function. Available when _USE_FIND == 1 and _FS_MINIMIZE <= 1.

+

This is a wrapper function of f_opendir and f_readdir function. Available when _USE_FIND >= 1 and _FS_MINIMIZE <= 1.

@@ -87,20 +87,11 @@ void find_image (void) FRESULT fr; /* Return value */ DIR dj; /* Directory search object */ FILINFO fno; /* File information */ -#if _USE_LFN - char lfn[_MAX_LFN + 1]; - fno.lfname = lfn; - fno.lfsize = _MAX_LFN + 1; -#endif - fr = f_findfirst(&dj, &fno, "", "dsc*.jpg"); /* Start to search for JPEG files with the name started by "dsc" */ + fr = f_findfirst(&dj, &fno, "", "dsc*.jpg"); /* Start to search for photo files */ while (fr == FR_OK && fno.fname[0]) { /* Repeat while an item is found */ -#if _USE_LFN - printf("%-12s %s\n", fno.fname, fno.lfname);/* Display the item name */ -#else - printf("%s\n", fno.fname); -#endif + printf("%s\n", fno.fname); /* Display the object name */ fr = f_findnext(&dj, &fno); /* Search for next item */ } f_closedir(&dj); diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/forward.html b/firmware/chibios-portapack/ext/fatfs/doc/en/forward.html index 7581140c..eddb051f 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/forward.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/forward.html @@ -59,7 +59,7 @@ FRESULT f_forward (

QuickInfo

-

Available when _USE_FORWARD == 1 and _FS_TINY == 1.

+

Available when _USE_FORWARD == 1.

@@ -112,7 +112,7 @@ FRESULT play_file ( if (rc) return rc; /* Repeat until the file pointer reaches end of the file */ - while (rc == FR_OK && !f_eof(&fil)) { + while (rc == FR_OK && !f_eof(&fil)) { /* any other processes... */ diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/getcwd.html b/firmware/chibios-portapack/ext/fatfs/doc/en/getcwd.html index cee958a6..c30aa650 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/getcwd.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/getcwd.html @@ -51,6 +51,7 @@ FRESULT f_getcwd (

Description

The f_getcwd function retrieves full path name of the current directory of the current drive. When _VOLUMES is larger than 1, a logical drive number is added to top of the path name.

+

Note: In this revision, R0.12, this function cannot retrieve the current directory path on the exFAT volume. It always returns the root directory path.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/getlabel.html b/firmware/chibios-portapack/ext/fatfs/doc/en/getlabel.html index 736792fd..cec65aa6 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/getlabel.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/getlabel.html @@ -29,7 +29,7 @@ FRESULT f_getlabel (
path
Pointer to the null-terminated string that specifies the logical drive. Null-string specifies the default drive.
label
-
Pointer to the buffer to store the volume label. The buffer size must be at least 12 items. If the volume has no label, a null-string will be returned. Set null pointer if this information is not needed.
+
Pointer to the buffer to store the volume label. The buffer size must be at least 24 items at _LFN_UNICODE == 0 or 12 items at _LFN_UNICODE == 1. If the volume has no label, a null-string will be returned. Set null pointer if this information is not needed.
vsn
Pointer to the DWORD variable to store the volume serial number. Set null pointer if this information is not needed.
@@ -60,7 +60,7 @@ FRESULT f_getlabel (

Example

-    char str[12];
+    char str[24];
 
     /* Get volume label of the default drive */
     f_getlabel("", str, 0);
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/lseek.html b/firmware/chibios-portapack/ext/fatfs/doc/en/lseek.html
index e684a380..cf4f47e5 100644
--- a/firmware/chibios-portapack/ext/fatfs/doc/en/lseek.html
+++ b/firmware/chibios-portapack/ext/fatfs/doc/en/lseek.html
@@ -17,8 +17,8 @@
 
 
 FRESULT f_lseek (
-  FIL* fp,   /* [IN] File object */
-  DWORD ofs  /* [IN] File read/write pointer */
+  FIL*    fp,  /* [IN] File object */
+  FSIZE_t ofs  /* [IN] File read/write pointer */
 );
 
@@ -29,7 +29,7 @@ FRESULT f_lseek (
fp
Pointer to the open file object.
ofs
-
Byte offset from top of the file.
+
Byte offset from top of the file. The data type FSIZE_t is an alias of either DWORD(32-bit) or QWORD(64-bit) depends on the configuration option _FS_EXFAT.
@@ -48,19 +48,19 @@ FRESULT f_lseek (

Description

-

The f_lseek function moves the file read/write pointer of an open file. The offset can be specified in only origin from top of the file. When an offset beyond the file size is specified at write mode, the file size is expanded to the specified offset. The file data in the expanded area is undefined because no data is written to the file. This is suitable to pre-allocate a cluster chain quickly, for fast write operation. After the f_lseek function succeeded, the current read/write pointer should be checked in order to make sure the read/write pointer has been moved correctry. In case of the current read/write pointer is not the expected value, either of followings has been occured.

+

The f_lseek function moves the file read/write pointer of an open file. The offset can be specified in only origin from top of the file. When an offset beyond the file size is specified at write mode, the file size is expanded to the specified offset. The file data in the expanded area is undefined because no data is written to the file in this process. This is suitable to pre-allocate a cluster chain quickly, for fast write operation. When a contiguous data area needs to be allocated to the file, use f_expand function instead. After the f_lseek function succeeded, the current read/write pointer should be checked in order to make sure the read/write pointer has been moved correctry. In case of the read/write pointer is not the expected value, either of followings has been occured.

-

The fast seek feature enables fast backward/long seek operations without FAT access by using an on-memory CLMT (cluster link map table). It is applied to f_read and f_write function as well, however, the file size cannot be expanded by f_write, f_lseek function while the file is in fast seek mode.

-

The fast seek feature is enabled when the member cltbl in the file object is not NULL. The CLMT must be created into the DWORD array prior to use the fast seek feature. To create the CLMT, set address of the DWORD array to the member cltbl in the open file object, set the size of array in unit of items to the first item and call the f_lseek function with ofs = CREATE_LINKMAP. After the function succeeded and CLMT is created, no FAT access is occured in subsequent f_read, f_write, f_lseek function to the file. The number of items used or required is returned into the first item of the array. The number of items to be used is (number of the file fragments + 1) * 2. For example, when the file is fragmented in 5, 12 items in the array will be used. If the function failed with FR_NOT_ENOUGH_CORE, the given array size is insufficient for the file.

+

The fast seek function enables fast backward/long seek operations without FAT access by using an on-memory CLMT (cluster link map table). It is applied to f_read and f_write function as well, however, the file size cannot be expanded by f_write, f_lseek function while the file is in fast seek mode.

+

The fast seek function is enabled when the member cltbl in the file object is not NULL. The CLMT must be created into the DWORD array prior to use the fast seek function. To create the CLMT, set address of the DWORD array to the member cltbl in the open file object, set the size of array in unit of items to the first item and call the f_lseek function with ofs = CREATE_LINKMAP. After the function succeeded and CLMT is created, no FAT access is occured in subsequent f_read, f_write, f_lseek function to the file. The number of items used or required is returned into the first item of the array. The number of items to be used is (number of the file fragments + 1) * 2. For example, when the file is fragmented in 5, 12 items in the array will be used. If the function failed with FR_NOT_ENOUGH_CORE, the given array size is insufficient for the file.

QuickInfo

-

Available when _FS_MINIMIZE <= 2. To use fast seek feature, _USE_FASTSEEK needs to be set 1.

+

Available when _FS_MINIMIZE <= 2. To use fast seek function, _USE_FASTSEEK needs to be set 1.

@@ -102,7 +102,7 @@ FRESULT f_lseek ( res = f_close(fp);
-/* Using fast seek feature */
+/* Using fast seek function */
 
     DWORD clmt[SZ_TBL];                    /* Cluster link map table buffer */
 
@@ -110,7 +110,7 @@ FRESULT f_lseek (
 
     res = f_lseek(fp, ofs1);               /* This is normal seek (cltbl is nulled on file open) */
 
-    fp->cltbl = clmt;                      /* Enable fast seek feature (cltbl != NULL) */
+    fp->cltbl = clmt;                      /* Enable fast seek function (cltbl != NULL) */
     clmt[0] = SZ_TBL;                      /* Set table size */
     res = f_lseek(fp, CREATE_LINKMAP);     /* Create CLMT */
     ...
@@ -122,7 +122,7 @@ FRESULT f_lseek (
 
 
 
 

Return

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/mkfs.html b/firmware/chibios-portapack/ext/fatfs/doc/en/mkfs.html index 1637a0e2..52f5c3cb 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/mkfs.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/mkfs.html @@ -13,12 +13,14 @@

f_mkfs

-

The f_mkfs fucntion creates an FAT volume on the logical drive.

+

The f_mkfs fucntion creates an FAT/exFAT volume on the logical drive.

 FRESULT f_mkfs (
   const TCHAR* path,  /* [IN] Logical drive number */
-  BYTE  sfd,          /* [IN] Partitioning rule */
-  UINT  au            /* [IN] Size of the allocation unit */
+  BYTE  opt,          /* [IN] Format options */
+  DWORD au,           /* [IN] Size of the allocation unit */
+  void* work,         /* [-]  Working buffer */
+  UINT len            /* [IN] Size of working buffer */
 );
 
@@ -27,11 +29,15 @@ FRESULT f_mkfs (

Parameters

path
-
Pointer to the null-terminated string that specifies the logical drive to be formatted. If there is no drive number, it means the default drive.
-
sfd
-
Specifies partitioning rule (FDISK(0) or SFD(1)). This argument will be ignored on some case.
+
Pointer to the null-terminated string specifies the logical drive to be formatted. If there is no drive number in it, it means the default drive. The logical drive does not need to be mounted.
+
opt
+
Specifies the format option in combination of FM_FAT, FM_FAT32, FM_EXFAT and bitwise-or of these three, FM_ANY. FM_EXFAT is ignored when exFAT is not enabled. These flags specify which FAT type to be created on the volume. If two or more types are specified, one out of them will be selected depends on the volume size. The flag FM_SFD specifies to place the volume on the drive in SFD format.
au
-
Specifies size of the allocation unit (cluter) in number of bytes or sectors. When the value is from 1 to 128, it specifies number of sectors. When the value is >= _MIN_SS, it specifies number of bytes. If any invalid value, zero or not power of 2, is given, the cluster size is automatically determined depends on the volume size.
+
Specifies size of the allocation unit (cluter) in unit of byte. The valid value is N times the sector size. N is power of 2 from 1 to 128 for FAT volume and upto 16MiB for exFAT volume. If zero is given, the default allocation unit size is selected depends on the volume size.
+
work
+
Pointer to the working buffer for the format process.
+
len
+
Size of the working buffer in unit of byte. It needs to be the sector size at least. Plenty of working buffer reduces number of write transaction to the device and the format process will be finished quickly.
@@ -43,7 +49,6 @@ FRESULT f_mkfs ( FR_NOT_READY, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, -FR_NOT_ENABLED, FR_MKFS_ABORTED, FR_INVALID_PARAMETER

@@ -51,11 +56,11 @@ FRESULT f_mkfs (

Description

-

The f_mkfs function creates an FAT volume on the specified logical drive. When FDISK format is specified, a primary partition occupies entire space of the physical drive is created and then an FAT volume is created on the partition. When SFD format is specified, the FAT volume starts from the first sector of the physical drive.

-

If the logical drive is bound to the specific partition (1-4) by multiple partition feature (_MULTI_PARTITION), the FAT volume is created into the partition. In this case, the second argument sfd is ignored. The physical drive must have been partitioned with f_fdisk function or any other partitioning tools prior to create the FAT volume with this function.

-

Note that there are two partitioning rules, FDISK and SFD. The FDISK partitioning is usually used for harddisk, MMC, SDC, CFC and U Disk. It can divide a physical drive into one or more partitions with a partition table on the MBR. However Windows does not support multiple partition on the removable disk. The SFD is non-partitioned method. The FAT volume starts from the first sector on the physical drive without partition table. It is usually used for floppy disk, Microdrive, optical disk and any type of super-floppy media.

-

The FAT sub-type, FAT12/FAT16/FAT32, is determined by number of clusters on the volume and nothing else, according to the FAT specification issued by Microsoft. Thus which FAT sub-type is selected, is depends on the volume size and the specified cluster size. The cluster size affects read/write throughput and space usage efficiency. Larger cluster size increases the read/write throughput and decreases the space usage efficiency of the volume.

-

In case of the number of clusters gets near the FAT sub-type boundaries, the function can fail with FR_MKFS_ABORTED.

+

The FAT sub-type, FAT12/FAT16/FAT32, of FAT volume except exFAT is determined by only number of clusters on the volume and nothing else, according to the FAT specification issued by Microsoft. Thus which FAT sub-type is selected, is depends on the volume size and the specified cluster size. In case of the combination of FAT type and cluter size specified by argument cannot be valid on the volume, the function will fail with FR_MKFS_ABORTED.

+

The allocation unit, also called 'cluster', is a unit of disk space allocation for files. When the size of allocation unit is 32768 bytes, a file with 100 bytes in size occupies 32768 bytes of disk space. The space efficiency of disk usage gets worse as increasing size of allocation unit, but, on the other hand, the read/write performance increases as the size of allocation unit. Therefore the allocation unit is a trade-off between space efficiency and performance. For the large storages in GB order, 32768 bytes or larger cluster (this is automatically selected by default) is recommended for most case unless extremely many files are created on a volume.

+

There are two disk formats, FDISK and SFD. The FDISK format is usually used for harddisk, MMC, SDC, CFC and U Disk. It can divide a physical drive into one or more partitions with a partition table on the MBR (maser boot record, the first sector of the physical drive). The SFD (super-floppy disk) is non-partitioned disk format. The FAT volume starts at the first sector of the physical drive without any disk partitioning. It is usually used for floppy disk, Microdrive, optical disk and most type of super-floppy media. Some systems support only either one of two formats and other is not supported.

+

When FM_SFD is not specified, a primary partition occupies whole drive space is created and then the FAT volume is created in it. When FM_SFD is specified, the FAT volume occupies from the first sector of the drive is created.

+

If the logical drive to be formatted is bound to the specific partition (1-4) by support of multiple partition, _MULTI_PARTITION, the FAT volume is created into the partition and FM_SFD flag is ignored. The physical drive needs to be partitioned with f_fdisk function or any other partitioning tools prior to create the FAT volume with this function.

@@ -69,19 +74,20 @@ FRESULT f_mkfs ( /* Format default drive and create a file */ int main (void) { - FATFS fs; /* File system object (volume work area) */ - FIL fil; /* File object */ - FRESULT res; /* API result code */ - UINT bw; /* Bytes written */ + FATFS fs; /* File system object */ + FIL fil; /* File object */ + FRESULT res; /* API result code */ + UINT bw; /* Bytes written */ + BYTE work[_MAX_SS]; /* Work area (larger is better for process time) */ - /* Register work area (do not care about error) */ - f_mount(&fs, "", 0); - - /* Create FAT volume with default cluster size */ - res = f_mkfs("", 0, 0); + /* Create FAT volume */ + res = f_mkfs("", FM_ANY, 0, work, sizeof work); if (res) ... + /* Register work area */ + f_mount(&fs, "", 0); + /* Create a file as new */ res = f_open(&fil, "hello.txt", FA_CREATE_NEW | FA_WRITE); if (res) ... @@ -95,12 +101,14 @@ int main (void) /* Unregister work area */ f_mount(0, "", 0); + + ...

See Also

-

Volume management, f_fdisk

+

Example of volume size and format parameters, Volume management, f_fdisk

Return

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/mount.html b/firmware/chibios-portapack/ext/fatfs/doc/en/mount.html index 5ab9a1f2..f7176327 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/mount.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/mount.html @@ -31,7 +31,7 @@ FRESULT f_mount (
path
Pointer to the null-terminated string that specifies the logical drive. The string without drive number means the default drive.
opt
-
Initialization option. 0: Do not mount now (to be mounted later), 1: Force mounted the volume to check if the FAT volume is ready to work.
+
Initialization option. 0: Do not mount now (to be mounted later), 1: Force mounted the volume to check if the volume is ready to work.
@@ -49,15 +49,15 @@ FRESULT f_mount (

Description

-

The f_mount function registers/unregisters a file system object used for the logical drive to the FatFs module as follows:

+

The f_mount function registers/unregisters a file system object used for the volume (logical drive) to the FatFs module as follows:

  1. Determines the logical drive which specified by path.
  2. -
  3. Clears and unregisters the regsitered work area of the drive.
  4. +
  5. Clears and unregisters the regsitered work area of the drive if exist.
  6. Clears and registers the new work area to the drive if fs is not NULL.
  7. Performs volume mount process to the drive if forced mount is specified.

The file system object is the work area needed for each logical drive. It must be given to the logical drive with this function prior to use any other file functions except for f_fdisk function to the logical drive. To unregister the work area, specify a NULL to the fs, and then the work area can be discarded.

-

If forced mounting is not specified, this function always succeeds regardless of the physical drive status due to delayed mount feature. It only clears (de-initializes) the given work area and registers its address to the internal table. No activity of the physical drive in this function. It can also be used to force de-initialized the registered work area of a logical drive. The volume mount processes, initialize the corresponding physical drive, find the FAT volume in it and initialize the work area, is performed in the subsequent file access functions when either or both of following condition is true.

+

If forced mounting is not specified, this function always succeeds regardless of the physical drive status due to delayed mount feature. It only clears (de-initializes) the given work area and registers its address to the internal table. There is no activity of the physical drive in this function. It can also be used to force de-initialized the registered work area of a logical drive. The volume mount processes, initialize the corresponding physical drive, find the FAT volume in it and initialize the work area, is performed in the subsequent file access functions when either or both of following condition is true.

@@ -53,7 +53,7 @@ FRESULT f_read (

Description

-

The file read/write pointer of the file object advances number of bytes read. After the function succeeded, *br should be checked to detect end of the file. In case of *br is less than btr, it means the read/write pointer reached end of the file during read operation.

+

The function starts to read data from the file at the position pointed by the read/write pointer. The read/write pointer advances as number of bytes read. After the function succeeded, *br should be checked to detect end of the file. In case of *br is less than btr, it means the read/write pointer reached end of the file during read operation.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/readdir.html b/firmware/chibios-portapack/ext/fatfs/doc/en/readdir.html index 2cb12107..235beee7 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/readdir.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/readdir.html @@ -13,7 +13,7 @@

f_readdir

-

The f_readdir function reads directory entries.

+

The f_readdir function reads an item of the directory.

 FRESULT f_readdir (
   DIR* dp,      /* [IN] Directory object */
@@ -26,9 +26,9 @@ FRESULT f_readdir (
 

Parameters

dp
-
Pointer to the directory object created by f_opendir function.
+
Pointer to the open directory object or null pointer.
fno
-
Pointer to the file information structure to store the information about read item.
+
Pointer to the file information structure to store the information about read item.
@@ -48,13 +48,14 @@ FRESULT f_readdir (

Description

-

The f_readdir function reads directory items, informations about file and directory, in sequence. All items in the directory can be read by calling f_readdir function repeatedly. When relative path feature is enabled (_FS_RPATH >= 1), dot entries ("." and "..") are not filtered out and they will appear in the read items. When all directory items have been read and no item to read, a null string is stored into the fno->fname[] without any error. When a null pointer is given to the fno, the read index of the directory object is rewinded.

-

When LFN feature is enabled, fno->lfname and fno->lfsize in the file information structure must be initialized with valid value prior to use it. The lfname points the LFN read buffer. The lfsize is size of the LFN read buffer in unit of TCHAR. If the LFN is not needed, set a null pointer to the lfname and the LFN is not returned. A null string will be returned into the LFN read buffer in case of following conditions.

+

The f_readdir function reads a directory item, informations about the object. All items in the directory can be read in sequence by f_readdir function calls. Dot entries ("." and "..") in the sub-directory are filtered out and they will never appear in the read items. When all directory items have been read and no item to read, a nul string is stored into the fno->fname[] without any error. When a null pointer is given to the fno, the read index of the directory object is rewinded.

+

When support of long file name (LFN) is enabled, a member altname[] is defined in the file information structure to store the short file name of the object. In case of the some conditions listed below, short file name is stored into the fname[] and altname[] has a null string.

+

There is a problem on reading a directory of exFAT volume. The exFAT does not support short file name. This means no name can be returned on the condition above. If it is the case, a "?" is returned as file name to indicate that the object is not accessible. To avoid this problem, configure FatFs _LFN_UNICODE = 1 and _MAX_LFN = 255 to support the full feature of LFN specification.

@@ -68,40 +69,28 @@ FRESULT f_readdir (

Sample Code

 FRESULT scan_files (
-    char* path        /* Start node to be scanned (also used as work area) */
+    char* path        /* Start node to be scanned (***also used as work area***) */
 )
 {
     FRESULT res;
-    FILINFO fno;
     DIR dir;
-    int i;
-    char *fn;   /* This function assumes non-Unicode configuration */
-#if _USE_LFN
-    static char lfn[_MAX_LFN + 1];   /* Buffer to store the LFN */
-    fno.lfname = lfn;
-    fno.lfsize = sizeof lfn;
-#endif
+    UINT i;
+    static FILINFO fno;
 
 
     res = f_opendir(&dir, path);                       /* Open the directory */
     if (res == FR_OK) {
-        i = strlen(path);
         for (;;) {
             res = f_readdir(&dir, &fno);                   /* Read a directory item */
             if (res != FR_OK || fno.fname[0] == 0) break;  /* Break on error or end of dir */
-            if (fno.fname[0] == '.') continue;             /* Ignore dot entry */
-#if _USE_LFN
-            fn = *fno.lfname ? fno.lfname : fno.fname;
-#else
-            fn = fno.fname;
-#endif
             if (fno.fattrib & AM_DIR) {                    /* It is a directory */
-                sprintf(&path[i], "/%s", fn);
-                res = scan_files(path);
-                path[i] = 0;
+                i = strlen(path);
+                sprintf(&path[i], "/%s", fno.fname);
+                res = scan_files(path);                    /* Enter the directory */
                 if (res != FR_OK) break;
+                path[i] = 0;
             } else {                                       /* It is a file. */
-                printf("%s/%s\n", path, fn);
+                printf("%s/%s\n", path, fno.fname);
             }
         }
         f_closedir(&dir)
@@ -109,6 +98,23 @@ FRESULT scan_files (
 
     return res;
 }
+
+
+int main (void)
+{
+    FATFS fs;
+    FRESULT res;
+    char buff[256];
+
+
+    res = f_mount(&fs, "", 1);
+    if (res == FR_OK) {
+        strcpy(buff, "/");
+        res = scan_files(buff);
+    }
+
+    return res;
+}
 
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/sdir.html b/firmware/chibios-portapack/ext/fatfs/doc/en/sdir.html index 27b205f5..a01a7166 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/sdir.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/sdir.html @@ -16,20 +16,15 @@

The DIR structure is used for the work area to read a directory by f_oepndir, f_readdir, f_findfirst and f_findnext function. Application program must not modify any member in this structure, or any data on the FAT volume can be collapsed.

 typedef struct {
-    FATFS*  fs;         /* Pointer to the owner file system object */
-    WORD    id;         /* Owner file system mount ID */
-    WORD    index;      /* Index of directory entry to start to search next */
-    DWORD   sclust;     /* Table start cluster (0:Root directory) */
+    _FDID   obj;        /* Owner file sytem object and object identifier */
+    DWORD   dptr;       /* Current read/write offset */
     DWORD   clust;      /* Current cluster */
     DWORD   sect;       /* Current sector */
     BYTE*   dir;        /* Pointer to the current SFN entry in the win[] */
     BYTE*   fn;         /* Pointer to the SFN buffer (in/out) {file[8],ext[3],status[1]} */
-#if _FS_LOCK
-    UINT    lockid;     /* Sub-directory lock ID (0:Root directory) */
-#endif
 #if _USE_LFN
-    WCHAR*  lfn;        /* Pointer to the LFN buffer (in/out) */
-    WORD    lfn_idx;    /* Index of the LFN entris (0xFFFF:No LFN) */
+    DWORD   blk_ofs;    /* Offset of the entry block (0xFFFFFFFF:Invalid) */
+    WCHAR*  lfn;        /* Pointer to the LFN working buffer (in/out) */
 #endif
 #if _USE_FIND
     const TCHAR*  pat;  /* Ponter to the matching pattern */
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/setlabel.html b/firmware/chibios-portapack/ext/fatfs/doc/en/setlabel.html
index e76a285d..f7a8b0eb 100644
--- a/firmware/chibios-portapack/ext/fatfs/doc/en/setlabel.html
+++ b/firmware/chibios-portapack/ext/fatfs/doc/en/setlabel.html
@@ -49,13 +49,14 @@ FRESULT f_setlabel (
 
 

Description

-

When the string has a drive number, the volume label will be set to the volume specified by the drive number. If not, the volume label will be set to the default drive. If length of the given volume label is zero, the volume label on the volume will be removed. The format of the volume label is similar to the short file name but there are some differences shown below:

+

When the string has a drive number, the volume label will be set to the volume specified by the drive number. If not, the volume label will be set to the default drive. If length of the given volume label is zero, the volume label on the volume will be removed. The format of the volume label on the FAT volume is similar to the file name but there are some differences shown below:

    -
  • 11 bytes or less in length as conversion into OEM code page. LFN feature is not applied to the volume label.
  • -
  • Cannot contain period.
  • -
  • Can contain spaces anywhere in it. Trailing spaces are truncated off.
  • +
  • Spaces can be contained anywhere in the volume label. Trailing spaces are truncated off at FAT volume.
  • +
  • Period is not allowed.
  • +
  • Up to 11 bytes long as conversion of OEM code page at FAT volume.
  • +
  • Up to 11 characters long at exFAT volume. Case information is preserved.
-

Remark: The standard system (Windows) has a problem on handling of the volume label with a heading \xE5. To avoid this problem, this function rejects such volume labels as invalid name.

+

Remark: The standard system (Windows) has a problem at handling of the volume label with a heading \xE5 on the FAT volume. To avoid this problem, this function rejects such volume label as invalid name.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/sfatfs.html b/firmware/chibios-portapack/ext/fatfs/doc/en/sfatfs.html index b4c9b71f..e701f441 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/sfatfs.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/sfatfs.html @@ -16,17 +16,20 @@

The FATFS structure (file system object) holds dynamic work area of individual logical drives. It is given by application program and registerd/unregisterd to the FatFs module with f_mount function. Initialization is done on first API call after f_mount function or media change. Application program must not modify any member in this structure, or any data on the FAT volume can be collapsed.

 typedef struct {
-    BYTE    fs_type;      /* FAT sub-type (0:Not mounted, FS_FAT12/FS_FAT16/FS_FAT32) */
+    BYTE    fs_type;      /* File system type (0, FS_FAT12, FS_FAT16, FS_FAT32 or FS_EXFAT) */
     BYTE    drv;          /* Physical drive number */
-    BYTE    csize;        /* Sectors per cluster (1,2,4,...,128) */
     BYTE    n_fats;       /* Number of FAT copies (1,2) */
     BYTE    wflag;        /* win[] flag (b0:win[] is dirty) */
     BYTE    fsi_flag;     /* FSINFO flags (b7:Disabled, b0:Dirty) */
     WORD    id;           /* File system mount ID */
     WORD    n_rootdir;    /* Number of root directory entries (FAT12/16) */
+    WORD    csize;        /* Sectors per cluster */
 #if _MAX_SS != _MIN_SS
     WORD    ssize;        /* Sector size (512,1024,2048 or 4096) */
 #endif
+#if _FS_EXFAT
+    BYTE*   dirbuf;       /* Directory entry block scratchpad buffer */
+#endif
 #if _FS_REENTRANT
     _SYNC_t sobj;         /* Identifier of sync object */
 #endif
@@ -36,6 +39,11 @@
 #endif
 #if _FS_RPATH
     DWORD   cdir;         /* Cluster number of current directory (0:root) */
+#if _FS_EXFAT
+    DWORD   cdc_scl;      /* Containing directory start cluster (invalid when cdir is 0) */
+    DWORD   cdc_size;     /* b31-b8:Size of containing directory, b7-b0: Chain status */
+    DWORD   cdc_ofs;      /* Offset in the containing directory (invalid when cdir is 0) */
+#endif
 #endif
     DWORD   n_fatent;     /* Number of FAT entries (Number of clusters + 2) */
     DWORD   fsize;        /* Sectors per FAT */
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/sfile.html b/firmware/chibios-portapack/ext/fatfs/doc/en/sfile.html
index ee62071f..5e53d167 100644
--- a/firmware/chibios-portapack/ext/fatfs/doc/en/sfile.html
+++ b/firmware/chibios-portapack/ext/fatfs/doc/en/sfile.html
@@ -17,15 +17,12 @@
 
 
 typedef struct {
-    FATFS*  fs;           /* Pointer to the owner file system object */
-    WORD    id;           /* Owner file system mount ID */
+    _FDID   obj;          /* Owner file sytem object and object identifier */
     BYTE    flag;         /* File object status flags */
     BYTE    err;          /* Abort flag (error code) */
-    DWORD   fptr;         /* File read/write pointer (Byte offset origin from top of the file) */
-    DWORD   fsize;        /* File size in unit of byte */
-    DWORD   sclust;       /* File start cluster */
+    FSIZE_t fptr;         /* File read/write pointer (Byte offset origin from top of the file) */
     DWORD   clust;        /* Current cluster of fptr (One cluster behind if fptr is on the cluster boundary. Invalid if fptr == 0.) */
-    DWORD   dsect;        /* Current data sector (Can be invalid if fptr is on the cluster boundary.)*/
+    DWORD   sect;         /* Current data sector (Can be invalid if fptr is on the cluster boundary.)*/
 #if !_FS_READONLY
     DWORD   dir_sect;     /* Sector number containing the directory entry */
     BYTE*   dir_ptr;      /* Ponter to the directory entry in the window */
@@ -33,9 +30,6 @@
 #if _USE_FASTSEEK
     DWORD*  cltbl;        /* Pointer to the cluster link map table (Nulled on file open. Set by application.) */
 #endif
-#if _FS_LOCK
-    UINT    lockid;       /* Fle lock ID */
-#endif
 #if !_FS_TINY
     BYTE    buf[_MAX_SS]; /* File private data transfer buffer (Always valid if fptr is not on the sector boundary but can be invalid if fptr is on the sector boundary.) */
 #endif
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/sfileinfo.html b/firmware/chibios-portapack/ext/fatfs/doc/en/sfileinfo.html
index 70e20e4e..268b8c14 100644
--- a/firmware/chibios-portapack/ext/fatfs/doc/en/sfileinfo.html
+++ b/firmware/chibios-portapack/ext/fatfs/doc/en/sfileinfo.html
@@ -16,14 +16,15 @@
 

The FILINFO structure holds information about the object returned by f_readdir, f_findfirst, f_findnext and f_stat function.

 typedef struct {
-    DWORD fsize;      /* File size */
-    WORD  fdate;      /* Last modified date */
-    WORD  ftime;      /* Last modified time */
-    BYTE  fattrib;    /* Attribute */
-    TCHAR fname[13];  /* Short file name (8.3 format) */
-#if _USE_LFN
-    TCHAR* lfname;    /* Pointer to the LFN buffer */
-    int   lfsize;     /* Size of the LFN buffer in unit of TCHAR */
+    FSIZE_t fsize;               /* File size */
+    WORD    fdate;               /* Last modified date */
+    WORD    ftime;               /* Last modified time */
+    BYTE    fattrib;             /* Attribute */
+#if _USE_LFN != 0
+    TCHAR   altname[13];         /* Alternative object name */
+    TCHAR   fname[_MAX_LFN + 1]; /* Primary object name */
+#else
+    TCHAR   fname[13];           /* Object name */
 #endif
 } FILINFO;
 
@@ -32,9 +33,9 @@

Members

fsize
-
Indicates size of the file in unit of byte. Always zero for directories.
+
Indicates size of the file in unit of byte. Always zero for directories. FSIZE_t is an alias of integer type either DWORD(32-bit) or QWORD(64-bit) depends on the configuration option _FS_EXFAT.
fdate
-
Indicates the date that the file was modified or the directory was created.
+
Indicates the date when the file was modified or the directory was created.
bit15:9
Year origin from 1980 (0..127)
@@ -45,7 +46,7 @@
ftime
-
Indicates the time that the file was modified or the directory was created.
+
Indicates the time when the file was modified or the directory was created.
bit15:11
Hour (0..23)
@@ -58,11 +59,9 @@
fattrib
Indicates the file/directory attribute in combination of AM_DIR, AM_RDO, AM_HID, AM_SYS and AM_ARC.
fname[]
-
The object name in 8.3 format (SFN) null-terminated string is stored. It is always upper case at non-LFN configuration but it can be returned with lower case at LFN configuration. A null string is stored when no item to read and this structure is invalid.
-
lfname
-
Pointer to the string buffer to store the long file name (LFN). This member must be initialized by application program prior to use this structure. Set a null pointer if LFN is not needed. Not available at non-LFN configuration.
-
lfsize
-
Size of the LFN buffer in unit of TCHAR. This member must be initialized by application program prior to use this structure. Not available at non-LFN configuration.
+
The null-terminated object name is stored. A null string is stored when no item to read and it indicates this structure is invalid.
+
altname[]
+
Alternative object name is stored if available. This member is not available at non-LFN configuration.

Return

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/size.html b/firmware/chibios-portapack/ext/fatfs/doc/en/size.html index a25a32cc..08990c61 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/size.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/size.html @@ -15,7 +15,7 @@

f_size

The f_size function gets the size of a file.

-DWORD f_size (
+FSIZE_t f_size (
   FIL* fp   /* [IN] File object */
 );
 
@@ -39,9 +39,9 @@ DWORD f_size (

Description

-

In this revision, the f_size function is implemented as a macro. It has not any validation and mutual exclusion.

+

In this revision, the f_size function is implemented as a macro. It does not have any validation and mutual exclusion.

-#define f_size(fp) ((fp)->fsize)
+#define f_size(fp) ((fp)->obj.objsize)
 
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/stat.html b/firmware/chibios-portapack/ext/fatfs/doc/en/stat.html index 5651c956..2924660e 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/stat.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/stat.html @@ -54,8 +54,7 @@ FRESULT f_stat (

Description

-

The f_stat function checks the existence of a file or sub-directory. If not exist, the function returns with FR_NO_FILE. If exist, the function returns with FR_OK and the informations of the object, file size, timestamp, attribute and SFN, are stored to the file information structure. For details of the file information, refer to the FILINFO structure and f_readdir function.

-

When LFN feature is enabled, lfname in the file information structure must be NULLed prior to use it.

+

The f_stat function checks the existence of a file or sub-directory. If not exist, the function returns with FR_NO_FILE. If exist, the function returns with FR_OK and the informations of the object, file size, timestamp and attribute, are stored to the file information structure. For details of the file information, refer to the FILINFO structure and f_readdir function.

@@ -74,23 +73,20 @@ FRESULT f_stat ( printf("Test for 'file.txt'...\n"); -#if _USE_LFN - fno.lfname = 0; -#endif fr = f_stat("file.txt", &fno); switch (fr) { case FR_OK: - printf("Size: %u\n", fno.fsize); + printf("Size: %lu\n", fno.fsize); printf("Timestamp: %u/%02u/%02u, %02u:%02u\n", (fno.fdate >> 9) + 1980, fno.fdate >> 5 & 15, fno.fdate & 31, fno.ftime >> 11, fno.ftime >> 5 & 63); printf("Attributes: %c%c%c%c%c\n", - (fno.fattrib & AM_DIR) ? 'D' : '-', - (fno.fattrib & AM_RDO) ? 'R' : '-', - (fno.fattrib & AM_HID) ? 'H' : '-', - (fno.fattrib & AM_SYS) ? 'S' : '-', - (fno.fattrib & AM_ARC) ? 'A' : '-'); + (fno.fattrib & AM_DIR) ? 'D' : '-', + (fno.fattrib & AM_RDO) ? 'R' : '-', + (fno.fattrib & AM_HID) ? 'H' : '-', + (fno.fattrib & AM_SYS) ? 'S' : '-', + (fno.fattrib & AM_ARC) ? 'A' : '-'); break; case FR_NO_FILE: diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/tell.html b/firmware/chibios-portapack/ext/fatfs/doc/en/tell.html index 49a2c578..7e0673cf 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/tell.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/tell.html @@ -15,7 +15,7 @@

f_tell

The f_tell function gets the current read/write pointer of a file.

-DWORD f_tell (
+FSIZE_t f_tell (
   FIL* fp   /* [IN] File object */
 );
 
@@ -39,7 +39,7 @@ DWORD f_tell (

Description

-

In this revision, the f_tell function is implemented as a macro. It has not any validation and mutual exclusion.

+

In this revision, the f_tell function is implemented as a macro. It does not have any validation and mutual exclusion.

 #define f_tell(fp) ((fp)->fptr)
 
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/truncate.html b/firmware/chibios-portapack/ext/fatfs/doc/en/truncate.html index 9be17b12..d0079d70 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/truncate.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/truncate.html @@ -36,6 +36,7 @@ FRESULT f_truncate ( FR_OK, FR_DISK_ERR, FR_INT_ERR, +FR_DENIED, FR_INVALID_OBJECT, FR_TIMEOUT

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/unlink.html b/firmware/chibios-portapack/ext/fatfs/doc/en/unlink.html index 34db349a..4cd1f660 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/unlink.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/unlink.html @@ -59,7 +59,7 @@ FRESULT f_unlink ( If condition of the object to be removed is applicable to the following terms, the function will be rejected.
  • The file/sub-directory must not have read-only attribute (AM_RDO), or the function will be rejected with FR_DENIED.
  • The sub-directory must be empty and must not be current directory, or the function will be rejected with FR_DENIED.
  • -
  • The file/sub-directory must not be opened, or the FAT volume can be collapsed. It will be rejected safely when file lock feature is enabled.
  • +
  • The file/sub-directory must not be opened, or the FAT volume can be collapsed. It will be rejected safely when file lock function is enabled.
diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/utime.html b/firmware/chibios-portapack/ext/fatfs/doc/en/utime.html index ac646e38..6b7f291e 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/utime.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/utime.html @@ -85,7 +85,7 @@ FRESULT set_timestamp (

QuickInfo

-

Available when _FS_READONLY == 0 and _FS_MINIMIZE == 0.

+

Available when _FS_READONLY == 0 and _USE_CHMOD == 1.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/en/write.html b/firmware/chibios-portapack/ext/fatfs/doc/en/write.html index 5bcd5a79..644dd6df 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/en/write.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/en/write.html @@ -34,7 +34,7 @@ FRESULT f_write (
btw
Specifies number of bytes to write in range of UINT type.
bw
-
Pointer to the UINT variable to return the number of bytes written. The value is always valid after the function call regardless of the result.
+
Pointer to the UINT variable to return the number of bytes written. The value is always valid after the function call regardless of the result code.
@@ -53,7 +53,7 @@ FRESULT f_write (

Description

-

The read/write pointer of the file object advances number of bytes written. After the function succeeded, *bw should be checked to detect the disk full. In case of *bw is less than btw, it means the volume got full during the write operation. The function can take a time when the volume is full or close to full.

+

The function starts to write data to the file at the position pointed by the read/write pointer. The read/write pointer advances as number of bytes written. After the function succeeded, *bw should be checked to detect the disk full. In case of *bw is less than btw, it means the volume got full during the write operation. The function can take a time when the volume is full or close to full.

diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/f7.png b/firmware/chibios-portapack/ext/fatfs/doc/img/f7.png deleted file mode 100644 index 392e8b8924b078c69424bdbc1ba6a8aa5f173a9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11388 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 a-WJ7rO^FjEQ%jKgC3MW@B%;FVbo@V^{mfnf diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/modules.png b/firmware/chibios-portapack/ext/fatfs/doc/img/modules.png deleted file mode 100644 index ef4c78cb304aabcf2b96b8c227c7b357026d0254..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7932 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 DAZV+8 diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/appnote.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/appnote.html index fdc69a9f..b716ebd3 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/appnote.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/appnote.html @@ -17,6 +17,7 @@
  • メモリ使用量
  • モジュール サイズの縮小
  • 長いファイル名
  • +
  • exFATファイルシステム
  • Unicode入出力への対応
  • リエントランシー
  • 多重ファイル アクセス
  • @@ -41,9 +42,9 @@ FatFsモジュールはANSI C(C89)準拠で記述されているので、普通

    システム構成

    下に示す依存関係図は、FatFsモジュール利用の組み込みシステムにおける代表的な構成を示します。

    -

    システム構成図

    +

    システム構成図

    (a) FatFs用に書かれたディスク モジュールがある場合は、そのまま追加するだけです。 (b) しかし、多くの既存のディスク モジュールはそのAPIをFatFsに合わせるため、グルー関数が必要になるでしょう。

    -

    functional diagram

    +

    functional diagram

    ユーザの作成する関数

    ポーティング作業は、要求されるデバイス制御関数を用意することが全てで、それ以外にすることは何もありません。既に動作しているデバイス制御モジュールがあるなら、そのAPIをFatFsに合わせるかグルー関数を介してつなぐだけで済みますが、無い場合はほかから移植するか最初から書くかする必要があります。定義されている全ての関数が常に必要なわけではありません。例えば、リード オンリ構成では書き込み系関数は必要ありません。次の表に構成オプションと要求される関数の対応を示します。

    @@ -63,13 +64,13 @@ FatFsモジュールはANSI C(C89)準拠で記述されているので、普通

    限界値

      -
    • FATタイプ: FAT12, FAT16, FAT32。
    • +
    • ファイルシステム: FAT12, FAT16, FAT32(r0.0) および exFAT(r1.0)。
    • 同時オープン ファイル数: 無制限。(利用可能メモリによる)
    • 同時マウント ボリューム数: 最大 10。
    • -
    • ファイル サイズ: 最大 4G-1バイト。
    • -
    • ボリューム サイズ: 最大 2Tバイト(512バイト/セクタ時)。
    • -
    • クラスタ サイズ: 最大 64Kバイト(512バイト/セクタ時)。
    • -
    • セクタ サイズ: 512, 1024, 2048, 4096バイト。
    • +
    • ファイル サイズ: 最大 4GiB - 1 (FATボリューム) および、事実上無制限(exFATボリューム)。
    • +
    • ボリューム サイズ: 最大 2TiB (512B/セクタ時)。
    • +
    • クラスタ サイズ: 最大 128セクタ (FATボリューム) および、最大 16MiB (exFATボリューム)。
    • +
    • セクタ サイズ: 512B, 1KiB, 2KiB, 4KiB。
    @@ -78,70 +79,72 @@ FatFsモジュールはANSI C(C89)準拠で記述されているので、普通

    次の表にいくつかのターゲットにおけるメモリ使用量の例を示します。テスト時の構成オプションはその下の通りです。数値の単位はバイトで、Vはボリューム数、Fは同時オープン ファイル数を示します。コンパイラの最適化オプションはコード サイズとしています。

    - - - - - - - - - - + + + + + + + + +
    ARM7
    32bit
    ARM7
    Thumb
    CM3
    Thumb-2
    AVRH8/300HPIC24RL78V850ESSH-2ARX600IA-32
    CompilerGCCGCCGCCGCCCH38C30CC78K0RCA850SHCRXCVC6
    _WORD_ACCESS00010001011
    text (Full, R/W)10.6k7.1k6.5k13.3k10.9k11.7k13.3k8.1k9.0k6.0k7.9k
    text (Min, R/W) 6.7k4.6k4.2k 8.6k 7.3k 7.7k 9.1k5.3k5.8k3.9k5.2k
    text (Full, R/O) 4.8k3.2k2.9k 6.2k 5.2k 5.5k 6.5k3.8k4.0k2.9k3.7k
    text (Min, R/O) 3.6k2.5k2.3k 4.6k 4.1k 4.3k 5.0k3.0k3.1k2.2k2.9k
    bssV*4 + 2V*4 + 2V*4 + 2V*2 + 2V*4 + 2V*2 + 2V*2 + 2V*4 + 2V*4 + 2V*4 + 2V*4 + 2
    Work area
    (_FS_TINY == 0)
    V*560
    + F*550
    V*560
    + F*550
    V*560
    + F*550
    V*560
    + F*544
    V*560
    + F*550
    V*560
    + F*544
    V*560
    + F*544
    V*560
    + F*544
    V*560
    + F*550
    V*560
    + F*550
    V*560
    + F*550
    Work area
    (_FS_TINY == 1)
    V*560
    + F*36
    V*560
    + F*36
    V*560
    + F*36
    V*560
    + F*32
    V*560
    + F*36
    V*560
    + F*32
    V*560
    + F*32
    V*560
    + F*36
    V*560
    + F*36
    V*560
    + F*36
    V*560
    + F*36
    CompilerGCCGCCGCCGCCCH38C30CC78K0RCA850SHCRXCMSC
    text (Full, R/W)10.1k6.6k6.2k12.1k10.5k11.2k12.6k8.5k8.7k6.3k8.4k
    text (Min, R/W) 6.6k4.5k4.2k 7.9k 7.0k 7.6k 8.8k5.9k5.8k4.3k5.8k
    text (Full, R/O) 4.8k3.1k2.9k 5.8k 5.1k 5.5k 6.4k4.1k4.0k3.1k4.0k
    text (Min, R/O) 3.5k2.4k2.3k 4.4k 3.9k 4.2k 5.0k3.3k3.1k2.4k3.1k
    bssV*4 + 2V*4 + 2V*4 + 2V*2 + 2V*4 + 2V*2 + 2V*2 + 2V*4 + 2V*4 + 2V*4 + 2V*4 + 2
    Work area
    (_FS_TINY == 0)
    V*564
    + F*552
    V*564
    + F*552
    V*564
    + F*552
    V*560
    + F*546
    V*560
    + F*546
    V*560
    + F*546
    V*560
    + F*546
    V*564
    + F*552
    V*564
    + F*552
    V*564
    + F*552
    V*564
    + F*552
    Work area
    (_FS_TINY == 1)
    V*564
    + F*40
    V*564
    + F*40
    V*564
    + F*40
    V*560
    + F*34
    V*560
    + F*34
    V*560
    + F*34
    V*560
    + F*34
    V*564
    + F*40
    V*564
    + F*40
    V*564
    + F*40
    V*564
    + F*40
    -FatFs R0.11 options:
    +FatFs R0.12 options:
     _FS_READONLY   0 (R/W) or 1 (R/O)
    -_FS_MINIMIZE   0 (Full basic function) or 3 (Minimized function)
    -_VOLUMES       V (Number of logical drives to be used)
    -_WORD_ACCESS   0 or 1 (Set by processor architecture)
    -(Any other options are left not changed from default setting)
    +_FS_MINIMIZE   0 (Full, with all basic functions) or 3 (Min, with fully minimized)
    +_FS_TINY       0 (Default) or 1 (Tiny file object)
    +And any other options are left not changed from default setting.
     

    モジュール サイズの縮小

    -

    次の表は構成オプションの設定値によりどの機能が削除されるかを示します。使用するAPI関数の行にxが無ければその関数は使用可能です。

    +

    次の表は構成オプションの設定値によりどの機能が削除されるかを示します。API関数の行にxが無ければその関数は使用可能です。

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Function_FS_MINIMIZE_FS_READONLY_USE_STRFUNC_FS_RPATH_USE_LABEL_USE_MKFS_USE_FORWARD_MULTI_PARTITION
    0123010  1/20120101010/12
    f_mount
    f_open
    f_close
    f_read
    f_writex
    f_syncx
    f_lseekx
    f_opendirxx
    f_closedirxx
    f_readdirxx
    f_statxxx
    f_getfreexxxx
    f_truncatexxxx
    f_unlinkxxxx
    f_mkdirxxxx
    f_chmodxxxx
    f_utimexxxx
    f_renamexxxx
    f_chdirx
    f_chdrivex
    f_getcwdxx
    f_getlabelx
    f_setlabelxx
    f_forwardx
    f_mkfsxx
    f_fdiskxxx
    f_putcxx
    f_putsxx
    f_printfxx
    f_getsx
    Function_FS_
    MINIMIZE
    _FS_
    READONLY
    _USE_
    STRFUNC
    _FS_
    RPATH
    _USE_
    FIND
    _USE_
    CHMOD
    _USE_
    EXPAND
    _USE_
    LABEL
    _USE_
    MKFS
    _USE_
    FORWARD
    _MULTI_
    PARTITION
    0123010101201010101010101
    f_mount
    f_open
    f_close
    f_read
    f_write x
    f_sync x
    f_lseek x
    f_opendir xx
    f_closedir xx
    f_readdir xx
    f_findfirst xx x
    f_findnext xx x
    f_stat xxx
    f_getfree xxx x
    f_truncate xxx x
    f_unlink xxx x
    f_mkdir xxx x
    f_rename xxx x
    f_chdir x
    f_chdrive x
    f_getcwd xx
    f_chmod x x
    f_utime x x
    f_getlabel x
    f_setlabel x x
    f_expand x x
    f_forward x
    f_mkfs x x
    f_fdisk x x x
    f_putc xx
    f_puts xx
    f_printf xx
    f_gets x

    長いファイル名

    -

    FatFsモジュールは、長いファイル名(LFN)をサポートします。ファイルに付けられた2つの異なる名前(短いファル名と長いファイル名)は、f_readdir関数を除くファイル操作関数において透過です。デフォルト構成では、LFN機能はOFFになっています。LFN機能を有効にするには、_USE_LFNを1,2または3に設定し、option/unicode.cをプロジェクトに追加します。LFN機能は、加えてある程度のワーク エリア(LFN操作バッファ)を必要とします。バッファ長は使用できるメモリに応じて_MAX_LFNオプションで構成されることができます。LFNの長さは最大255文字に達するので、LFN完全対応のためには_MAX_LFNは255に設定されるべきです。与えられたファイル名に対してバッファ長が不足した場合、ファイル関数はFR_INVALID_NAMEで失敗します。

    -

    ファイル関数に再入を行う条件の下でLFN機能を使用する場合は、_USE_LFNは2または3に設定されなければなりません。この場合、ファイル関数はワーク エリアを動的に確保(スタックまたはヒープ)します。ワーク エリアのサイズは、(_MAX_LFN + 1) * 2バイトになるので、スタック等のサイズはそれを考慮した十分な余裕がなければなりません。

    +

    FatFsモジュールは、長いファイル名(LFN)をサポートします。ファイルに付けられた2つの異なる名前(短いファル名と長いファイル名)は、f_readdir関数を除くファイル操作関数において透過です。デフォルト構成では、LFN機能はOFFになっています。LFN機能を有効にするには、_USE_LFNを1,2または3に設定し、option/unicode.cをプロジェクトに追加します。LFN機能は、加えてある程度のワーク エリア(LFN操作バッファ)を必要とします。バッファ長は使用できるメモリに応じて_MAX_LFNで構成されることができます。LFNの長さは最大255文字に達するので、LFN完全対応のためには_MAX_LFNは255に設定されるべきです。与えられたファイル名に対してバッファ長が不足した場合、ファイル関数はFR_INVALID_NAMEで失敗します。

    +

    ファイル関数に再入を行う条件の下でLFN機能を使用する場合は、_USE_LFNは2または3に設定されなければなりません。この場合、ファイル関数はワーク エリアを動的に確保(スタックまたはヒープ)します。ワーク エリアのサイズは、(_MAX_LFN + 1) * 2バイト(exFAT利用時はさらに+608バイト)になるので、スタック等のサイズはそれを考慮した十分な余裕がなければなりません。

    @@ -152,21 +155,29 @@ _WORD_ACCESS 0 or 1 (Set by processor architecture)
    LFN構成 at CM3
    _CODE_PAGE追加コード
    950(Big5)+111k

    LFN機能の上手な使い方は、それを使わないということです。実際、組み込み用途ではLFN機能がどうしても必要になるということはほとんど無いはずです。LFNを有効にすると、選択されたコード ページに応じてモジュール サイズが増大します。右の表に各コード ページにおけるLFNを有効にしたときのモジュール サイズの違いを示します。特に、CJK地域では数万の文字が使われていますが、不幸なことにそれは巨大なOEM-Unicode相互変換テーブルを要求し、モジュール サイズは劇的に増大します。その結果、それらのコード ページにおいてLFNを有効にしたFatFsモジュールは、多くの8ビット マイコンにインプリメントすることができません。

    -

    LFN機能のハードルはそれだけではありません。マイクロソフト社はFATファイル システムについていくつかの特許を保有しています。いずれもLFN機能の実装に関するもので、その利用に対して$0.25/unitのライセンス料を要求しています。このため、商用製品でLFN機能を利用するときは、最終仕向地によってはライセンスが必要になります。最近のFAT32ドライバの多くはLFN機能を含んでいるため、それらの使用に当たってライセンスが必要になりますが、FatFsではLFN機能を構成オプションで任意にON/OFFできるため、無効にしてライセンス問題を回避することもできます。

    +

    LFN機能のハードルはそれだけではありません。マイクロソフト社はFATファイルシステムについていくつかの特許を保有しています。いずれもLFN機能の実装に関するもので、その利用に対して$0.25/unitのライセンス料を要求しています。このため、商用製品でLFN機能を利用する場合は、製品の最終仕向地によってはライセンスが必要になります。最近のFAT32ドライバの多くはLFN機能を含んでいるため、それらの使用に当たってライセンスが必要になりますが、FatFsは構成オプションでLFN機能を任意にON/OFFできるため、無効にしてライセンス問題を回避することもできます。

    Unicode入出力への対応

    -

    FatFs API上におけるファイル名等の文字列データの入出力は、デフォルトではANSI/OEMコードで行われますが、これをUnicode(UTF-16)に切り替えることもできます(_LFN_UNICODEオプションで設定)。つまり、これはFatFsがLFN機能に完全対応していることを意味します。Unicodeのファイル名に関する詳細は、パス名のフォーマットを参照してください。

    +

    FatFs API上におけるファイル名等の文字列データの入出力は、デフォルトではANSI/OEMコードで行われますが、これをUnicode(UTF-16)に切り替えることもできます(_LFN_UNICODEで設定)。つまり、これはFatFsがLFN機能に完全対応していることを意味します。Unicodeのファイル名に関する詳細は、パス名のフォーマットを参照してください。

    +
    + +
    +

    exFATファイルシステム

    +

    exFAT(Microsoft's Extended File Allocation Table)ファイルシステムは、既に組み込みシステムや情報家電で広く使われているFATファイルシステムを置き換える目的で開発されました。exFATは、64GiB以上のSDメモリ カードで標準ファイルシステムに採用されるなど、FATに並びリムーバブル メディアの標準ファイルシステムの一つとなりつつあります。

    +

    exFATボリュームでは、FATボリュームで制約となっていた4GiB以上のサイズのファイルを扱え、ファイルシステムのオーバーヘッド(特にファイル アロケーション ディレイ)も大幅に低減され、書き込みスループットがFATより向上しています。しかし、現リビジョンのFatFsでは、実装上の理由から不連続ファイルへのサイズ拡大を伴う書き込み時のとき、スループットがFATより低下します。f_expand関数による連続領域の割り当て機能は、この問題の回避に有効かもしれません。

    +

    exFATはマイクロソフト社が開発したものなので、マイクロソフト社はexFATについていくつかの特許を保有しています。FatFsのexFAT機能は、それの US. Pat. App. Pub. No. 2009/0164440 A1 に基づいた実装です。このため、商用製品でexFAT機能を利用する場合、製品の最終仕向地によってはライセンスが必要になります。最近のFATドライバの多くはexFAT機能を含んでいるため、それらの使用に当たってライセンスが必要になりますが、FatFsは構成オプションでexFAT機能を任意にON/OFFできるため、無効にしてライセンス問題を回避することもできます。

    +

    exFATを有効にすると、FatFsモジュールのC89互換は失われます(64ビット整数型が必要なため)。

    リエントランシー

    -

    互いに異なるボリュームに対するファイル操作は構成にかかわらずリエントラントで、常に同時平行に動作できます。

    -

    同じボリュームに対してはデフォルトではリエントラントではありませんが、_FS_REENTRANTオプションでリエントラント(スレッド セーフ)にすることはできます。この場合、OS依存の同期オブジェクト操作関数ff_cre_syncobj, ff_del_syncobj, ff_req_grant, ff_rel_grant関数もまたプロジェクトに追加されなければなりません。サンプル コードと解説はoption/syncobj.cにあります。

    +

    互いに異なるボリュームに対するファイル操作は、_USE_LFN == 1を除いて構成にかかわらずリエントラントで、常に同時平行に動作できます。

    +

    同じボリュームに対してはデフォルトではリエントラントではありませんが、_FS_REENTRANTでリエントラント(スレッド セーフ)にすることはできます。この場合、OS依存の同期オブジェクト操作関数ff_cre_syncobj, ff_del_syncobj, ff_req_grant, ff_rel_grant関数もまたプロジェクトに追加されなければなりません。サンプル コードと解説はoption/syncobj.cにあります。

    この場合、あるタスクがボリュームを使用中に他のタスクからそのボリュームに対するファイル関数が呼び出されると、そのアクセスは先のタスクがファイル関数を抜けるまでサスペンドされます。待ち時間が_TIMEOUTで指定された期間を越えた場合、その関数はFR_TIMEOUTでアボートします。いくつかのRTOSではタイムアウト機能はサポートされないかも知れません。

    ひとつの例外がf_mount, f_mkfs, f_fdisk関数にあります。これらのボリューム制御関数は同じボリューム(または関連する物理ドライブ)に対してリエントラントではありません。これらの関数を使用するときは、アプリケーション レベルで排他制御しなければなりません。

    -

    注: このセクションはFatFsモジュールそれ自体のリエントランシーについて説明しています。_FS_REENTRANTは、各ファイル システム オブジェクトの排他制御を行うのみで、下位のディスク関数への再入を防止するものではありません。たとえば、シングル ボリューム構成ではdisk_status関数のみ再入される可能性があり、マルチ ボリューム構成ではどのディスク関数も再入される可能性があります。このように、複数のタスクから同時にFatFs APIを使う条件では、ディスクI/Oモジュールはスレッド セーフである必要があります。

    +

    注: このセクションはFatFsモジュールそれ自体のリエントランシーについて説明しています。_FS_REENTRANTは、各ファイルシステム オブジェクトの排他制御を行うのみで、下位のディスク関数への再入を防止するものではありません。たとえば、シングル ボリューム構成ではdisk_status関数のみ再入される可能性があり、マルチ ボリューム構成ではどのディスク関数も再入される可能性があります。このように、複数のタスクから同時にFatFs APIを使う条件では、ディスクI/Oモジュールはスレッド セーフである必要があります。

    @@ -179,15 +190,15 @@ _WORD_ACCESS 0 or 1 (Set by processor architecture)

    効率的なファイル アクセス

    小規模な組込システムでのファイルの読み書きにおける効率の良いアクセスのため、アプリケーション プログラマはFatFsモジュールの中でどのような処理が行われているか考慮すべきです。ストレージ上のデータはf_read関数により次のシーケンスで転送されます。

    図1. セクタ ミスアラインド リード (ショート)
    -fig.1 +fig.1

    図2. セクタ ミスアラインド リード (ロング)
    -fig.2 +fig.2

    図3. セクタ アラインド リード
    -fig.3 +fig.3

    -

    ファイルI/Oバッファはセクタの一部のデータを読み書きするためのセクタ バッファを意味します。セクタ バッファは、それぞれのファイル オブジェクト内のプライベート セクタ バッファまたはファイル システム オブジェクト内の共有セクタ バッファのどちらかです。バッファ構成オプションの_FS_TINYは、データ転送にどちらを使うかを決定します。タイニー バッファ(1)が選択されるとデータ メモリの消費はそれぞれのファイル オブジェクトで_MAX_SSバイト減少されます。この場合、FatFsモジュールはファイル データの転送とFAT/ディレクトリ アクセスにファイル システム オブジェクト内のセクタ バッファだけを使用します。タイニー バッファの欠点は、セクタ バッファにキャッシュされたFATデータがファイル データの転送により失われ、クラスタ境界の毎にリロードされなければならないことです。でも、悪くない性能と少ないメモリ消費の視点から多くのアプリケーションに適するでしょう。

    +

    ファイルI/Oバッファはセクタの一部のデータを読み書きするためのセクタ バッファを意味します。セクタ バッファは、それぞれのファイル オブジェクト内のプライベート セクタ バッファまたはファイルシステム オブジェクト内の共有セクタ バッファのどちらかです。バッファ構成オプションの_FS_TINYは、データ転送にどちらを使うかを決定します。タイニー バッファ(1)が選択されるとデータ メモリの消費はそれぞれのファイル オブジェクトで_MAX_SSバイト減少されます。この場合、FatFsモジュールはファイル データの転送とFAT/ディレクトリ アクセスにファイルシステム オブジェクト内のセクタ バッファだけを使用します。タイニー バッファの欠点は、セクタ バッファにキャッシュされたFATデータがファイル データの転送により失われ、クラスタ境界の毎にリロードされなければならないことです。でも、悪くない性能と少ないメモリ消費の視点から多くのアプリケーションに適するでしょう。

    図1はセクタの一部のデータがファイルI/Oバッファを経由で転送されることを示します。図2に示される長いデータの転送では、転送データの中間の1セクタまたはそれ以上のセクタにまたがる転送データがアプリケーション バッファに直接転送されています。図3は転送データ全体がセクタ境界にアライメントされている場合を示しています。この場合、ファイルI/Oバッファは使用されません。直接転送においては最大の範囲のセクタがdisk_read関数で一度に読み込まれますが、クラスタ境界を越えるマルチ セクタ転送はそれが隣接であっても行われません。

    このように、セクタにアライメントしたファイルの読み書きへの配慮はバッファ経由のデータ転送を避け、読み書き性能は改善されるでしょう。その効果に加え、タイニー構成でキャッシュされたFATデータがファイル データの転送によりフラッシュされず、非タイニー構成と同じ性能を小さなメモリ フットプリントで達成できます。

    @@ -198,12 +209,12 @@ _WORD_ACCESS 0 or 1 (Set by processor architecture)

    マルチ セクタ書き込み

    図6. マルチ/シングル セクタ ライトの比較
    -fig.6 +fig.6
    -

    フラッシュ メモリ メディアの書き込み速度はシングル セクタ書き込みの時に最も低いものになり、一回のトランザクションで転送されるセクタ数が大きくなるほど書き込み速度は向上します(図6)。この効果はバス速度が高速になるほど大きく、10倍以上の差が現れることも珍しくありません。テスト結果は、マルチ セクタ書き込み(W:16K, 32 sectors)がシングル セクタ書き込み(W:100, 1 sector)よりどの程度速いかを明確に示しています。大容量メディアほどシングル セクタ書き込みが遅くなる点もまた重要です。書き込みトランザクションの回数はまた、メディアの寿命にも影響してきます。つまり、同じ量のデータを書き込む場合、図6上のシングル セクタ書き込みは、図6下のマルチ セクタ書き込みに比べて16倍早くフラッシュ メモリ メディアを消耗させてしまうということです。

    +

    フラッシュ メモリ メディアの書き込み速度はシングル セクタ書き込みの時に最も低いものになり、一回のトランザクションで転送されるセクタ数が大きくなるほど書き込み速度は向上します(図6)。この効果はバス速度が高速になるほど大きく、10倍以上の差が現れることも珍しくありません。テスト結果は、マルチ セクタ書き込み(W:16K, 32 sectors)がシングル セクタ書き込み(W:100, 1 sector)よりどの程度速いかを明確に示しています。大容量メディアほどシングル セクタ書き込みが遅くなる点もまた重要です。書き込みトランザクションの回数はまた、メディアの寿命にも影響してきます。つまり、同じ量のデータを書き込む場合、図6上のシングル セクタ書き込みは、図6下のマルチ セクタ書き込みに比べて16倍早くフラッシュ メモリ メディアを消耗させてしまうということです。

    このように、アプリケーションはなるべく大きなブロック(クラスタ サイズまたは2の累乗セクタ境界にアライメントした)で読み書きを行う必要があります。もちろん、アプリケーションからメディアに至る全てのレイヤがマルチ セクタ転送に対応していないと意味がありません。残念ながら、既存のオープン ソースのドライバの多くはマルチ セクタ転送に未対応です。なお、FatFsモジュールおよびサンプル ドライバはマルチ セクタ転送に対応しています。

    明示的なメモリ消去

    -

    通常のファイル消去では、記録されたデータに対して何らかの制御が行われるわけではなく、単にFAT上に未使用クラスタとして記録されているだけです。このため、ファイルが消去されたあともそれらは有効なメモリ ブロックとしてフラッシュ メモリ上に残ります。そこで、ファイルを消去するとき、占有していたデータ セクタを明示的に消去(つまり未使用ブロックにする)することにより、メディア内の空きブロックを増やすことができます。これにより、次にそのブロックに書き込むときの消去動作が無くなり、書き込み性能が向上する可能性があります。また、ウェアレベリングに使えるブロックが増え、メディアの耐久性も向上するかも知れません。この機能を有効にするには、構成オプションの_USE_TRIMに1を設定します。これはフラッシュ メモリ メディアの内部動作に期待した制御なので、効果があるとは限りません。また、ファイル消去の時間が延びることも考慮に入れるべきです。

    +

    通常のファイル消去では、記録されたデータに対して何らかの処理が行われるわけではなく、単にFAT上にその領域を未使用と記録しているだけです。このため、ファイルが消去されたあともそれらは有効なデータ ブロックとしてフラッシュ メモリ上に残ります。そこで、ファイルを消去するとき、占有していたデータ セクタを明示的に消去(つまり未使用ブロックにする)することにより、メディア内の空きブロックを増やすことができます。これにより、次にそのブロックに書き込むときの消去動作が無くなり、書き込み性能が向上する可能性があります。また、ウェアレベリングに使えるブロックが増え、メディアの耐久性も向上するかも知れません。この機能を有効にするには、_USE_TRIMに1を設定します。これはフラッシュ メモリ ドライブの内部動作に期待した制御なので、効果があるとは限りません。また、ファイル消去の時間が延びることも考慮に入れるべきです。

    @@ -211,11 +222,11 @@ _WORD_ACCESS 0 or 1 (Set by processor architecture)

    ストレージ上のFAT構造を操作している途中で、停電、不正なメディアの取り外し、回復不能なデータ エラー等の障害が発生すると、処理が中途半端な状態で中断され、その結果としてFATボリュームの構造が破壊される可能性があります。次にFatFsモジュールにおけるクリチカル セクションと、その間の障害により起きうるエラーの状態を示します。

    図4. 長いクリチカル セクション
    -fig.4 +fig.4
    図5. 最小化したクリチカル セクション
    -fig.5 +fig.5

    赤で示したセクションを実行中に中断が発生した場合、クロス リンクが発生して操作中のファイルやディレクトリが失われる可能性があります。黄色で示したセクションを実行中に中断が発生した場合、次のうちいずれかまたは複数の結果が生じる可能性があります。

    @@ -233,27 +244,30 @@ _WORD_ACCESS 0 or 1 (Set by processor architecture)

    APIの拡張的使用例

    FatFs APIの拡張的使用例です。有用なコードがあった場合は、随時追加していきます。。

      -
    1. 追記モードでのオープン/新規作成
    2. -
    3. ディレクトリを空にする
    4. -
    5. ファイルに連続領域を割り当てる
    6. -
    7. ディスクI/Oモジュールの機能/互換性チェッカー
    8. -
    9. FATイメージ作成ツール
    10. +
    11. 追記モードでのオープン/新規作成(R0.12以前)
    12. +
    13. ディレクトリを空にする
    14. +
    15. ファイルに連続領域を割り当てる(R0.11a以前)
    16. +
    17. ディスクI/Oモジュールの機能/互換性チェッカー
    18. +
    19. FATイメージ作成ツール

    FatFsのライセンスについて

    -

    ソース ファイルにライセンス条件が記述されているので、利用の際はそれに従うこと。原文は英語ですが、参考までに以下に日本語訳を示しておきます。

    +

    FatFsは、作者(ChaN)の個人プロジェクトとして開発されています。現在のリビジョンにおいてコントリビューターはいないため、作者の書いたソース コード以外は含まれません。ソース ファイルにライセンス条件が記述されているので、利用の際はそれに従うこと。原文は英語ですが、参考までに以下に日本語訳を示しておきます。

     /*----------------------------------------------------------------------------/
    -/  FatFs - FAT file system module  R0.11                 (C)ChaN, 2015
    +/  FatFs - Generic FAT file system module  R0.12a                             /
     /-----------------------------------------------------------------------------/
    -/ FatFsモジュールはフリーソフトウェアで、次に示す条件の下に公開されています。
     /
    -/ Copyright (C) 2015, ChaN, all right reserved.
    +/ Copyright (C) 2016, ChaN, all right reserved.
     /
    -/ 1. ソースコードで再配布するときは、その中に上記の著作権表示、この条文、および
    -/    次の免責事項を保持すること
    +/ FatFsモジュールはオープンソースソフトウェアです。FatFsの再配布および使用は、
    +/ ソースコードかバイナリ形式か、また変更の有無にかかわらず、次の条件が満たされ
    +/ る場合に限り許可されます。
    +/
    +/ 1. ソースコードで再配布するときは、その中に上記の著作権表示、この条件、および
    +/    次の免責事項を保持すること。
     / 
     / このソフトウェアは、著作権者らおよびコントリビューターらによって現状のまま
     / 提供されており、いかなる保証もありません。
    @@ -261,7 +275,7 @@ _WORD_ACCESS   0 or 1 (Set by processor architecture)
     / 損害についても、責任を負いません。
     /----------------------------------------------------------------------------*/
     
    -

    このようにFatFsはBSDライクなライセンスとしていますが、一つ大きな違いがあります。FatFsは主に組み込み向けとして開発されたため、バイナリ形式(ソース コードを含まない形式全て)での再配布については、商用での使いやすさを考慮して配布時の条件を設けていません。つまり、バイナリ配布の場合は、FatFsおよびそのライセンス文書についてドキュメントに明記してもしなくてもかまいません。これは、一条項BSDライセンスと等価ということです。もちろん、GNU GPLなどほとんど全てのオープン ソース ライセンスの下のプロジェクトにおいて共存可能です。FatFsに何らかの修正や拡張を加えてソース コードで再配布する場合は、矛盾しない他のオープン ソース ライセンス(GNU GPLや修正BSDライセンスなど)に変更することも可能です。

    +

    このようにFatFsはBSDライクなライセンスとしていますが、一つ大きな違いがあります。FatFsは主に組み込み向けとして開発されたため、バイナリ形式(ソース コードを含まない形式全て)での再配布については、商用での使いやすさを考慮して配布時の条件を設けていません。つまり、バイナリ配布の場合は、FatFsおよびそのライセンス文書についてドキュメントに明記してもしなくてもかまいません。これは、一条項BSDライセンスと等価ということです。もちろん、GNU GPLなどほとんど全てのオープン ソース ライセンスの下のプロジェクトにおいて共存可能です。FatFsからフォークを作成し公開する場合は、矛盾しない他のオープン ソース ライセンス(GNU GPLや修正BSDライセンスなど)に変更することも可能です。

    戻る

    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/config.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/config.html index 2b059d29..f01fb199 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/config.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/config.html @@ -43,19 +43,25 @@ div.doc h4 {margin-top: 2em }

    _USE_FIND

    -

    フィルタ付きディレクトリ読み出し機能の構成(0:無効 または 1:有効)。有効にすると、f_findfirstf_findnext関数が利用可能になります。

    +

    フィルタ付きディレクトリ読み出し機能の構成(0:無効 または 1:有効)。有効にすると、f_findfirstf_findnext関数が利用可能になります。_FS_MINIMIZEは、1以下でなければなりません。

    _USE_MKFS

    ボリューム作成機能の構成(0:無効 または 1:有効)。有効にするとf_mkfs関数が利用可能になります。

    _USE_FASTSEEK

    -

    高速シーク機能の構成(0:無効 または 1:有効)。有効にすると、f_lseekf_readf_write関数の高速化モードが利用可能になります。詳しくは、こちらを参照してください。

    +

    高速シーク機能の構成(0:無効 または 1:有効)。有効にすると、f_lseekf_readf_write関数において高速化モードが利用可能になります。詳しくは、こちらを参照してください。

    + +

    _USE_EXPAND

    +

    連続領域割り当て機能の構成(0:無効 または 1:有効)。有効にするとf_expand関数が利用可能になります。_FS_READONLYは0でなければなりません。

    + +

    _USE_CHMOD

    +

    メタデータ操作機能の構成(0:無効 または 1:有効)。有効にすると、f_chmodf_utime関数が利用可能になります。_FS_READONLYは0でなければなりません。

    _USE_LABEL

    -

    ボリューム ラベル操作機能の構成(0:無効 または 1:有効)。有効にすると、f_getlabelf_setlabel関数が利用可能になります。

    +

    ボリューム ラベル操作機能の構成(0:無効 または 1:有効)。有効にすると、f_getlabelf_setlabel関数(_FS_READONLY == 0のとき)が利用可能になります。

    _USE_FORWARD

    -

    ストリーミング読み出し機能の構成(0:無効 または 1:有効)。これと_FS_TINYを有効にすると、f_forward関数が利用可能になります。

    +

    ストリーミング読み出し機能(f_forward関数)の構成(0:無効 または 1:有効)。

    @@ -92,13 +98,13 @@ div.doc h4 {margin-top: 2em }

    _USE_LFN

    -

    LFN(長いファイル名)対応を設定します。LFN機能を有効にするときは、Unicode操作関数option/unicode.cをプロジェクトに加える必要があります。また、LFN操作バッファ((_MAX_LFN + 1) * 2バイトを占有)を使用します。バッファをスタックに確保するときは、スタック オーバ フローに注意する必要があります。ヒープに確保するときは、メモリ操作関数、ff_memallocff_memfree(option/syscall.cにサンプルあり)、をプロジェクトに加える必要があります。

    +

    LFN(長いファイル名)対応を設定します。LFN機能を有効にするときは、Unicode操作関数option/unicode.cをプロジェクトに加える必要があります。また、LFN操作のワーク エリアとして(_MAX_LFN + 1) * 2バイト(exFAT構成時はさらに608バイト)を使用します。このため、バッファをスタックに確保するときは、スタック オーバ フローに注意する必要があります。ヒープに確保するときは、メモリ操作関数(ff_memallocff_memfree(option/syscall.cにサンプルあり))をプロジェクトに加える必要があります。

    - - - + + +
    解説
    0LFN機能を使わない。8.3形式の名前のみ使用可能。
    1LFN機能を使う。LFN操作バッファは静的に確保。常にスレッド セーフではない。
    2LFN機能を使う。LFN操作バッファはスタックから確保。
    3LFN機能を使う。LFN操作バッファはヒープから確保。
    1LFN機能を使う。ワーク エリアは静的に確保。常にスレッド セーフではない。
    2LFN機能を使う。ワーク エリアはスタックに確保。
    3LFN機能を使う。ワーク エリアはヒープに確保。

    _MAX_LFN

    @@ -169,6 +175,9 @@ div.doc h4 {margin-top: 2em }

    _FS_TINY

    ファイル データ転送バッファの構成(0:ノーマル または 1:タイニ)。タイニ構成では、ファイル オブジェクトFIL内のプライベート セクタ バッファが削除され、_MAX_SSバイト小さくなります。ファイル データの転送には、代わりにファイル システム オブジェクトFATFS内のボリューム共有セクタ バッファが使われます。

    +

    _FS_EXFAT

    +

    exFATのサポート(0:使用しない または 1:使用する)。exFATを使用するには、LFN機能を有効にしなければなりません。また、exFATの完全サポートのためには、_LFN_UNICODE = 1_MAX_LFN = 255の設定が推奨されます。exFAT機能では64ビット整数を使用するため、これを有効にするとC89(ANSI C)互換が失われます。

    +

    _FS_NORTC

    RTC機能の使用の設定(0:使用する または 1:使用しない)。システムがRTC(カレンダ時計)をサポートしない場合は、1をセットします。この場合、FatFsが変更を加えたオブジェクトのタイムスタンプはデフォルトの日時を持ちます。RTCが使用可能なときは、0を設定し、get_fattime関数をプロジェクトに加えます。リード オンリ構成ではこのオプションは意味を持ちません。

    @@ -193,31 +202,6 @@ div.doc h4 {margin-top: 2em }

    _SYNC_t

    O/S定義の同期オブジェクトの型を設定します。例: HANDLEIDOS_EVENT*SemaphoreHandle_tなど。また、O/S機能のヘッダ ファイルをff.cのスコープ内にインクルードする必要があります。_FS_REENTRANTが0のときは意味を持ちません。

    -

    _WORD_ACCESS

    -

    ボリューム上のワード データへのアクセス方法を設定します。唯一のプラットフォーム依存オプションです。

    - - - - -
    解説
    0Byte-by-byteアクセス。全てのプラットフォームでコンパチブル。
    1ワード アクセス。コード サイズが少し減るが、次の条件を共に満たしていない限り選択できない。
    -・非アライン メモリ アクセスが常に全ての命令で使用可能。
    -・メモリ上のバイト順がリトル エンディアン。
    -

    次にいくつかのプロセッサにおける許可された設定を示します。

    -
    -   ARM7TDMI   0   *2          ColdFire   0    *1         V850E      0    *2
    -   Cortex-M3  0   *3          Z80        0/1             V850ES     0/1
    -   Cortex-M0  0   *2          x86        0/1             TLCS-870   0/1
    -   AVR        0/1             RX600(LE)  0/1             TLCS-900   0/1
    -   AVR32      0   *1          RL78       0    *2         R32C       0    *2
    -   PIC18      0/1             SH-2       0    *1         M16C       0/1
    -   PIC24      0   *2          H8S        0    *1         MSP430     0    *2
    -   PIC32      0   *1          H8/300H    0    *1         8051       0/1
    -
    -   *1:ビッグ エンディアン
    -   *2:非アライン メモリ アクセス不可
    -   *3:いくつかのコンパイラがmem_cpy関数にLDM/STM命令を生成する
    -
    -

    戻る

    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/dioctl.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/dioctl.html index f5715a7b..b37ffeaf 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/dioctl.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/dioctl.html @@ -80,6 +80,9 @@ DRESULT disk_ioctl ( ATA_GET_REVリビジョン コードをbuffの示す16バイトのバッファに読み出します。(ATA/CFカード専用) ATA_GET_MODELモデル コードをbuffの示す40バイトのバッファに読み出します。(ATA/CFカード専用) ATA_GET_SNシリアル番号をbuffの示す20バイトのバッファに読み出します。(ATA/CFカード専用) +ISDIO_READbuffの示すコマンド構造体に従いiSDIOレジスタからデータを読み出します。(FlashAir専用) +ISDIO_WRITEbuffの示すコマンド構造体に従いiSDIOレジスタにデータを書き込みます。(FlashAir専用) +ISDIO_MRITEbuffの示すコマンド構造体に従いiSDIOレジスタの一部のビットを書き換えます。(FlashAir専用) diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/dread.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/dread.html index 24991dbf..4c8ddba8 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/dread.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/dread.html @@ -34,7 +34,7 @@ DRESULT disk_read (
    sector
    読み出しを開始するセクタ番号。32ビットLBAで指定されます。
    count
    -
    読み出すセクタ数が1~128の範囲で指定されます。
    +
    読み出すセクタ数(1以上の値)が指定されます。
    @@ -45,11 +45,11 @@ DRESULT disk_read (
    RES_OK (0)
    正常終了。
    RES_ERROR
    -
    読み込み中にエラーが発生し、その回復にも失敗した。
    +
    回復不能なエラーにより、読み出し操作を完了できなかった。
    RES_PARERR
    パラメータが不正。
    RES_NOTRDY
    -
    ストレージ デバイスが動作可能状態ではない(初期化されていない)。
    +
    ストレージ デバイスが動作可能な状態ではない (初期化されていない)。
    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/dwrite.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/dwrite.html index 020d5f1c..fa0de8d1 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/dwrite.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/dwrite.html @@ -30,11 +30,11 @@ DRESULT disk_write (
    pdrv
    対象のデバイスを識別する物理ドライブ番号(0-9)が指定されます。
    buff
    -
    ストレージ デバイスに書き込むセクタ データが格納されたバイト配列が指定されます。バイト数は、セクタ サイズ*countとなります。
    +
    ストレージ デバイスに書き込むセクタ データが格納されたバイト配列が指定されます。データのバイト数は、セクタ サイズ*countとなります。
    sector
    書き込みを開始するセクタ番号。32ビットLBAで指定されます。
    count
    -
    書き込むセクタ数が1~128の範囲で指定されます。
    +
    書き込むセクタ数(1以上の値)が指定されます。
    @@ -45,13 +45,13 @@ DRESULT disk_write (
    RES_OK (0)
    正常終了。
    RES_ERROR
    -
    書き込み中にエラーが発生し、その回復にも失敗した。
    +
    回復不能なエラーにより、書き込み操作を完了できなかった。
    RES_WRPRT
    メディアが書き込み禁止状態。
    RES_PARERR
    パラメータが不正。
    RES_NOTRDY
    -
    ストレージ デバイスが動作可能状態ではない(初期化されていない)。
    +
    ストレージ デバイスが動作可能な状態ではない (初期化されていない)。
    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/expand.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/expand.html new file mode 100644 index 00000000..98667d10 --- /dev/null +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/expand.html @@ -0,0 +1,116 @@ + + + + + + + + +FatFs - f_expand + + + + +
    +

    f_expand

    +

    ファイルに連続したデータ領域を割り当てます。

    + +
    +FRESULT f_expand (
    +  FIL*    fp,  /* [IN] ファイル オブジェクト構造体へのポインタ */
    +  FSIZE_t fsz, /* [IN] 割り当てサイズ */
    +  BYTE    opt  /* [IN] 動作オプション */
    +);
    +
    +
    + +
    +

    Parameters

    +
    +
    fp
    +
    対象となるファイル オブジェクト構造体へのポインタを指定します。
    +
    fsz
    +
    ファイルに割り当てるバイト単位のサイズ。データ型FSIZE_tは、DWORD(32-bit)またはQWORD(64-bit)のエリアスで、exFATサポートの有無により切り替わります。
    +
    opt
    +
    実際に割り当てを行うかどうか指定するフラグ。
    +
    +
    + + + + + +
    +

    解説

    +

    optに1を指定すると、ファイルに連続したデータ領域を割り当てます。f_lseekによるサイズ拡張とは異なり、対象ファイルのサイズは0(つまりデータ領域未割り当て)でなければなりません。また、リード/ライト ポインタは、ファイル先頭に留まります。この関数により割り当てられたファイルの内容は未定義なので、それに対して何の前提も持つべきではありません。この関数は、次の理由によりFR_DENIEDで失敗することがあります。

    +
      +
    • ボリューム上に連続した空き領域が見つからなかった。
    • +
    • ファイルのサイズが0ではなかった。
    • +
    • ファイルが非書き込みモードで開かれている。
    • +
    • 指定されたファイル サイズが無効。(FATボリューム上で >=4GiB)
    • +
    +

    optに0を指定したときは、連続したデータ領域を探すのみで、その時点ではファイルへの割り当てを行わず、代わりにそれを検索開始ポイントとしてファイル システム オブジェクトにセットします。これにより、そのボリューム上で別の操作(FAT変更を伴う)が行われない限り、書き込まれるファイルは少なくともそのサイズまでは連続性が保証され、遅延無く書き込めることになります。

    +

    時間的制約のあるファイル読み書き操作において、連続データ領域を割り当てられたファイルは有利となります。これは、分割されたファイルによりもたらされる無用なランダム アクセスが減ることにより、ファイル システムやストレージ デバイスの処理のオーバーヘッドが削減されるからです。特にexFATボリューム上の連続ファイルでは一切のFATアクセスが発生せず、効率的なシーケンシャル アクセスが行えます。

    +

    連続ファイルに対して低レベルI/Oを使用したさらに効率的な直接アクセスも容易に行えますが、これは将来の互換性の点で推奨はされません。

    +
    + +
    +

    対応情報

    +

    _USE_EXPAND == 1で、かつ_FS_READONLY == 0のとき使用可能です。

    +
    + + +
    +

    使用例

    +
    +    /* 連続ファイルの作成 */
    +
    +    /* 新しいファイルの作成 */
    +    res = f_open(fp = malloc(sizeof (FIL)), "file.dat", FA_WRITE|FA_CREATE_ALWAYS);
    +    if (res) { /* ファイルが開かれたかチェック */
    +        free(fp);
    +        ...
    +    }
    +
    +    /* 100 MiB の連続領域を割り当てる */
    +    res = f_expand(fp, 104857600, 1);
    +    if (res) { /* 割り当てられたかチェック */
    +        ...
    +        free(fp);
    +        ...
    +    }
    +    /* 連続ファイル作成成功 fp でアクセス可能 */
    +
    +
    +
    +    /* ファイル システムを介さず直接アクセスする例 */
    +
    +    /* ファイル データの物理的位置を取得 */
    +    drv = fp->obj.fs->drv;
    +    sect = fp->obj.fs->database + fp->obj.fs->csize * (fp->obj.sclust - 2);
    +
    +    /* ファイル先頭から2048セクタを書き込み */
    +    res = disk_write(drv, buffer, sect, 2048);
    +
    +
    +
    + + +
    +

    参照

    +

    f_open, f_lseek, FIL

    +
    + +

    戻る

    + + diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/fdisk.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/fdisk.html index 53b6cbb9..2598f7e6 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/fdisk.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/fdisk.html @@ -16,9 +16,9 @@

    物理ドライブを分割します。

     FRESULT f_fdisk (
    -  BYTE  pdrv,         /* [IN] 物理ドライブ番号 */
    -  const DWORD part[], /* [IN] 区画マップ テーブル */
    -  void* work          /* [-] ワークエリア */
    +  BYTE  pdrv,       /* [IN] 物理ドライブ番号 */
    +  const DWORD* szt, /* [IN] 区画サイズ テーブル */
    +  void* work        /* [-] ワークエリア */
     );
     
    @@ -27,9 +27,9 @@ FRESULT f_fdisk (

    引数

    pdrv
    -
    分割する物理ドライブのドライブ番号を指定します。
    -
    part[]
    -
    区画マップ テーブルへのポインタを指定します。
    +
    分割する物理ドライブを指定します。これは論理ドライブ番号ではなく、ディスク関数に渡される物理ドライブ番号です。
    +
    szt
    +
    区画サイズ テーブルの先頭項目へのポインタを指定します。
    work
    ワークエリアへのポインタを指定します。サイズは_MAX_SSバイト必要です。
    @@ -48,7 +48,7 @@ FRESULT f_fdisk (

    説明

    -

    この関数は、指定された物理ドライブのMBRに区画テーブルを作成します。区画分けは一般的なFDISK形式で行うため、最大4つの基本区画を作成することができます。拡張区画には対応していません。区画マップテーブルpart[]にはドライブをどのように分割するか指定して渡します。この配列は4つの項目から成り、先頭の項目が1番目の、最後の項目が4番目の区画のサイズを示します。値が100以下の場合、ドライブの総容量に対する割合をパーセント単位で指定します。100を超える値の場合はセクタ数の直接指定になります。

    +

    この関数は、指定された物理ドライブのMBRに区画テーブルを作成します。区画分けは一般的なFDISK形式で行うため、最大4つの基本区画を作成することができます。拡張区画には対応していません。区画サイズ テーブルにはドライブをどのように分割するか指定します。この配列は4つの項目から成り、先頭の項目が1番目の区画のサイズを示します。項目の値が100以下の場合、その区画のドライブの総容量に対する割合をパーセント単位で指定します。100を超える値の場合はセクタ数の直接指定になります。ドライブ上への区画の配置順は、項目順になります。

    @@ -71,27 +71,22 @@ FRESULT f_fdisk ( /* 新しい物理ドライブ(0)の初期化 */ FATFS fs; - DWORD plist[] = {50, 50, 0, 0}; /* 2分割 */ + DWORD plist[] = {50, 50, 0, 0}; /* 第1区画,第2区画それぞれに50%ずつ割り当て */ BYTE work[_MAX_SS]; - f_fdisk(0, plist, work); /* 物理ドライブ 0 の分割 */ + f_fdisk(0, plist, work); /* 物理ドライブ 0 の分割 */ - f_mount(&fs "0:", 0); - f_mkfs("0:", 0, 0); /* 論理ドライブ 0 のフォーマット. 第二引数は無視される. */ - f_mount(0, "0:", 0); - - f_mount(&fs, "1:", 0); - f_mkfs("1:", 0, 0); /* 論理ドライブ 0 のフォーマット. 第二引数は無視される. */ - f_mount(0, "1:", 0); + f_mkfs("0:", FMT_ANY, work, sizeof work); /* 論理ドライブ 0: のフォーマット */ + f_mkfs("1:", FMT_ANY, work, sizeof work); /* 論理ドライブ 1: のフォーマット */
    -

    See Also

    -

    Volume management, f_mkfs

    +

    参照

    +

    ボリューム管理, f_mkfs

    -

    Return

    +

    戻る

    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/filename.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/filename.html index 72c98487..5f38eaad 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/filename.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/filename.html @@ -33,7 +33,8 @@ dir1/..無効カレント ディレクトリ /..無効ルート ディレクトリ(その上は辿れない) -

    また、_STR_VOLUME_IDオプションを有効にすることでドライブ番号の識別には数字のほか、"sd:file1.txt""ram:swapfile.dat"のように、任意の文字列を使用することも可能になります。

    +

    また、_STR_VOLUME_IDオプションを有効にすることでドライブ番号の識別には数字のほか、"sd:file1.txt""ram:swapfile.dat"のように、任意の文字列(もちろんDOS/Windowsライクなドライブ文字も)を使用することも可能になります。

    +

    【注意】現リビジョン(R0.12)では、exFATボリューム上においてダブル ドット".."はシングル ドット"."として機能し、親ディレクトリを辿ることはできません。

    @@ -55,20 +56,20 @@

    ボリューム管理

    -

    デフォルトの構成では、それぞれの論理ドライブは同じ番号の物理ドライブに1:1で結びつけられていて、自動検出機能によりその物理ドライブ上の一つのFATボリュームがマウントされます。FATボリュームの検出は、セクタ0、第一区画~第四区画の順に行われます。

    +

    デフォルトの構成では、それぞれの論理ドライブは同じ番号の物理ドライブに1:1で結びつけられていて、自動検出機能によりその物理ドライブ上の一つのFATボリュームがマウントされます。FATボリュームの検出は、セクタ0(SFD)、第一区画~第四区画(FDISK)の順に行われます。

    _MULTI_PARTITIONに1を指定すると、それぞれの論理ドライブに対して個別に物理ドライブ番号と区画を指定できるようになります。この構成では、論理ドライブと区画の対応を解決するためのテーブルを次に示すように定義する必要があります。

     例:論理ドライブ0~2を物理ドライブ0(非リムーバブル)の3つの基本区画に割り当て、
        論理ドライブ3を物理ドライブ1(リムーバブル)に割り当てる場合。
     
     PARTITION VolToPart[] = {
    -    {0, 1},     /* 論理ドライブ 0 ==> 物理ドライブ 0, 第1区画 */
    -    {0, 2},     /* 論理ドライブ 1 ==> 物理ドライブ 0, 第2区画 */
    -    {0, 3},     /* 論理ドライブ 2 ==> 物理ドライブ 0, 第3区画 */
    -    {1, 0}      /* 論理ドライブ 3 ==> 物理ドライブ 1, 自動検出 */
    +    {0, 1},     /* "0:" ==> 物理ドライブ 0, 第1区画 */
    +    {0, 2},     /* "1:" ==> 物理ドライブ 0, 第2区画 */
    +    {0, 3},     /* "2:" ==> 物理ドライブ 0, 第3区画 */
    +    {1, 0}      /* "3:" ==> 物理ドライブ 1, 自動検出 */
     };
     
    -
    論理ドライブと物理ドライブの関係
    +
    論理ドライブと物理ドライブの関係

    複数区画指定を使用する場合、次の点に注意しなければなりません。

    • 複数のマウントされた区画を持つ物理ドライブは、非リムーバブルでなければならず、システム動作中のメディア交換は禁止。
    • diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/findfirst.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/findfirst.html index b809908c..c788768b 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/findfirst.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/findfirst.html @@ -62,7 +62,7 @@ FRESULT f_findfirst (

      解説

      pathで指定されるディレクトリを開き、そのディレクトリ内の項目の検索を開始します。正常終了すると、ディレクトリ オブジェクト構造体が作成され、最初に検索名文字列に名前がマッチした項目の情報がfnoの指定するファイル情報構造体にストアされます。名前のマッチする項目が見つからなかった場合は、fno->fname[]にヌル文字列が返されます。ファイル情報構造体の使い方については、f_readdir関数を参照してください。

      -

      マッチ パターン文字列は、ワイルドカード文字(?*)を含むことができます。?は任意の1文字に、*は0文字以上の任意の文字列にマッチします。LFN構成では、SFNとLFN(あれば)の両方に対してテストを行います。現リビジョンではパターン マッチングにおいて次の点で標準システムとは異なる動作となります。

      +

      マッチ パターン文字列は、ワイルドカード文字(?*)を含むことができます。?は任意の1文字に、*は0文字以上の任意の文字列にマッチします。LFN構成では、_USE_FIND = 1のときfname[]のみテストし、_USE_FIND = 2のときはaltname[]もテストします。現リビジョンではパターン マッチングにおいて次の点で標準システムとは異なる動作となります。

      • "*.*"は拡張子なしの名前にマッチしない。(標準システムでは全ての名前にマッチ)
      • ピリオドで終わるパターンは、どの名前にもマッチしない。(標準システムでは拡張子無しの名前にマッチ)
      • @@ -73,7 +73,7 @@ FRESULT f_findfirst (

        対応情報

        -

        この関数は、f_opendir関数およびf_readdir関数のラッパー関数です。_USE_FIND == 1で、かつ_FS_MINIMIZE <= 1のとき使用可能になります。

        +

        この関数は、f_opendir関数およびf_readdir関数のラッパー関数です。_USE_FIND >= 1で、かつ_FS_MINIMIZE <= 1のとき使用可能になります。

        @@ -84,23 +84,14 @@ FRESULT f_findfirst ( void find_image (void) { - FRESULT fr; /* 戻り値 */ + FRESULT fr; /* API戻り値 */ DIR dj; /* ディレクトリ オブジェクト */ - FILINFO fno; /* ファイル情報構造体 */ -#if _USE_LFN - char lfn[_MAX_LFN + 1]; - fno.lfname = lfn; - fno.lfsize = _MAX_LFN + 1; -#endif + FILINFO fno; /* ファイル情報 */ fr = f_findfirst(&dj, &fno, "", "dsc*.jpg"); /* "dsc"で始まるJPEGファイルを検索 */ while (fr == FR_OK && fno.fname[0]) { /* 見つかる間繰り返し */ -#if _USE_LFN - printf("%s %s\n", fno.fname, fno.lfname);/* 見つけた項目の名前を表示 */ -#else - printf("%s\n", fno.fname); -#endif + printf("%s\n", fno.fname); /* 見つけた項目の名前を表示 */ fr = f_findnext(&dj, &fno); /* 次を検索 */ } f_closedir(&dj); diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/findnext.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/findnext.html index 40191a02..e7c1331a 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/findnext.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/findnext.html @@ -54,7 +54,7 @@ FRESULT f_findnext (

        対応情報

        -

        この関数は、f_readdir関数のラッパー関数です。_USE_FIND == 1で、かつ_FS_MINIMIZE <= 1のとき使用可能になります。

        +

        この関数は、f_readdir関数のラッパー関数です。_USE_FIND >= 1で、かつ_FS_MINIMIZE <= 1のとき使用可能になります。

        diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/forward.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/forward.html index acbb4677..34f4f483 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/forward.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/forward.html @@ -60,7 +60,7 @@ FRESULT f_forward (

        対応情報

        -

        _USE_FORWARD == 1で、且つ_FS_TINY == 1のときに使用可能です。

        +

        _USE_FORWARD == 1のときに使用可能です。

        @@ -113,7 +113,7 @@ FRESULT play_file ( if (rc) return rc; /* 全てのデータが転送されるかエラーが発生するまで続ける */ - while (rc == FR_OK && !f_eof(&fil)) { + while (rc == FR_OK && !f_eof(&fil)) { /* ほかの処理... */ diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/getcwd.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/getcwd.html index 24e48446..e5e4a62d 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/getcwd.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/getcwd.html @@ -51,6 +51,7 @@ FRESULT f_getcwd (

        解説

        カレント ドライブのカレント ディレクトリのフル パス文字列を取得します。_VOLUMESが2以上のときは、論理ドライブ番号の付加されたパス名となります。

        +

        現リビジョン(R0.12)では、exFATボリューム上ではカレント ディレクトリを得ることが出来ません。常にルート ディレクトリを返します。

        diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/getfree.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/getfree.html index 91a14090..bb2a4527 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/getfree.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/getfree.html @@ -78,7 +78,7 @@ FRESULT f_getfree ( tot_sect = (fs->n_fatent - 2) * fs->csize; fre_sect = fre_clust * fs->csize; - /* ボリューム全体のサイズと空きのサイズを表示 (512バイト/セクタと仮定) */ + /* ボリュームのサイズと空きサイズを表示 (512バイト/セクタと仮定) */ printf("%10lu KiB total drive space.\n%10lu KiB available.\n", tot_sect / 2, fre_sect / 2); diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/getlabel.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/getlabel.html index bfcad0d6..da84632f 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/getlabel.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/getlabel.html @@ -29,7 +29,7 @@ FRESULT f_getlabel (
        path
        対象となる論理ドライブのパス名を示すヌル文字'\0'終端の文字列へのポインタを指定します。ヌル文字列の場合は、デフォルト ドライブを指定したことになります。
        label
        -
        ボリューム名を格納する配列へのポインタを指定します。少なくとも12要素のサイズが必要です。ボリューム名がない場合はヌル文字列が返されます。この情報が不要なときはヌル ポインタを指定してください。
        +
        ボリューム名を格納する配列へのポインタを指定します。_LFN_UNICODE == 0では少なくとも24要素、_LFN_UNICODE == 1では少なくとも12要素のサイズが必要です。ボリューム名がない場合はヌル文字列が返されます。この情報が不要なときはヌル ポインタを指定してください。
        vsn
        ボリューム シリアル番号を格納するDWORD型変数へのポインタを指定します。この情報が不要なときはヌル ポインタを指定してください。
        @@ -60,7 +60,7 @@ FRESULT f_getlabel (

        使用例

        -    char str[12];
        +    char str[24];
         
             /* デフォルト ドライブのボリューム名を得る */
             f_getlabel("", str, 0);
        diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/lseek.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/lseek.html
        index 88bb5ec0..00e4a958 100644
        --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/lseek.html
        +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/lseek.html
        @@ -16,8 +16,8 @@
         

        ファイルのリード/ライト ポインタを移動します。また、高速シーク機能使用時にはCLMT(後述)の作成にも使用します。

         FRESULT f_lseek (
        -  FIL* fp,    /* [IN] ファイル オブジェクト構造体へのポインタ */
        -  DWORD ofs   /* [IN] 移動先オフセット */
        +  FIL* fp,      /* [IN] ファイル オブジェクト構造体へのポインタ */
        +  FSIZE_t ofs   /* [IN] 移動先オフセット */
         );
         
        @@ -28,7 +28,7 @@ FRESULT f_lseek (
        fp
        対象となるファイル オブジェクト構造体へのポインタを指定します。
        ofs
        -
        移動先のオフセット(リード/ライト ポインタ)値。ファイル先頭からのオフセットをバイト単位で指定します。
        +
        移動先のオフセット(リード/ライト ポインタ)値。ファイル先頭からのオフセットをバイト単位で指定します。データ型FSIZE_tは、DWORD(32-bit)またはQWORD(64-bit)のエリアスで、exFATサポートの有無により切り替わります。
      @@ -48,7 +48,7 @@ FRESULT f_lseek (

      解説

      -

      ファイルのリード/ライト ポインタ(次に読み出し・書き込みされるバイトのオフセット)を移動します。オフセットの原点はファイル先頭です。書き込みモードでファイル サイズより大きな値を指定すると、そこまでファイル サイズが拡張され、拡張された部分のデータは未定義となります。データを遅延無く高速に書き込みたいときは、予めこの関数で必要なサイズまでファイル サイズを拡張しておくと良いでしょう。f_lseek関数が正常終了したあとは、リード/ライト ポインタが正しく移動したかチェックするべきです。リード/ライト ポインタが指定より小さいときは、次の原因が考えられます。

      +

      ファイルのリード/ライト ポインタ(次に読み出し・書き込みされるバイトのオフセット)を移動します。オフセットの原点はファイル先頭です。書き込みモードでファイル サイズより大きな値を指定すると、そこまでファイル サイズが拡張され、拡張された部分のデータは未定義となります。データを遅延無く高速に書き込みたいときは、予めこの関数で必要なサイズまでファイル サイズを拡張しておくと良いでしょう。ファイルに連続したデータ領域を割り当てる必要があるときは、f_expand関数を使用してください。f_lseek関数が正常終了したあとは、リード/ライト ポインタが正しく移動したかチェックするべきです。リード/ライト ポインタが指定より小さいときは、次の原因が考えられます。

      • 非書き込みモードまたは高速シーク モードのため、ファイル サイズでクリップされた。
      • ファイル拡張中にディスクが満杯になった。
      • @@ -122,7 +122,7 @@ FRESULT f_lseek (

        戻る

        diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/mkfs.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/mkfs.html index 20bee57a..b237e6bd 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/mkfs.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/mkfs.html @@ -13,12 +13,14 @@

        f_mkfs

        -

        論理ドライブ上にFATボリュームを作成します。

        +

        論理ドライブ上にFAT/exFATボリュームを作成(フォーマット)します。

         FRESULT f_mkfs (
        -  const TCHAR* path, /* [IN] 論理ドライブ番号 */
        -  BYTE  sfd,         /* [IN] 区画作成方法 */
        -  UINT  au           /* [IN] クラス タサイズ */
        +  const TCHAR* path,  /* [IN] 論理ドライブ番号 */
        +  BYTE  opt,          /* [IN] フォーマット オプション */
        +  DWORD au,           /* [IN] クラスタ サイズ */
        +  void* work,         /* [-]  ワーク エリア */
        +  UINT len            /* [IN] ワーク エリアのサイズ */
         );
         
        @@ -27,11 +29,15 @@ FRESULT f_mkfs (

        引数

        path
        -
        対象の論理ドライブを示すパス名を示すヌル文字'\0'終端の文字列へのポインタを指定します。ドライブ番号を含まない場合は、カレント ドライブを意味します。
        -
        sfd
        -
        パーテーション形式。(0(FDISK) または 1(SFD))
        +
        フォーマット対象の論理ドライブを示すパス名を示すヌル文字'\0'終端の文字列へのポインタを指定します。ドライブ番号を含まない場合は、デフォルト ドライブを意味します。論理ドライブにはf_mount関数でワークエリアが与えられている必要はありません。
        +
        opt
        +
        フォーマット オプション。FM_FAT, FM_FAT32, FM_EXFATの各フラグの組み合わせ(FM_ANYは、これらのOR値)で作成するFATボリュームのタイプを指定します。複数のタイプが指定された場合、その中のいずれかが自動選択されます。exFATが無効のときは、FM_EXFATは無視されます。FM_SFDフラグを指定すると、SFD形式(後述)でボリュームを配置します。
        au
        -
        クラスタ サイズをバイト数またはセクタ数で指定します。値が1~128の範囲場合は、セクタ数を示します。値が_MIN_SS以上の場合は、バイト数を示します。無効値(0または2の累乗でない値など)を指定した場合、ボリュームのサイズに応じたデフォルトのクラスタ サイズが自動選択されます。
        +
        クラスタ サイズをバイト単位で指定します。有効値は、セクタ サイズのN倍となります。Nは2の累乗で、FAT/FAT32ボリュームでは1~128、exFATボリュームでは1~32768です。0を指定した場合は、ボリュームのサイズと選択されたFATタイプに応じたデフォルトのクラスタ サイズが選択されます。
        +
        work
        +
        ワークエリアへのポインタを指定します。
        +
        len
        +
        ワークエリアのサイズをバイト単位で指定します。ワークエリアのサイズは少なくとも関連するドライブのセクタサイズは必要です。また、十分に大きなサイズを与えることにより、書き込みトランザクションの回数が減ってフォーマット時間を短縮できます。
      @@ -41,7 +47,6 @@ FRESULT f_mkfs ( FR_OK, FR_DISK_ERR, FR_NOT_READY, -FR_NOT_ENABLED, FR_MKFS_ABORTED, FR_INVALID_PARAMETER

      @@ -49,14 +54,13 @@ FRESULT f_mkfs (

      説明

      -

      物理ドライブ上にFATボリュームを作成(フォーマット)します。FDISK形式が指定された場合は、物理ドライブ全体を占める基本区画(パーテーション)が作成され、その中にFATボリュームが作成されます。SFD形式では、FATボリュームが物理ドライブの先頭セクタからベタで作成されます。

      -

      マルチ パーテーション機能(_MULTI_PARTITION)が有効で、かつフォーマット対象の論理ドライブが特定の区画(1~4)に結び付けられている場合は、その区画の中にFATボリュームが作成されます。sfdは無視され、その物理ドライブはこれに先立ち、f_fdisk関数または他のツールで適切に区画設定されている必要があります。

      -

      パーテーション形式には、FDISK形式とSFD形式の二通りあります。FDISK形式は、ハードディスク、MMC、SDC、CFC、U Diskなどで標準的に使用されます。FDISK形式では一台の物理ドライブ上に一つまたは複数の区画を作成することができます。区画管理情報はMBR(物理ドライブの先頭セクタ)に記録されます。SFD形式は単に何の分割も行わない形式で、ボリュームは物理ドライブの先頭セクタから開始します。SFD形式は、フロッピー ディスク、マイクロドライブ、光学ディスク、およびその他スーパー フロッピー メディアで標準的に使用されています。

      -

      FATタイプ(FAT12/FAT16/FAT32)は、そのボリューム上のクラスタ数によってのみ決定される決まり[FAT仕様書より]になっていて、それ以外の要因はありません。したがって、どのFATタイプになるかはボリューム サイズとクラスタ サイズに依存します。クラスタ サイズは大きくするほど性能が上がります。

      -

      クラスタ数がFATタイプの境界に近くなるときは、FR_MKFS_ABORTEDで関数が失敗する可能性があります。

      +

      exFAT以外のボリュームのFATタイプ(FAT12/FAT16/FAT32)は、そのボリューム上のクラスタ数によってのみ決定される決まり[FAT仕様書より]になっていて、それ以外の要因はありません。したがって、どのFATタイプになるかはボリューム サイズとクラスタ サイズに依存します。そのボリュームのサイズにおいて、指定されたFATタイプとクラスタ サイズの組み合わせが成立し得ないときは、関数はFR_MKFS_ABORTEDで失敗します。

      +

      クラスタとは、データ格納領域の管理の単位のことで、これを単位にファイルにデータ領域が割り当てられます。たとえば、クラスタ サイズが32768のときは、100バイトのファイルも32768バイトのスペースを消費することになります。このように、クラスタ サイズを大きくするほどボリュームの利用効率が悪くなりますが、その一方で読み書きの性能は上がります。クラスタ サイズによる利用効率と性能はトレード オフの関係にあります。GBクラスのストレージでは、極端に多くのファイルを扱わない限り32768バイト以上に(デフォルト指定ではそのようになる)しておくとよいでしょう。

      +

      パーテーション形式には、FDISK形式とSFD形式の二通りあります。FDISK形式は、ハードディスク、マルチメディアカード、SDカード、CFカード、USBメモリなどで標準的に使用されます。FDISK形式では一台の物理ドライブ上に一つまたは複数の区画を作成することができ、区画管理情報はMBR(物理ドライブの先頭セクタ)に記録されます。SFD形式は単に何の分割も行わない形式で、ボリュームは物理ドライブの先頭セクタから開始します。SFD形式は、フロッピー ディスク、マイクロドライブ、光学ディスク、およびその他スーパー フロッピー メディアで標準的に使用されています。システムによっては、FDISK形式またはSFD形式のどちらか一方のみをサポートし他方をサポートしません。

      +

      FM_SFDが指定されないときはFDISK形式となり、その物理ドライブ全体を占める1個の基本区画(パーテーション)が作成され、その中にFATボリュームが作成されます。FM_SFDが指定されたときはSFD形式となり、FATボリュームがその物理ドライブの先頭セクタからベタで作成されます。

      +

      マルチ パーテーション機能(_MULTI_PARTITION)により、その論理ドライブが特定の区画(1~4)に結び付けられている場合は、その区画の中にFATボリュームが作成されます。FM_SFDの指定は無視され、その物理ドライブはこれに先立ち、f_fdisk関数または他のツールで適切に区画設定されている必要があります。

      -

      対応情報

      _FS_READONLY == 0で、且つ_USE_MKFS == 1のとき使用可能です。

      @@ -69,19 +73,20 @@ FRESULT f_mkfs ( /* Format default drive and create a file */ int main (void) { - FATFS fs; /* File system object (volume work area) */ - FIL fil; /* File object */ - FRESULT res; /* API result code */ - UINT bw; /* Bytes written */ + FATFS fs; /* File system object */ + FIL fil; /* File object */ + FRESULT res; /* API result code */ + UINT bw; /* Bytes written */ + BYTE work[_MAX_SS]; /* Work area (larger is better for process time) */ - /* Register work area (do not care about error) */ - f_mount(&fs, "", 0); - - /* Create FAT volume with default cluster size */ - res = f_mkfs("", 0, 0); + /* Create FAT volume */ + res = f_mkfs("", FM_ANY, 0, work, sizeof work); if (res) ... + /* Register work area */ + f_mount(&fs, "", 0); + /* Create a file as new */ res = f_open(&fil, "hello.txt", FA_CREATE_NEW | FA_WRITE); if (res) ... @@ -93,15 +98,14 @@ int main (void) /* Close the file */ f_close(&fil); - /* Unregister work area */ - f_mount(0, "", 0); + ...
      diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/mount.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/mount.html index 239ae20c..d7906bec 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/mount.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/mount.html @@ -18,7 +18,7 @@ FRESULT f_mount ( FATFS* fs, /* [IN] ファイル システム オブジェクト */ const TCHAR* path, /* [IN] 論理ドライブ番号 */ - BYTE opt /* [IN] オプション */ + BYTE opt /* [IN] 動作オプション */ );
    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/open.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/open.html index 016341de..9fb76427 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/open.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/open.html @@ -37,9 +37,10 @@ FRESULT f_open ( FA_READ読み出しモードで開きます。読み書きする場合はFA_WRITEと共に指定します。 FA_WRITE書き込みモードで開きます。読み書きする場合はFA_READと共に指定します。 FA_OPEN_EXISTING既存のファイルを開きます。ファイルが無いときはエラーになります。(デフォルト) -FA_OPEN_ALWAYS既存のファイルを開きます。ファイルが無いときはファイルを作成します。追記の場合は、この方法でオープンした後、f_lseek関数でファイルの最後尾に移動してください。 FA_CREATE_NEWファイルを作成します。同名のファイルがある場合は、FR_EXISTで失敗します。 FA_CREATE_ALWAYSファイルを作成します。同名のファイルがある場合は、サイズを0にしてから開きます。 +FA_OPEN_ALWAYS既存のファイルを開きます。ファイルが無いときはファイルを作成します。 +FA_OPEN_APPENDFA_OPEN_ALWAYSと同じですが、リード/ライト ポインタはファイルの最後尾にセットされます。 @@ -75,13 +76,13 @@ FRESULT f_open (

    解説

    既存のファイルを開いたり、新しいファイルを作成します。関数が成功するとファイル オブジェクトが作成され、以降そのファイルに対するアクセスに使用します。ファイルを閉じるときは、f_close関数を使用します。何らかの変更が行われたファイルがその後正しく閉じられなかった場合、そのファイルが破損する場合があります。

    既に開かれているファイルを開く必要がある場合は、多重アクセス制御を参照してください。しかし、一つのファイルに対する書き込みモードを含む重複オープンは常に禁止です。

    -

    ファイル アクセスを開始する前に、f_mount関数を使ってそれぞれの論理ドライブにワーク エリア(ファイル システム オブジェクト)を与える必要があります。この初期化の後、その論理ドライブに対して全てのファイル関数が使えるようになります。

    +

    ファイル アクセスを開始する前に、f_mount関数を使ってそれぞれの論理ドライブにワーク エリア(ファイル システム オブジェクト)を与える必要があります。この初期化の後、その論理ドライブに対して全てのファイル関数が使えるようになります。f_mkfs関数とf_fdsk関数は、ワークエリア無しでも使えます。

    対応情報

    -

    全ての構成で使用可能です。_FS_READONLY == 1のときは、FA_WRITE, FA_CREATE_ALWAYS, FA_CREATE_NEW, FA_OPEN_ALWAYSの各フラグはサポートされません。

    +

    全ての構成で使用可能です。_FS_READONLY == 1のときは、FA_READFA_OPEN_EXISTING以外の各フラグはサポートされません。

    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/rc.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/rc.html index 37bd11f4..561f3846 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/rc.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/rc.html @@ -37,11 +37,11 @@
    そのオブジェクトに対する操作の拒否。原因としては次のようなことが考えられます。
    FR_EXIST
    @@ -65,8 +65,9 @@
    FR_MKFS_ABORTED
    f_mkfs関数の処理が開始前に中断された。原因としては次のようなことが考えられます。
    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/readdir.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/readdir.html index 2480a6b5..5305b0a3 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/readdir.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/readdir.html @@ -28,7 +28,7 @@ FRESULT f_readdir (
    dp
    f_opendir関数で作成された有効なディレクトリ オブジェクトへのポインタを指定します。
    fno
    -
    読み出したディレクトリ項目を格納するファイル情報構造体へのポインタを指定します。
    +
    読み出したディレクトリ項目を格納するファイル情報構造体へのポインタ、またはヌル ポインタを指定します。
    @@ -48,17 +48,16 @@ FRESULT f_readdir (

    解説

    -

    ディレクトリの項目(ファイルおよびサブ ディレクトリ)を順次読み出します。この関数を繰り返し実行することによりそのディレクトリの全ての項目を読み出すことができます。得られるファイル情報の詳細については FILINFO構造体を参照してください。全ての項目が読み出され、読み出す項目がもう無いときは、fno->fname[]にヌル文字列が返されます。fnoにヌル ポインタを指定すると、そのディレクトリのリード インデックスを先頭に巻き戻します。また、この関数は次に示すように関連する構成オプションにより動作が変わります。

    -

    ドット エントリ(".""..")は、相対パスが有効なとき(_FS_RPATH >= 1)にのみ出力に現れます。

    -

    LFN機能が有効な時は、この関数の呼び出しに先立ってfno->lfnamefno->lfsizeが有効な値で初期化されていなければなりません。lfnameはLFNを格納するバッファを示し、lfsizeはそのバッファの要素数を示します。LFNを読み出す必要がないときは、lfnameにヌル ポインタをセットしてください。次の条件に一つでも該当する場合は、LFN格納バッファにヌル文字列が返されます。

    +

    ディレクトリの項目(ファイルおよびサブ ディレクトリ)の情報を順次読み出します。この関数を繰り返し実行することによりそのディレクトリの全ての項目を読み出すことができます。得られるファイル情報の詳細については FILINFO構造体を参照してください。全ての項目が読み出され、読み出す項目がもう無いときは、fno->fname[]にヌル文字列が返されます。fnoにヌル ポインタを指定すると、そのディレクトリのリード インデックスを先頭に巻き戻します。サブ ディレクトリのドット エントリ(".""..")は、出力に現れません。

    +

    LFN構成では、altname[]が新たに定義され、そのオブジェクトの短いファイル名がストアされます。次の条件のときは長いファイル名を返せないのでfname[]に短いファイル名がストアされ、altname[]はヌル文字列になります。

    +

    exFATボリュームのディレクトリを読み出すとき、構成によっては問題が発生します。exFATでは短いファイル名がサポートされません。つまり、上記の条件のとき代わりに返すファイル名が無いということです。このような場合はfname[]に"?"が返され、そのオブジェクトにアクセスできないことを示します。この問題を避けるには、FatFsの構成を_LFN_UNICODE = 1および_MAX_LFN = 255として長いファイル名に完全対応とする必要があります。

    -

    対応情報

    _FS_MINIMIZE <= 1のときに使用可能です。

    @@ -68,44 +67,50 @@ FRESULT f_readdir (

    使用例

    +FATFS fs;
    +char buff[256];
    +
     FRESULT scan_files (
         char* path        /* 開始ノード (ワークエリアとしても使用) */
     )
     {
         FRESULT res;
    -    FILINFO fno;
         DIR dir;
    -    int i;
    -    char *fn;   /* 非Unicode構成を想定 */
    -#if _USE_LFN
    -    static char lfn[_MAX_LFN + 1];
    -    fno.lfname = lfn;
    -    fno.lfsize = sizeof lfn;
    -#endif
    +    UINT i;
    +    static FILINFO fno;
     
     
         res = f_opendir(&dir, path);                       /* ディレクトリを開く */
         if (res == FR_OK) {
    -        i = strlen(path);
             for (;;) {
                 res = f_readdir(&dir, &fno);                   /* ディレクトリ項目を1個読み出す */
                 if (res != FR_OK || fno.fname[0] == 0) break;  /* エラーまたは項目無しのときは抜ける */
    -            if (fno.fname[0] == '.') continue;             /* ドットエントリは無視 */
    -#if _USE_LFN
    -            fn = *fno.lfname ? fno.lfname : fno.fname;
    -#else
    -            fn = fno.fname;
    -#endif
                 if (fno.fattrib & AM_DIR) {                    /* ディレクトリ */
    -                sprintf(&path[i], "/%s", fn);
    -                res = scan_files(path);
    -                path[i] = 0;
    +                i = strlen(path);
    +                sprintf(&path[i], "/%s", fno.fname);
    +                res = scan_files(path);                    /* 一つ下へ */
                     if (res != FR_OK) break;
    +                path[i] = 0;
                 } else {                                       /* ファイル */
    -                printf("%s/%s\n", path, fn);
    +                printf("%s/%s\n", path, fno.fname);
                 }
             }
    -        f_closedir(&dir);
    +        f_closedir(&dir)
    +    }
    +
    +    return res;
    +}
    +
    +
    +int main (void)
    +{
    +    FRESULT res;
    +
    +
    +    res = f_mount(&fs, "", 1);
    +    if (res == FR_OK) {
    +        strcpy(buff, "/");
    +        res = scan_files(buff);
         }
     
         return res;
    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/sdir.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/sdir.html
    index 4ee3e53a..0ac0b0e5 100644
    --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/sdir.html
    +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/sdir.html
    @@ -16,20 +16,15 @@
     

    DIR構造体は、f_opendir/f_readdir/f_findfirst/f_findnext関数のワーク エリアとして使用されます。アプリケーションは、この構造体のメンバを書き換えてはなりません。

     typedef struct {
    -    FATFS*  fs;        /* 親ファイル システム オブジェクトへのポインタ */
    -    WORD    id;        /* 親ファイル システム オブジェクトのマウントID */
    -    WORD    index;     /* 次に検索開始するディレクトリ インデックス番号 */
    -    DWORD   sclust;    /* テーブル開始クラスタ (0:ルート) */
    +    _FDID   obj;       /* オブジェクトID */
    +    DOWRD   dptr;      /* 現在のread/writeオフセット */
         DWORD   clust;     /* 現在のクラスタ番号 */
         DWORD   sect;      /* 現在のセクタ番号 */
         BYTE*   dir;       /* 現在のSFNエントリ(Win[]内)へのポインタ */
         BYTE*   fn;        /* SFNバッファへのポインタ (in/out) {file[8],ext[3],status[1]} */
    -#if _FS_LOCK
    -    UINT    lockid;    /* サブ ディレクトリ ロックID (0:ルート) */
    -#endif
     #if _USE_LFN
    +    DWORD   blk_ofs;   /* 現在のエントリブロックの先頭 (0xFFFFFFFF:無効) */
         WCHAR*  lfn;       /* LFNバッファへのポインタ (in/out) */
    -    WORD    lfn_idx;   /* LFNエントリの先頭インデックス (0xFFFF:無効) */
     #endif
     #if _USE_FIND
         const TCHAR*  pat; /* マッチング パターンへのポインタ */
    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/setlabel.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/setlabel.html
    index 26dce4b6..d578fa18 100644
    --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/setlabel.html
    +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/setlabel.html
    @@ -49,11 +49,12 @@ FRESULT f_setlabel (
     
     

    解説

    -

    文字列の先頭にドライブ番号を含む場合は、その論理ドライブに対して設定されます。含まない場合は、デフォルト ドライブに設定されます。ボリューム ラベルを削除するときは、ヌル文字列を指定します。ボリューム ラベルのフォーマットは、ファイル名(SFN)とほぼ同じですが、次の点が異なります。

    +

    文字列の先頭にドライブ番号を含む場合は、その論理ドライブに対して設定されます。含まない場合は、デフォルト ドライブに設定されます。ボリューム ラベルを削除するときは、ヌル文字列を指定します。FATボリューム上では、ボリューム ラベルのフォーマットは、ファイル名とほぼ同じですが、次の点が異なります。

      -
    • OEMコード換算で11バイト以下。ボリューム ラベルにはLFN拡張は適用されません。
    • +
    • 任意の位置にスペースを置くことができる。ただし、FATボリュームではトレーリング スペースは除去される。
    • ピリオドを含むことはできない。
    • -
    • 任意の位置にスペースを置くことができる。ただし、最後尾となるスペースは除去される。
    • +
    • FATボリュームでは、OEMコード換算で11バイト以下。
    • +
    • exFATボリュームでは、11文字以下で、大文字小文字は保持される。

    【補足】 標準システム(Windows)では\xE5で始まるボーリューム ラベル(CP932なら「薔薇」など)の扱いに問題があります。このため、この関数ではそのような名前は無効として処理しています。

    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/sfatfs.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/sfatfs.html index fd7405dd..12e54e16 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/sfatfs.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/sfatfs.html @@ -17,17 +17,20 @@
     typedef struct {
    -    BYTE    fs_type;      /* FATタイプ (0:無効, FS_FAT12, FS_FAT16 or FS_FAT32) */
    +    BYTE    fs_type;      /* ファイル システム (0, FS_FAT12, FS_FAT16, FS_FAT32 or FS_EXFAT) */
         BYTE    drv;          /* 物理ドライブ番号 */
    -    BYTE    csize;        /* クラスタ当たりのセクタ数 (1,2,4,8,...,128) */
         BYTE    n_fats;       /* FATの多重化数 (1,2) */
         BYTE    wflag;        /* win[]ダーティ フラグ */
         BYTE    fsi_flag;     /* FSINFOフラグ (b7:Disabled, b0:Dirty) */
         WORD    id;           /* ファイル システム マウントID */
         WORD    n_rootdir;    /* ルート ディレクトリのエントリ数 (FAT12/16) */
    +    WORD    csize;        /* クラスタ当たりのセクタ数 */
     #if _MAX_SS != _MIN_SS
         WORD    ssize;        /* セクタ サイズ (512, 1024, 2048 or 4096) */
     #endif
    +#if _FS_EXFAT
    +    BYTE*   dirbuf;       /* ディレクトリ エントリ ブロック操作バッファへのポインタ */
    +#endif
     #if _FS_REENTRANT
         _SYNC_t sobj;         /* 同期オブジェクトID */
     #endif
    @@ -37,6 +40,11 @@
     #endif
     #if _FS_RPATH
         DWORD   cdir;         /* カレント ディレクトリのクラスタ番号 (0:ルート) */
    +#if _FS_EXFAT
    +    DWORD   cdc_scl;      /* カレント ディレクトリを含むディレクトリの開始クラスタ番号 (cdir == 0では無効) */
    +    DWORD   cdc_size;     /* b31-b8:カレント ディレクトリを含むディレクトリのサイズ, b7-b0: チェーン ステータス */
    +    DWORD   cdc_ofs;      /* カレント ディレクトリを含むディレクトリ内の位置 (cdir == 0では無効) */
    +#endif
     #endif
         DWORD   n_fatent;     /* FATエントリ数 (総クラスタ数 + 2) */
         DWORD   fsize;        /* FAT 1個のセクタ数 */
    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/sfile.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/sfile.html
    index f5f70256..da6bc6a2 100644
    --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/sfile.html
    +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/sfile.html
    @@ -17,14 +17,11 @@
     
     
     typedef struct {
    -    FATFS*  fs;           /* 親ファイル システム オブジェクトへのポインタ */
    -    WORD    id;           /* 親ファイル システム オブジェクトのマウントID */
    +    _FDID   obj;          /* オブジェクトID */
         BYTE    flag;         /* ファイル ステータス フラグ */
         BYTE    err;          /* エラー中断フラグ */
    -    DWORD   fptr;         /* ファイル読み書きポインタ (ファイル先頭からのバイト オフセット) */
    -    DWORD   fsize;        /* ファイル サイズ(バイト単位) */
    -    DWORD   sclust;       /* ファイル開始クラスタ番号 (0:割り当て無し) */
    -    DWORD   clust;        /* 現在のクラスタ (fptrが0のときは無効、fptrがクラスタ境界上のときは前のクラスタ) */
    +    FSIZE_t fptr;         /* ファイル読み書きポインタ (ファイル先頭からのバイト オフセット) */
    +    DWORD   clust;        /* 現在のクラスタ (fptrがクラスタ境界上のときは前のクラスタ、fptrが0のときは無効) */
         DWORD   dsect;        /* 現在のデータ セクタ */
     #if !_FS_READONLY
         DWORD   dir_sect;     /* このファイルのディレクトリ エントリのあるセクタ */
    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/sfileinfo.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/sfileinfo.html
    index 108e332e..f6694109 100644
    --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/sfileinfo.html
    +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/sfileinfo.html
    @@ -16,14 +16,15 @@
     

    FILINFO構造体は、f_stat/f_readdir/f_findfirst/f_findnext関数で返されるオブジェクトに関する情報を保持します。

     typedef struct {
    -    DWORD fsize;     /* ファイル サイズ */
    -    WORD fdate;      /* 最後に更新された日付 */
    -    WORD ftime;      /* 最後に更新された時刻  */
    -    BYTE fattrib;    /* アトリビュート */
    -    TCHAR fname[13]; /* 短いファイル名 (8.3フォーマット) */
    -#if _USE_LFN
    -    TCHAR* lfname;   /* 長いファイル名のバッファへのポインタ */
    -    int lfsize;      /* 長いファイル名のバッファのサイズ [文字数] */
    +    FSIZE_t fsize;               /* ファイル サイズ */
    +    WORD    fdate;               /* 最後に更新された日付 */
    +    WORD    ftime;               /* 最後に更新された時刻  */
    +    BYTE    fattrib;             /* アトリビュート */
    +#if _USE_LFN != 0
    +    TCHAR   altname[13];         /* 代替ファイル名 */
    +    TCHAR   fname[_MAX_LFN + 1]; /* 主ファイル名 */
    +#else
    +    TCHAR   fname[13];           /* ファイル名 */
     #endif
     } FILINFO;
     
    @@ -32,7 +33,7 @@

    メンバ

    fsize
    -
    ファイルのバイト単位のサイズが格納されます。ディレクトリの場合は常に0です。
    +
    ファイルのバイト単位のサイズが格納されます。ディレクトリの場合は常に0です。データ型FSIZE_tは、DWORD(32-bit)またはQWORD(64-bit)のエリアスで、exFATサポートの有無により切り替わります。
    fdate
    ファイルの変更された日付、またはディレクトリの作成された日付が格納されます。
    @@ -58,11 +59,9 @@
    fattrib
    属性フラグが格納されます。フラグはAM_DIR, AM_RDO, AM_HID, AM_SYS, AM_ARCの組み合わせとなります。
    fname[]
    -
    8.3形式の名前(SFN)が'\0'で終わる文字列として格納されます。非LFN構成のときは、常に大文字で返されます。LFN構成のときは、文字列に含まれるASCII英字が小文字になる場合があります。
    -
    lfname
    -
    長いファイル名(LFN)を格納するバッファへのポインタ。このメンバは、この構造体を使用する前にアプリケーションにより初期化されなければなりません。NULLが設定されるとLFNは返されません。非LFN構成のときはこのメンバは存在しません。
    -
    lfsize
    -
    長いファイル名を格納するバッファのサイズ(要素数)。このメンバは、この構造体を使用する前にアプリケーションにより初期化されなければなりません。非LFN構成のときはこのメンバは存在しません。
    +
    オブジェクト名が'\0'で終わる文字列として格納されます。読み出すべき項目が無いときは、ヌル文字列が返され、この構造体が無効であることを示します。
    +
    altname[]
    +
    代替ファイル名があるときは、それが'\0'で終わる文字列として格納されます。非LFN構成のときは、このメンバはありません。

    戻る

    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/size.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/size.html index 207b4f9e..17d4adf3 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/size.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/size.html @@ -15,7 +15,7 @@

    f_size

    ファイルのサイズを取得します。

    -DWORD f_size (
    +FSIZE_t f_size (
       FIL* fp   /* [IN] ファイル オブジェクト */
     );
     
    @@ -41,7 +41,7 @@ DWORD f_size (

    解説

    この関数は、現リビジョンではマクロとして実装されています。ファイル オブジェクトの正当性チェックや排他制御は行いません。

    -#define f_size(fp) ((fp)->fsize)
    +#define f_size(fp) ((fp)->obj.objsize)
     
    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/stat.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/stat.html index 6e89c3ee..deba89b3 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/stat.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/stat.html @@ -54,8 +54,7 @@ FRESULT f_stat (

    解説

    -

    指定されたファイルまたはサブ ディレクトリの存在を調べます。存在しない場合は、FR_NO_FILEが帰ります。存在する場合はFR_OKが帰り、ファイル情報構造体にそれ関する情報(サイズ、タイムスタンプ、属性および短いファイル名)がストアされます。

    -

    LFN構成のときは、ファイル情報構造体を使う前にfno.lfnameをヌルに設定しておく必要があります。

    +

    指定されたファイルまたはサブ ディレクトリの存在を調べます。存在しない場合は、FR_NO_FILEが帰ります。存在する場合はFR_OKが帰り、それ関する情報(サイズ、タイムスタンプおよび属性)がファイル情報構造体にストアされます。

    @@ -74,23 +73,20 @@ FRESULT f_stat ( printf("Test for 'file.txt'...\n"); -#if _USE_LFN - fno.lfname = 0; -#endif fr = f_stat("file.txt", &fno); switch (fr) { case FR_OK: - printf("Size: %u\n", fno.fsize); + printf("Size: %lu\n", fno.fsize); printf("Timestamp: %u/%02u/%02u, %02u:%02u\n", (fno.fdate >> 9) + 1980, fno.fdate >> 5 & 15, fno.fdate & 31, fno.ftime >> 11, fno.ftime >> 5 & 63); printf("Attributes: %c%c%c%c%c\n", - (fno.fattrib & AM_DIR) ? 'D' : '-', - (fno.fattrib & AM_RDO) ? 'R' : '-', - (fno.fattrib & AM_HID) ? 'H' : '-', - (fno.fattrib & AM_SYS) ? 'S' : '-', - (fno.fattrib & AM_ARC) ? 'A' : '-'); + (fno.fattrib & AM_DIR) ? 'D' : '-', + (fno.fattrib & AM_RDO) ? 'R' : '-', + (fno.fattrib & AM_HID) ? 'H' : '-', + (fno.fattrib & AM_SYS) ? 'S' : '-', + (fno.fattrib & AM_ARC) ? 'A' : '-'); break; case FR_NO_FILE: diff --git a/firmware/chibios-portapack/ext/fatfs/doc/ja/tell.html b/firmware/chibios-portapack/ext/fatfs/doc/ja/tell.html index c0194447..48628d3c 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/ja/tell.html +++ b/firmware/chibios-portapack/ext/fatfs/doc/ja/tell.html @@ -15,7 +15,7 @@

    f_tell

    現在のリード/ライト ポインタを取得します。

    -DWORD f_tell (
    +FSIZE_t f_tell (
       FIL* fp   /* [IN] ファイル オブジェクト */
     );
     
    diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/app1.c b/firmware/chibios-portapack/ext/fatfs/doc/res/app1.c similarity index 90% rename from firmware/chibios-portapack/ext/fatfs/doc/img/app1.c rename to firmware/chibios-portapack/ext/fatfs/doc/res/app1.c index c7e690be..85ce9e25 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/img/app1.c +++ b/firmware/chibios-portapack/ext/fatfs/doc/res/app1.c @@ -1,5 +1,6 @@ /*------------------------------------------------------------/ / Open or create a file in append mode +/ (This function was sperseded by FA_OPEN_APPEND at FatFs R0.12a) /------------------------------------------------------------*/ FRESULT open_append ( diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/app2.c b/firmware/chibios-portapack/ext/fatfs/doc/res/app2.c similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/app2.c rename to firmware/chibios-portapack/ext/fatfs/doc/res/app2.c diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/app3.c b/firmware/chibios-portapack/ext/fatfs/doc/res/app3.c similarity index 94% rename from firmware/chibios-portapack/ext/fatfs/doc/img/app3.c rename to firmware/chibios-portapack/ext/fatfs/doc/res/app3.c index 6733ee61..c4cfcf5f 100644 --- a/firmware/chibios-portapack/ext/fatfs/doc/img/app3.c +++ b/firmware/chibios-portapack/ext/fatfs/doc/res/app3.c @@ -6,7 +6,8 @@ / If the file has been opened without FA_WRITE flag, it only checks if / the file is contiguous and returns the resulut. /-----------------------------------------------------------------------/ -/ This function can work with FatFs R0.09 to R0.11a. +/ This function can work with FatFs R0.09 - R0.11a. +/ It is incompatible with R0.12+. Use f_expand function instead. /----------------------------------------------------------------------*/ /* Declarations of FatFs internal functions accessible from applications. diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/app4.c b/firmware/chibios-portapack/ext/fatfs/doc/res/app4.c similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/app4.c rename to firmware/chibios-portapack/ext/fatfs/doc/res/app4.c diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/f1.png b/firmware/chibios-portapack/ext/fatfs/doc/res/f1.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/f1.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/f1.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/f2.png b/firmware/chibios-portapack/ext/fatfs/doc/res/f2.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/f2.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/f2.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/f3.png b/firmware/chibios-portapack/ext/fatfs/doc/res/f3.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/f3.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/f3.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/f4.png b/firmware/chibios-portapack/ext/fatfs/doc/res/f4.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/f4.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/f4.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/f5.png b/firmware/chibios-portapack/ext/fatfs/doc/res/f5.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/f5.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/f5.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/f6.png b/firmware/chibios-portapack/ext/fatfs/doc/res/f6.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/f6.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/f6.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/res/f7.png b/firmware/chibios-portapack/ext/fatfs/doc/res/f7.png new file mode 100644 index 0000000000000000000000000000000000000000..dee738615053e9dade2641ca6e5a9b5edb10c36e GIT binary patch literal 30461 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 literal 0 HcmV?d00001 diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/layers.png b/firmware/chibios-portapack/ext/fatfs/doc/res/layers.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/layers.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/layers.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/layers1.png b/firmware/chibios-portapack/ext/fatfs/doc/res/layers1.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/layers1.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/layers1.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/layers2.png b/firmware/chibios-portapack/ext/fatfs/doc/res/layers2.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/layers2.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/layers2.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/layers3.png b/firmware/chibios-portapack/ext/fatfs/doc/res/layers3.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/layers3.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/layers3.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/mkfatimg.zip b/firmware/chibios-portapack/ext/fatfs/doc/res/mkfatimg.zip similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/mkfatimg.zip rename to firmware/chibios-portapack/ext/fatfs/doc/res/mkfatimg.zip diff --git a/firmware/chibios-portapack/ext/fatfs/doc/res/mkfs.xls b/firmware/chibios-portapack/ext/fatfs/doc/res/mkfs.xls new file mode 100644 index 0000000000000000000000000000000000000000..f01ddcc8ce572a781312620d03d47549f3487d7c GIT binary patch literal 3238912 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

    qHv1|laeqg3Z=ISlR9Bi5++5V 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

    ui^OJVN%_6axB&-J#n?+(XNkpSuVzWqW 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 z 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�e~`WdJ%G=0rcKO^-s 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 literal 0 HcmV?d00001 diff --git a/firmware/chibios-portapack/ext/fatfs/doc/res/rwtest1.png b/firmware/chibios-portapack/ext/fatfs/doc/res/rwtest1.png new file mode 100644 index 0000000000000000000000000000000000000000..af51c5f9cf13ff09c65ba7fe0a8dc973dd476bcb GIT binary patch literal 27860 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 W{F2^CIIy;4x^~r2yYR}L(EkB`Cc@hQ literal 0 HcmV?d00001 diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/rwtest2.png b/firmware/chibios-portapack/ext/fatfs/doc/res/rwtest2.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/rwtest2.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/rwtest2.png diff --git a/firmware/chibios-portapack/ext/fatfs/doc/img/rwtest3.png b/firmware/chibios-portapack/ext/fatfs/doc/res/rwtest3.png similarity index 100% rename from firmware/chibios-portapack/ext/fatfs/doc/img/rwtest3.png rename to firmware/chibios-portapack/ext/fatfs/doc/res/rwtest3.png diff --git a/firmware/chibios-portapack/ext/fatfs/src/00history.txt b/firmware/chibios-portapack/ext/fatfs/src/00history.txt index 8afb4813..151b6949 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/00history.txt +++ b/firmware/chibios-portapack/ext/fatfs/src/00history.txt @@ -10,7 +10,7 @@ R0.00 (February 26, 2006) R0.01 (April 29, 2006) - First stable version. + The first release. @@ -193,7 +193,7 @@ R0.10 (October 02, 2013) Improved write throughput of f_puts() and f_printf(). Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write(). Fixed f_write() can be truncated when the file size is close to 4GB. - Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect error code. + Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error. @@ -243,3 +243,25 @@ R0.11a (September 05, 2015) Fixed errors in the case conversion teble of Unicode (cc*.c). + +R0.12 (April 12, 2016) + + Added support for exFAT file system. (_FS_EXFAT) + Added f_expand(). (_USE_EXPAND) + Changed some members in FINFO structure and behavior of f_readdir(). + Added an option _USE_CHMOD. + Removed an option _WORD_ACCESS. + Fixed errors in the case conversion table of Unicode (cc*.c). + + + +R0.12a (July 10, 2016) + + Added support for creating exFAT volume with some changes of f_mkfs(). + Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed. + f_forward() is available regardless of _FS_TINY. + Fixed f_mkfs() creates wrong volume. + Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD. + Fixed wrong memory read in create_name(). + + diff --git a/firmware/chibios-portapack/ext/fatfs/src/00readme.txt b/firmware/chibios-portapack/ext/fatfs/src/00readme.txt index 70994eda..42426a40 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/00readme.txt +++ b/firmware/chibios-portapack/ext/fatfs/src/00readme.txt @@ -1,4 +1,4 @@ -FatFs Module Source Files R0.11 +FatFs Module Source Files R0.12a FILES diff --git a/firmware/chibios-portapack/ext/fatfs/src/diskio.c b/firmware/chibios-portapack/ext/fatfs/src/diskio.c index 82200ad0..1d57ddb4 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/diskio.c +++ b/firmware/chibios-portapack/ext/fatfs/src/diskio.c @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------*/ -/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2014 */ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */ /*-----------------------------------------------------------------------*/ /* If a working storage control module is available, it should be */ /* attached to the FatFs via a glue function rather than modifying it. */ @@ -8,14 +8,11 @@ /*-----------------------------------------------------------------------*/ #include "diskio.h" /* FatFs lower layer API */ -#include "usbdisk.h" /* Example: Header file of existing USB MSD control module */ -#include "atadrive.h" /* Example: Header file of existing ATA harddisk control module */ -#include "sdcard.h" /* Example: Header file of existing MMC/SDC contorl module */ /* Definitions of physical drive number for each drive */ -#define ATA 0 /* Example: Map ATA harddisk to physical drive 0 */ -#define MMC 1 /* Example: Map MMC/SD card to physical drive 1 */ -#define USB 2 /* Example: Map USB MSD to physical drive 2 */ +#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */ +#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */ +#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */ /*-----------------------------------------------------------------------*/ @@ -30,21 +27,21 @@ DSTATUS disk_status ( int result; switch (pdrv) { - case ATA : - result = ATA_disk_status(); + case DEV_RAM : + result = RAM_disk_status(); // translate the reslut code here return stat; - case MMC : + case DEV_MMC : result = MMC_disk_status(); // translate the reslut code here return stat; - case USB : + case DEB_USB : result = USB_disk_status(); // translate the reslut code here @@ -68,21 +65,21 @@ DSTATUS disk_initialize ( int result; switch (pdrv) { - case ATA : - result = ATA_disk_initialize(); + case DEV_RAM : + result = RAM_disk_initialize(); // translate the reslut code here return stat; - case MMC : + case DEV_MMC : result = MMC_disk_initialize(); // translate the reslut code here return stat; - case USB : + case DEV_USB : result = USB_disk_initialize(); // translate the reslut code here @@ -101,7 +98,7 @@ DSTATUS disk_initialize ( DRESULT disk_read ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ BYTE *buff, /* Data buffer to store read data */ - DWORD sector, /* Sector address in LBA */ + DWORD sector, /* Start sector in LBA */ UINT count /* Number of sectors to read */ ) { @@ -109,16 +106,16 @@ DRESULT disk_read ( int result; switch (pdrv) { - case ATA : + case DEV_RAM : // translate the arguments here - result = ATA_disk_read(buff, sector, count); + result = RAM_disk_read(buff, sector, count); // translate the reslut code here return res; - case MMC : + case DEV_MMC : // translate the arguments here result = MMC_disk_read(buff, sector, count); @@ -127,7 +124,7 @@ DRESULT disk_read ( return res; - case USB : + case DEV_USB : // translate the arguments here result = USB_disk_read(buff, sector, count); @@ -146,11 +143,10 @@ DRESULT disk_read ( /* Write Sector(s) */ /*-----------------------------------------------------------------------*/ -#if _USE_WRITE DRESULT disk_write ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ const BYTE *buff, /* Data to be written */ - DWORD sector, /* Sector address in LBA */ + DWORD sector, /* Start sector in LBA */ UINT count /* Number of sectors to write */ ) { @@ -158,16 +154,16 @@ DRESULT disk_write ( int result; switch (pdrv) { - case ATA : + case DEV_RAM : // translate the arguments here - result = ATA_disk_write(buff, sector, count); + result = RAM_disk_write(buff, sector, count); // translate the reslut code here return res; - case MMC : + case DEV_MMC : // translate the arguments here result = MMC_disk_write(buff, sector, count); @@ -176,7 +172,7 @@ DRESULT disk_write ( return res; - case USB : + case DEV_USB : // translate the arguments here result = USB_disk_write(buff, sector, count); @@ -188,14 +184,13 @@ DRESULT disk_write ( return RES_PARERR; } -#endif + /*-----------------------------------------------------------------------*/ /* Miscellaneous Functions */ /*-----------------------------------------------------------------------*/ -#if _USE_IOCTL DRESULT disk_ioctl ( BYTE pdrv, /* Physical drive nmuber (0..) */ BYTE cmd, /* Control code */ @@ -206,19 +201,19 @@ DRESULT disk_ioctl ( int result; switch (pdrv) { - case ATA : + case DEV_RAM : - // Process of the command for the ATA drive + // Process of the command for the RAM drive return res; - case MMC : + case DEV_MMC : // Process of the command for the MMC/SD card return res; - case USB : + case DEV_USB : // Process of the command the USB drive @@ -227,4 +222,4 @@ DRESULT disk_ioctl ( return RES_PARERR; } -#endif + diff --git a/firmware/chibios-portapack/ext/fatfs/src/diskio.h b/firmware/chibios-portapack/ext/fatfs/src/diskio.h index 9650f683..03e8b7c5 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/diskio.h +++ b/firmware/chibios-portapack/ext/fatfs/src/diskio.h @@ -9,9 +9,6 @@ extern "C" { #endif -#define _USE_WRITE 1 /* 1: Enable disk_write function */ -#define _USE_IOCTL 1 /* 1: Enable disk_ioctl fucntion */ - #include "integer.h" @@ -67,6 +64,9 @@ DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); #define MMC_GET_CID 12 /* Get CID */ #define MMC_GET_OCR 13 /* Get OCR */ #define MMC_GET_SDSTAT 14 /* Get SD status */ +#define ISDIO_READ 55 /* Read data form SD iSDIO register */ +#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ +#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ /* ATA/CF specific ioctl command */ #define ATA_GET_REV 20 /* Get F/W revision */ diff --git a/firmware/chibios-portapack/ext/fatfs/src/ff.c b/firmware/chibios-portapack/ext/fatfs/src/ff.c index dfcf12ae..c6e91b37 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ff.c +++ b/firmware/chibios-portapack/ext/fatfs/src/ff.c @@ -1,11 +1,13 @@ /*----------------------------------------------------------------------------/ -/ FatFs - FAT file system module R0.11a (C)ChaN, 2015 / +/ FatFs - Generic FAT file system module R0.12a / /-----------------------------------------------------------------------------/ -/ FatFs module is a free software that opened under license policy of -/ following conditions. / -/ Copyright (C) 2015, ChaN, all right reserved. +/ Copyright (C) 2016, ChaN, all right reserved. / +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + / 1. Redistributions of source code must retain the above copyright notice, / this condition and the following disclaimer. / @@ -17,7 +19,7 @@ #include "ff.h" /* Declarations of FatFs API */ -#include "diskio.h" /* Declarations of disk I/O functions */ +#include "diskio.h" /* Declarations of device I/O functions */ /*-------------------------------------------------------------------------- @@ -26,11 +28,14 @@ ---------------------------------------------------------------------------*/ -#if _FATFS != 64180 /* Revision ID */ +#if _FATFS != 80186 /* Revision ID */ #error Wrong include file (ff.h). #endif +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + /* Reentrancy related */ #if _FS_REENTRANT #if _USE_LFN == 1 @@ -43,7 +48,6 @@ #define LEAVE_FF(fs, res) return res #endif -#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } /* Definitions of sector size */ @@ -57,7 +61,7 @@ #endif -/* Timestamp feature */ +/* Timestamp */ #if _FS_NORTC == 1 #if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31 #error Invalid _FS_NORTC settings @@ -68,15 +72,15 @@ #endif -/* File access control feature */ -#if _FS_LOCK +/* File lock controls */ +#if _FS_LOCK != 0 #if _FS_READONLY #error _FS_LOCK must be 0 at read-only configuration #endif typedef struct { FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ DWORD clu; /* Object ID 2, directory (0:root) */ - WORD idx; /* Object ID 3, directory index */ + DWORD ofs; /* Object ID 3, directory offset */ WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ } FILESEM; #endif @@ -309,8 +313,8 @@ typedef struct { 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} #elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ -#if _USE_LFN -#error Cannot use LFN feature without valid code page. +#if _USE_LFN != 0 +#error Cannot enable LFN without valid code page. #endif #define _DF1S 0 @@ -325,7 +329,7 @@ typedef struct { #define IsLower(c) (((c)>='a')&&((c)<='z')) #define IsDigit(c) (((c)>='0')&&((c)<='9')) -#if _DF1S /* Code page is DBCS */ +#if _DF1S != 0 /* Code page is DBCS */ #ifdef _DF2S /* Two 1st byte areas */ #define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) @@ -347,6 +351,18 @@ typedef struct { #endif /* _DF1S */ +/* File attribute bits (internal use) */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* File access control and file status flags (internal use) */ +#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ +#define FA_MODIFIED 0x40 /* File has been modified */ +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ + + /* Name status flags */ #define NSFLAG 11 /* Index of name status byte in fn[] */ #define NS_LOSS 0x01 /* Out of 8.3 format */ @@ -355,92 +371,154 @@ typedef struct { #define NS_BODY 0x08 /* Lower case flag (body) */ #define NS_EXT 0x10 /* Lower case flag (ext) */ #define NS_DOT 0x20 /* Dot entry */ +#define NS_NOLFN 0x40 /* Do not find LFN */ +#define NS_NONAME 0x80 /* Not followed */ -/* FAT sub-type boundaries (Differ from specs but correct for real DOS/Windows) */ -#define MIN_FAT16 4086U /* Minimum number of clusters of FAT16 */ -#define MIN_FAT32 65526U /* Minimum number of clusters of FAT32 */ +/* Limits and boundaries (differ from specs but correct for real DOS/Windows) */ +#define MAX_FAT12 0xFF5 /* Maximum number of FAT12 clusters */ +#define MAX_FAT16 0xFFF5 /* Maximum number of FAT16 clusters */ +#define MAX_FAT32 0xFFFFFF5 /* Maximum number of FAT32 clusters */ +#define MAX_EXFAT 0x7FFFFFFD /* Maximum number of exFAT clusters (limited by implementation) */ +#define MAX_DIR 0x200000 /* Maximum size of FAT directory */ +#define MAX_DIR_EX 0x10000000 /* Maximum size of exFAT directory */ /* FatFs refers the members in the FAT structures as byte array instead of / structure members because the structure is not binary compatible between / different platforms */ -#define BS_jmpBoot 0 /* x86 jump instruction (3) */ -#define BS_OEMName 3 /* OEM name (8) */ -#define BPB_BytsPerSec 11 /* Sector size [byte] (2) */ -#define BPB_SecPerClus 13 /* Cluster size [sector] (1) */ -#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */ -#define BPB_NumFATs 16 /* Number of FAT copies (1) */ -#define BPB_RootEntCnt 17 /* Number of root directory entries for FAT12/16 (2) */ -#define BPB_TotSec16 19 /* Volume size [sector] (2) */ -#define BPB_Media 21 /* Media descriptor (1) */ -#define BPB_FATSz16 22 /* FAT size [sector] (2) */ -#define BPB_SecPerTrk 24 /* Track size [sector] (2) */ -#define BPB_NumHeads 26 /* Number of heads (2) */ -#define BPB_HiddSec 28 /* Number of special hidden sectors (4) */ -#define BPB_TotSec32 32 /* Volume size [sector] (4) */ -#define BS_DrvNum 36 /* Physical drive number (1) */ -#define BS_NTres 37 /* Error flag (1) */ -#define BS_BootSig 38 /* Extended boot signature (1) */ -#define BS_VolID 39 /* Volume serial number (4) */ -#define BS_VolLab 43 /* Volume label (8) */ -#define BS_FilSysType 54 /* File system type (1) */ -#define BPB_FATSz32 36 /* FAT size [sector] (4) */ -#define BPB_ExtFlags 40 /* Extended flags (2) */ -#define BPB_FSVer 42 /* File system version (2) */ -#define BPB_RootClus 44 /* Root directory first cluster (4) */ -#define BPB_FSInfo 48 /* Offset of FSINFO sector (2) */ -#define BPB_BkBootSec 50 /* Offset of backup boot sector (2) */ -#define BS_DrvNum32 64 /* Physical drive number (1) */ -#define BS_NTres32 65 /* Error flag (1) */ -#define BS_BootSig32 66 /* Extended boot signature (1) */ -#define BS_VolID32 67 /* Volume serial number (4) */ -#define BS_VolLab32 71 /* Volume label (8) */ -#define BS_FilSysType32 82 /* File system type (1) */ -#define FSI_LeadSig 0 /* FSI: Leading signature (4) */ -#define FSI_StrucSig 484 /* FSI: Structure signature (4) */ -#define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */ -#define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */ -#define MBR_Table 446 /* MBR: Partition table offset (2) */ -#define SZ_PTE 16 /* MBR: Size of a partition table entry */ -#define BS_55AA 510 /* Signature word (2) */ +#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ +#define BS_OEMName 3 /* OEM name (8-byte) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */ +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ +#define BPB_RootEntCnt 17 /* Size of root directory area for FAT12/16 [entry] (WORD) */ +#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ +#define BPB_Media 21 /* Media descriptor (BYTE) */ +#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ +#define BPB_SecPerTrk 24 /* Track size for int13h [sector] (WORD) */ +#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ +#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */ +#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */ +#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */ +#define BS_NTres 37 /* Error flag (BYTE) */ +#define BS_BootSig 38 /* Extended boot signature (BYTE) */ +#define BS_VolID 39 /* Volume serial number (DWORD) */ +#define BS_VolLab 43 /* Volume label string (8-byte) */ +#define BS_FilSysType 54 /* File system type string (8-byte) */ +#define BS_BootCode 62 /* Boot code (448-byte) */ -#define DIR_Name 0 /* Short file name (11) */ -#define DIR_Attr 11 /* Attribute (1) */ -#define DIR_NTres 12 /* Lower case flag (1) */ -#define DIR_CrtTimeTenth 13 /* Created time sub-second (1) */ -#define DIR_CrtTime 14 /* Created time (2) */ -#define DIR_CrtDate 16 /* Created date (2) */ -#define DIR_LstAccDate 18 /* Last accessed date (2) */ -#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (2) */ -#define DIR_WrtTime 22 /* Modified time (2) */ -#define DIR_WrtDate 24 /* Modified date (2) */ -#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (2) */ -#define DIR_FileSize 28 /* File size (4) */ -#define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */ -#define LDIR_Attr 11 /* LFN attribute (1) */ -#define LDIR_Type 12 /* LFN type (1) */ -#define LDIR_Chksum 13 /* Checksum of corresponding SFN entry */ -#define LDIR_FstClusLO 26 /* Must be zero (0) */ -#define SZ_DIRE 32 /* Size of a directory entry */ +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ +#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ +#define BPB_FSVer32 42 /* FAT32: File system version (WORD) */ +#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */ +#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */ +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ +#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */ +#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */ +#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */ +#define BS_FilSysType32 82 /* FAT32: File system type string (8-byte) */ +#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ + +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */ +#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */ +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */ +#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */ +#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */ +#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */ +#define BPB_RootClusEx 96 /* exFAT: Root directory cluster (DWORD) */ +#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ +#define BPB_FSVerEx 104 /* exFAT: File system version (WORD) */ +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (BYTE) */ +#define BPB_ActFatEx 107 /* exFAT: Active FAT flags (BYTE) */ +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in byte (BYTE) */ +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in sector (BYTE) */ +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ +#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */ +#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */ +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ +#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */ + +#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */ +#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */ +#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */ +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */ + +#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ +#define PTE_StHead 1 /* MBR PTE: Start head */ +#define PTE_StSec 2 /* MBR PTE: Start sector */ +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ +#define PTE_System 4 /* MBR PTE: System ID */ +#define PTE_EdHead 5 /* MBR PTE: End head */ +#define PTE_EdSec 6 /* MBR PTE: End sector */ +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ + +#define BS_55AA 510 /* Signature word (WORD) */ + +#define DIR_Name 0 /* Short file name (11-byte) */ +#define DIR_Attr 11 /* Attribute (BYTE) */ +#define DIR_NTres 12 /* Lower case flag (BYTE) */ +#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ +#define DIR_CrtTime 14 /* Created time (DWORD) */ +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ +#define DIR_ModTime 22 /* Modified time (DWORD) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ +#define DIR_FileSize 28 /* File size (DWORD) */ +#define LDIR_Ord 0 /* LFN entry order and LLE flag (BYTE) */ +#define LDIR_Attr 11 /* LFN attribute (BYTE) */ +#define LDIR_Type 12 /* LFN type (BYTE) */ +#define LDIR_Chksum 13 /* Checksum of the SFN entry (BYTE) */ +#define LDIR_FstClusLO 26 /* Must be zero (WORD) */ +#define XDIR_Type 0 /* Type of exFAT directory entry (BYTE) */ +#define XDIR_NumLabel 1 /* Number of volume label characters (BYTE) */ +#define XDIR_Label 2 /* Volume label (11-WORD) */ +#define XDIR_CaseSum 4 /* Sum of case conversion table (DWORD) */ +#define XDIR_NumSec 1 /* Number of secondary entries (BYTE) */ +#define XDIR_SetSum 2 /* Sum of the set of directory entries (WORD) */ +#define XDIR_Attr 4 /* File attribute (WORD) */ +#define XDIR_CrtTime 8 /* Created time (DWORD) */ +#define XDIR_ModTime 12 /* Modified time (DWORD) */ +#define XDIR_AccTime 16 /* Last accessed time (DWORD) */ +#define XDIR_CrtTime10 20 /* Created time subsecond (BYTE) */ +#define XDIR_ModTime10 21 /* Modified time subsecond (BYTE) */ +#define XDIR_CrtTZ 22 /* Created timezone (BYTE) */ +#define XDIR_ModTZ 23 /* Modified timezone (BYTE) */ +#define XDIR_AccTZ 24 /* Last accessed timezone (BYTE) */ +#define XDIR_GenFlags 33 /* Gneral secondary flags (WORD) */ +#define XDIR_NumName 35 /* Number of file name characters (BYTE) */ +#define XDIR_NameHash 36 /* Hash of file name (WORD) */ +#define XDIR_ValidFileSize 40 /* Valid file size (QWORD) */ +#define XDIR_FstClus 52 /* First cluster of the file data (DWORD) */ +#define XDIR_FileSize 56 /* File/Directory size (QWORD) */ + +#define SZDIRE 32 /* Size of a directory entry */ #define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ -#define DDEM 0xE5 /* Deleted directory entry mark at DIR_Name[0] */ +#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */ #define RDDEM 0x05 /* Replacement of the character collides with DDEM */ + /*-------------------------------------------------------------------------- Module Private Work Area ---------------------------------------------------------------------------*/ -/* Remark: Uninitialized variables with static duration are guaranteed -/ zero/null at start-up. If not, either the linker or start-up routine -/ being used is not compliance with ANSI-C standard. -*/ +/* Remark: Variables here without initial value shall be guaranteed zero/null +/ at start-up. If not, either the linker or start-up routine being used is +/ not compliance with C standard. */ #if _VOLUMES < 1 || _VOLUMES > 9 #error Wrong _VOLUMES setting @@ -448,35 +526,54 @@ typedef struct { static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ static WORD Fsid; /* File system mount ID */ -#if _FS_RPATH && _VOLUMES >= 2 +#if _FS_RPATH != 0 && _VOLUMES >= 2 static BYTE CurrVol; /* Current drive */ #endif -#if _FS_LOCK +#if _FS_LOCK != 0 static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ #endif -#if _USE_LFN == 0 /* Non LFN feature */ -#define DEFINE_NAMEBUF BYTE sfn[12] -#define INIT_BUF(dobj) (dobj).fn = sfn -#define FREE_BUF() +#if _USE_LFN == 0 /* Non-LFN configuration */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() #else #if _MAX_LFN < 12 || _MAX_LFN > 255 #error Wrong _MAX_LFN setting #endif -#if _USE_LFN == 1 /* LFN feature with static working buffer */ -static WCHAR LfnBuf[_MAX_LFN + 1]; -#define DEFINE_NAMEBUF BYTE sfn[12] -#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } -#define FREE_BUF() -#elif _USE_LFN == 2 /* LFN feature with dynamic working buffer on the stack */ -#define DEFINE_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN + 1] -#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; } -#define FREE_BUF() -#elif _USE_LFN == 3 /* LFN feature with dynamic working buffer on the heap */ -#define DEFINE_NAMEBUF BYTE sfn[12]; WCHAR *lfn -#define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); (dobj).lfn = lfn; (dobj).fn = sfn; } -#define FREE_BUF() ff_memfree(lfn) + +#if _USE_LFN == 1 /* LFN enabled with static working buffer */ +#if _FS_EXFAT +static BYTE DirBuf[SZDIRE*19]; /* Directory entry block scratchpad buffer (19 entries in size) */ +#endif +static WCHAR LfnBuf[_MAX_LFN+1]; /* LFN enabled with static working buffer */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() + +#elif _USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ +#if _FS_EXFAT +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; BYTE dbuf[SZDIRE*19]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } +#define FREE_NAMBUF() +#else +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } +#define FREE_NAMBUF() +#endif + +#elif _USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ +#if _FS_EXFAT +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2 + SZDIRE*19); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+_MAX_LFN+1); } +#define FREE_NAMBUF() ff_memfree(lfn) +#else +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } +#define FREE_NAMBUF() ff_memfree(lfn) +#endif + #else #error Wrong _USE_LFN setting #endif @@ -498,6 +595,85 @@ static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for SBCS extended ---------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------*/ +/* Load/Store multi-byte word in the FAT structure */ +/*-----------------------------------------------------------------------*/ + +static +WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ +{ + WORD rv; + + rv = ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +static +DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ +{ + DWORD rv; + + rv = ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +#if _FS_EXFAT +static +QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ +{ + QWORD rv; + + rv = ptr[7]; + rv = rv << 8 | ptr[6]; + rv = rv << 8 | ptr[5]; + rv = rv << 8 | ptr[4]; + rv = rv << 8 | ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} +#endif + +#if !_FS_READONLY +static +void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +static +void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +#if _FS_EXFAT +static +void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} +#endif +#endif /* !_FS_READONLY */ + + + /*-----------------------------------------------------------------------*/ /* String functions */ /*-----------------------------------------------------------------------*/ @@ -508,39 +684,35 @@ void mem_cpy (void* dst, const void* src, UINT cnt) { BYTE *d = (BYTE*)dst; const BYTE *s = (const BYTE*)src; -#if _WORD_ACCESS == 1 - while (cnt >= sizeof (int)) { - *(int*)d = *(int*)s; - d += sizeof (int); s += sizeof (int); - cnt -= sizeof (int); + if (cnt) { + do *d++ = *s++; while (--cnt); } -#endif - while (cnt--) - *d++ = *s++; } -/* Fill memory */ +/* Fill memory block */ static void mem_set (void* dst, int val, UINT cnt) { BYTE *d = (BYTE*)dst; - while (cnt--) - *d++ = (BYTE)val; + do *d++ = (BYTE)val; while (--cnt); } -/* Compare memory to memory */ +/* Compare memory block */ static -int mem_cmp (const void* dst, const void* src, UINT cnt) { +int mem_cmp (const void* dst, const void* src, UINT cnt) { /* ZR:same, NZ:different */ const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; int r = 0; - while (cnt-- && (r = *d++ - *s++) == 0) ; + do { + r = *d++ - *s++; + } while (--cnt && r == 0); + return r; } /* Check if chr is contained in the string */ static -int chk_chr (const char* str, int chr) { +int chk_chr (const char* str, int chr) { /* NZ:contained, ZR:not contained */ while (*str && *str != chr) str++; return *str; } @@ -548,10 +720,10 @@ int chk_chr (const char* str, int chr) { +#if _FS_REENTRANT /*-----------------------------------------------------------------------*/ /* Request/Release grant to access the volume */ /*-----------------------------------------------------------------------*/ -#if _FS_REENTRANT static int lock_fs ( FATFS* fs /* File system object */ @@ -567,23 +739,19 @@ void unlock_fs ( FRESULT res /* Result code to be returned */ ) { - if (fs && - res != FR_NOT_ENABLED && - res != FR_INVALID_DRIVE && - res != FR_INVALID_OBJECT && - res != FR_TIMEOUT) { + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) { ff_rel_grant(fs->sobj); } } + #endif - +#if _FS_LOCK != 0 /*-----------------------------------------------------------------------*/ /* File lock control functions */ /*-----------------------------------------------------------------------*/ -#if _FS_LOCK static FRESULT chk_lock ( /* Check if the file can be accessed */ @@ -596,15 +764,16 @@ FRESULT chk_lock ( /* Check if the file can be accessed */ /* Search file semaphore table */ for (i = be = 0; i < _FS_LOCK; i++) { if (Files[i].fs) { /* Existing entry */ - if (Files[i].fs == dp->fs && /* Check if the object matched with an open object */ - Files[i].clu == dp->sclust && - Files[i].idx == dp->index) break; + if (Files[i].fs == dp->obj.fs && /* Check if the object matched with an open object */ + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; } else { /* Blank entry */ be = 1; } } - if (i == _FS_LOCK) /* The object is not opened */ + if (i == _FS_LOCK) { /* The object is not opened */ return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */ + } /* The object has been opened. Reject any open against writing file and all write mode open */ return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; @@ -631,17 +800,17 @@ UINT inc_lock ( /* Increment object open counter and returns its index (0:Intern for (i = 0; i < _FS_LOCK; i++) { /* Find the object */ - if (Files[i].fs == dp->fs && - Files[i].clu == dp->sclust && - Files[i].idx == dp->index) break; + if (Files[i].fs == dp->obj.fs && + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; } if (i == _FS_LOCK) { /* Not opened. Register it as new. */ for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */ - Files[i].fs = dp->fs; - Files[i].clu = dp->sclust; - Files[i].idx = dp->index; + Files[i].fs = dp->obj.fs; + Files[i].clu = dp->obj.sclust; + Files[i].ofs = dp->dptr; Files[i].ctr = 0; } @@ -665,9 +834,9 @@ FRESULT dec_lock ( /* Decrement object open counter */ if (--i < _FS_LOCK) { /* Shift index number origin from 0 */ n = Files[i].ctr; if (n == 0x100) n = 0; /* If write mode open, delete the entry */ - if (n) n--; /* Decrement read mode open count */ + if (n > 0) n--; /* Decrement read mode open count */ Files[i].ctr = n; - if (!n) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */ res = FR_OK; } else { res = FR_INT_ERR; /* Invalid index nunber */ @@ -687,8 +856,8 @@ void clear_lock ( /* Clear lock entries of the volume */ if (Files[i].fs == fs) Files[i].fs = 0; } } -#endif +#endif /* _FS_LOCK != 0 */ @@ -697,8 +866,8 @@ void clear_lock ( /* Clear lock entries of the volume */ /*-----------------------------------------------------------------------*/ #if !_FS_READONLY static -FRESULT sync_window ( /* FR_OK:succeeded, !=0:error */ - FATFS* fs /* File system object */ +FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERROR */ + FATFS* fs /* File system object */ ) { DWORD wsect; @@ -726,9 +895,9 @@ FRESULT sync_window ( /* FR_OK:succeeded, !=0:error */ static -FRESULT move_window ( /* FR_OK(0):succeeded, !=0:error */ - FATFS* fs, /* File system object */ - DWORD sector /* Sector number to make appearance in the fs->win[] */ +FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERROR */ + FATFS* fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ ) { FRESULT res = FR_OK; @@ -752,10 +921,11 @@ FRESULT move_window ( /* FR_OK(0):succeeded, !=0:error */ +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Synchronize file system and strage device */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY + static FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */ FATFS* fs /* File system object */ @@ -770,33 +940,32 @@ FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */ if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* Create FSInfo structure */ mem_set(fs->win, 0, SS(fs)); - ST_WORD(fs->win + BS_55AA, 0xAA55); - ST_DWORD(fs->win + FSI_LeadSig, 0x41615252); - ST_DWORD(fs->win + FSI_StrucSig, 0x61417272); - ST_DWORD(fs->win + FSI_Free_Count, fs->free_clust); - ST_DWORD(fs->win + FSI_Nxt_Free, fs->last_clust); + st_word(fs->win + BS_55AA, 0xAA55); + st_dword(fs->win + FSI_LeadSig, 0x41615252); + st_dword(fs->win + FSI_StrucSig, 0x61417272); + st_dword(fs->win + FSI_Free_Count, fs->free_clst); + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* Write it into the FSInfo sector */ fs->winsect = fs->volbase + 1; disk_write(fs->drv, fs->win, fs->winsect, 1); fs->fsi_flag = 0; } /* Make sure that no pending write process in the physical drive */ - if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) - res = FR_DISK_ERR; + if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR; } return res; } -#endif +#endif /*-----------------------------------------------------------------------*/ /* Get sector# from cluster# */ /*-----------------------------------------------------------------------*/ -/* Hidden API for hacks and disk tools */ +static DWORD clust2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ FATFS* fs, /* File system object */ DWORD clst /* Cluster# to be converted */ @@ -813,16 +982,16 @@ DWORD clust2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ /*-----------------------------------------------------------------------*/ /* FAT access - Read value of a FAT entry */ /*-----------------------------------------------------------------------*/ -/* Hidden API for hacks and disk tools */ -DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluster status */ - FATFS* fs, /* File system object */ - DWORD clst /* FAT index number (cluster number) to get the value */ +static +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ + _FDID* obj, /* Corresponding object */ + DWORD clst /* Cluster number to get the value */ ) { UINT wc, bc; - BYTE *p; DWORD val; + FATFS *fs = obj->fs; if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ @@ -838,21 +1007,42 @@ DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluste wc = fs->win[bc++ % SS(fs)]; if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; wc |= fs->win[bc % SS(fs)] << 8; - val = clst & 1 ? wc >> 4 : (wc & 0xFFF); + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); break; case FS_FAT16 : if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; - p = &fs->win[clst * 2 % SS(fs)]; - val = LD_WORD(p); + val = ld_word(fs->win + clst * 2 % SS(fs)); break; case FS_FAT32 : if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; - p = &fs->win[clst * 4 % SS(fs)]; - val = LD_DWORD(p) & 0x0FFFFFFF; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; break; +#if _FS_EXFAT + case FS_EXFAT : + if (obj->objsize) { + DWORD cofs = clst - obj->sclust; /* Offset from start cluster */ + DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */ + if (obj->stat == 2) { /* Is there no valid chain on the FAT? */ + if (cofs <= clen) { + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* Generate the value */ + break; + } + } + if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the contiguous part? */ + val = clst + 1; /* Generate the value */ + break; + } + if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */ + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF; + break; + } + } + /* go next */ +#endif default: val = 1; /* Internal error */ } @@ -864,209 +1054,366 @@ DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluste +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* FAT access - Change value of a FAT entry */ /*-----------------------------------------------------------------------*/ -/* Hidden API for hacks and disk tools */ -#if !_FS_READONLY +static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ - FATFS* fs, /* File system object */ + FATFS* fs, /* Corresponding file system object */ DWORD clst, /* FAT index number (cluster number) to be changed */ DWORD val /* New value to be set to the entry */ ) { UINT bc; BYTE *p; - FRESULT res; + FRESULT res = FR_INT_ERR; - if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ - res = FR_INT_ERR; - - } else { + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ switch (fs->fs_type) { - case FS_FAT12 : + case FS_FAT12 : /* Bitfield items */ bc = (UINT)clst; bc += bc / 2; res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; - p = &fs->win[bc++ % SS(fs)]; + p = fs->win + bc++ % SS(fs); *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; fs->wflag = 1; res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; - p = &fs->win[bc % SS(fs)]; + p = fs->win + bc % SS(fs); *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); fs->wflag = 1; break; - case FS_FAT16 : + case FS_FAT16 : /* WORD aligned items */ res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); if (res != FR_OK) break; - p = &fs->win[clst * 2 % SS(fs)]; - ST_WORD(p, (WORD)val); + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); fs->wflag = 1; break; - case FS_FAT32 : + case FS_FAT32 : /* DWORD aligned items */ +#if _FS_EXFAT + case FS_EXFAT : +#endif res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); if (res != FR_OK) break; - p = &fs->win[clst * 4 % SS(fs)]; - val |= LD_DWORD(p) & 0xF0000000; - ST_DWORD(p, val); + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); + } + st_dword(fs->win + clst * 4 % SS(fs), val); fs->wflag = 1; break; - - default : - res = FR_INT_ERR; } } - return res; } + #endif /* !_FS_READONLY */ +#if _FS_EXFAT && !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* FAT handling - Remove a cluster chain */ +/* exFAT: Accessing FAT and Allocation Bitmap */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY + +/*---------------------------------------------*/ +/* exFAT: Find a contiguous free cluster block */ +/*---------------------------------------------*/ + static -FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ - FATFS* fs, /* File system object */ - DWORD clst /* Cluster# to remove a chain from */ +DWORD find_bitmap ( /* 0:No free cluster, 2..:Free cluster found, 0xFFFFFFFF:Disk error */ + FATFS* fs, /* File system object */ + DWORD clst, /* Cluster number to scan from */ + DWORD ncl /* Number of contiguous clusters to find (1..) */ +) +{ + BYTE bm, bv; + UINT i; + DWORD val, scl, ctr; + + + clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */ + if (clst >= fs->n_fatent - 2) clst = 0; + scl = val = clst; ctr = 0; + for (;;) { + if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; + i = val / 8 % SS(fs); bm = 1 << (val % 8); + do { + do { + bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */ + if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */ + val = 0; bm = 0; i = 4096; + } + if (!bv) { /* Is it a free cluster? */ + if (++ctr == ncl) return scl + 2; /* Check run length */ + } else { + scl = val; ctr = 0; /* Encountered a live cluster, restart to scan */ + } + if (val == clst) return 0; /* All cluster scanned? */ + } while (bm); + bm = 1; + } while (++i < SS(fs)); + } +} + + +/*------------------------------------*/ +/* exFAT: Set/Clear a block of bitmap */ +/*------------------------------------*/ + +static +FRESULT change_bitmap ( + FATFS* fs, /* File system object */ + DWORD clst, /* Cluster number to change from */ + DWORD ncl, /* Number of clusters to be changed */ + int bv /* bit value to be set (0 or 1) */ +) +{ + BYTE bm; + UINT i; + DWORD sect; + + + clst -= 2; /* The first bit corresponds to cluster #2 */ + sect = fs->database + clst / 8 / SS(fs); /* Sector address */ + i = clst / 8 % SS(fs); /* Byte offset in the sector */ + bm = 1 << (clst % 8); /* Bit mask in the byte */ + for (;;) { + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; + do { + do { + if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */ + fs->win[i] ^= bm; /* Flip the bit */ + fs->wflag = 1; + if (--ncl == 0) return FR_OK; /* All bits processed? */ + } while (bm <<= 1); /* Next bit */ + bm = 1; + } while (++i < SS(fs)); /* Next byte */ + } +} + + +/*---------------------------------------------*/ +/* Complement contiguous part of the FAT chain */ +/*---------------------------------------------*/ + +static +FRESULT fill_fat_chain ( + _FDID* obj /* Pointer to the corresponding object */ ) { FRESULT res; - DWORD nxt; -#if _USE_TRIM - DWORD scl = clst, ecl = clst, rt[2]; -#endif + DWORD cl, n; - if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ - res = FR_INT_ERR; - - } else { - res = FR_OK; - while (clst < fs->n_fatent) { /* Not a last link? */ - nxt = get_fat(fs, clst); /* Get cluster status */ - if (nxt == 0) break; /* Empty cluster? */ - if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ - if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ - res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */ - if (res != FR_OK) break; - if (fs->free_clust != 0xFFFFFFFF) { /* Update FSINFO */ - fs->free_clust++; - fs->fsi_flag |= 1; - } -#if _USE_TRIM - if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ - ecl = nxt; - } else { /* End of contiguous clusters */ - rt[0] = clust2sect(fs, scl); /* Start sector */ - rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ - disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Erase the block */ - scl = ecl = nxt; - } -#endif - clst = nxt; /* Next cluster */ + if (obj->stat == 3) { /* Has the object been changed 'fragmented'? */ + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */ + res = put_fat(obj->fs, cl, cl + 1); + if (res != FR_OK) return res; } + obj->stat = 0; /* Change status 'FAT chain is valid' */ + } + return FR_OK; +} + +#endif /* _FS_EXFAT && !_FS_READONLY */ + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +static +FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ + _FDID* obj, /* Corresponding object */ + DWORD clst, /* Cluster to remove a chain from */ + DWORD pclst /* Previous cluster of clst (0:an entire chain) */ +) +{ + FRESULT res = FR_OK; + DWORD nxt; + FATFS *fs = obj->fs; +#if _FS_EXFAT || _USE_TRIM + DWORD scl = clst, ecl = clst; +#endif +#if _USE_TRIM + DWORD rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */ + + /* Mark the previous cluster 'EOC' on the FAT if it exists */ + if (pclst && (!_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) { + res = put_fat(fs, pclst, 0xFFFFFFFF); + if (res != FR_OK) return res; } - return res; -} + /* Remove the chain */ + do { + nxt = get_fat(obj, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ + if (res != FR_OK) return res; + } + if (fs->free_clst != 0xFFFFFFFF) { /* Update FSINFO */ + fs->free_clst++; + fs->fsi_flag |= 1; + } +#if _FS_EXFAT || _USE_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous cluster block */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */ + if (res != FR_OK) return res; + } #endif +#if _USE_TRIM + rt[0] = clust2sect(fs, scl); /* Start sector */ + rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Inform device the block can be erased */ +#endif + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } while (clst < fs->n_fatent); /* Repeat while not the last link */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + if (pclst == 0) { /* Does object have no chain? */ + obj->stat = 0; /* Change the object status 'initial' */ + } else { + if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Did the chain got contiguous? */ + obj->stat = 2; /* Change the object status 'contiguous' */ + } + } + } +#endif + return FR_OK; +} /*-----------------------------------------------------------------------*/ -/* FAT handling - Stretch or Create a cluster chain */ +/* FAT handling - Stretch a chain or Create a new chain */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY static DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ - FATFS* fs, /* File system object */ + _FDID* obj, /* Corresponding object */ DWORD clst /* Cluster# to stretch, 0:Create a new chain */ ) { DWORD cs, ncl, scl; FRESULT res; + FATFS *fs = obj->fs; - if (clst == 0) { /* Create a new chain */ - scl = fs->last_clust; /* Get suggested start point */ - if (!scl || scl >= fs->n_fatent) scl = 1; + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clst; /* Get suggested cluster to start at */ + if (scl == 0 || scl >= fs->n_fatent) scl = 1; } - else { /* Stretch the current chain */ - cs = get_fat(fs, clst); /* Check the cluster status */ - if (cs < 2) return 1; /* Invalid value */ + else { /* Stretch current chain */ + cs = get_fat(obj, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* Invalid value */ if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */ if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ scl = clst; } - ncl = scl; /* Start cluster */ - for (;;) { - ncl++; /* Next cluster */ - if (ncl >= fs->n_fatent) { /* Check wrap around */ - ncl = 2; - if (ncl > scl) return 0; /* No free cluster */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */ + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */ + res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */ + if (res == FR_INT_ERR) return 1; + if (res == FR_DISK_ERR) return 0xFFFFFFFF; + if (clst == 0) { /* Is it a new chain? */ + obj->stat = 2; /* Set status 'contiguous chain' */ + } else { /* This is a stretched chain */ + if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */ + obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */ + obj->stat = 3; /* Change status 'just fragmented' */ + } + } + } else +#endif + { /* At the FAT12/16/32 */ + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Check wrap-around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster */ + } + cs = get_fat(obj, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* An error occurred */ + if (ncl == scl) return 0; /* No free cluster */ } - cs = get_fat(fs, ncl); /* Get the cluster status */ - if (cs == 0) break; /* Found a free cluster */ - if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */ - return cs; - if (ncl == scl) return 0; /* No free cluster */ } - res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */ - if (res == FR_OK && clst != 0) { - res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */ - } - if (res == FR_OK) { - fs->last_clust = ncl; /* Update FSINFO */ - if (fs->free_clust != 0xFFFFFFFF) { - fs->free_clust--; - fs->fsi_flag |= 1; - } + if (_FS_EXFAT && fs->fs_type == FS_EXFAT && obj->stat == 2) { /* Is it a contiguous chain? */ + res = FR_OK; /* FAT does not need to be written */ } else { - ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */ + if (res == FR_OK && clst) { + res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */ + } } - return ncl; /* Return new cluster number or error code */ + if (res == FR_OK) { /* Update FSINFO if function succeeded. */ + fs->last_clst = ncl; + if (fs->free_clst < fs->n_fatent - 2) fs->free_clst--; + fs->fsi_flag |= 1; + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Create error status */ + } + + return ncl; /* Return new cluster number or error status */ } + #endif /* !_FS_READONLY */ +#if _USE_FASTSEEK /*-----------------------------------------------------------------------*/ /* FAT handling - Convert offset into cluster with link map table */ /*-----------------------------------------------------------------------*/ -#if _USE_FASTSEEK static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ FIL* fp, /* Pointer to the file object */ - DWORD ofs /* File offset to be converted to cluster# */ + FSIZE_t ofs /* File offset to be converted to cluster# */ ) { DWORD cl, ncl, *tbl; + FATFS *fs = fp->obj.fs; tbl = fp->cltbl + 1; /* Top of CLMT */ - cl = ofs / SS(fp->fs) / fp->fs->csize; /* Cluster order from top of the file */ + cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */ for (;;) { ncl = *tbl++; /* Number of cluters in the fragment */ - if (!ncl) return 0; /* End of table? (error) */ + if (ncl == 0) return 0; /* End of table? (error) */ if (cl < ncl) break; /* In this fragment? */ cl -= ncl; tbl++; /* Next fragment */ } return cl + *tbl; /* Return the cluster number */ } + #endif /* _USE_FASTSEEK */ @@ -1079,40 +1426,41 @@ DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ DIR* dp, /* Pointer to directory object */ - UINT idx /* Index of directory table */ + DWORD ofs /* Offset of directory table */ ) { - DWORD clst, sect; - UINT ic; + DWORD csz, clst; + FATFS *fs = dp->obj.fs; - dp->index = (WORD)idx; /* Current index */ - clst = dp->sclust; /* Table start cluster (0:root) */ - if (clst == 1 || clst >= dp->fs->n_fatent) /* Check start cluster range */ + if (ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */ return FR_INT_ERR; - if (!clst && dp->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ - clst = dp->fs->dirbase; + } + dp->dptr = ofs; /* Set current offset */ + clst = dp->obj.sclust; /* Table start cluster (0:root) */ + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */ + clst = fs->dirbase; + if (_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */ + } if (clst == 0) { /* Static table (root-directory in FAT12/16) */ - if (idx >= dp->fs->n_rootdir) /* Is index out of range? */ - return FR_INT_ERR; - sect = dp->fs->dirbase; - } - else { /* Dynamic table (root-directory in FAT32 or sub-directory) */ - ic = SS(dp->fs) / SZ_DIRE * dp->fs->csize; /* Entries per cluster */ - while (idx >= ic) { /* Follow cluster chain */ - clst = get_fat(dp->fs, clst); /* Get next cluster */ + if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */ + dp->sect = fs->dirbase; + + } else { /* Dynamic table (sub-directory or root-directory in FAT32+) */ + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ + while (ofs >= csz) { /* Follow cluster chain */ + clst = get_fat(&dp->obj, clst); /* Get next cluster */ if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ - if (clst < 2 || clst >= dp->fs->n_fatent) /* Reached to end of table or internal error */ - return FR_INT_ERR; - idx -= ic; + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */ + ofs -= csz; } - sect = clust2sect(dp->fs, clst); + dp->sect = clust2sect(fs, clst); } - dp->clust = clst; /* Current cluster# */ - if (!sect) return FR_INT_ERR; - dp->sect = sect + idx / (SS(dp->fs) / SZ_DIRE); /* Sector# of the directory entry */ - dp->dir = dp->fs->win + (idx % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE; /* Ptr to the entry in the sector */ + dp->clust = clst; /* Current cluster# */ + if (!dp->sect) return FR_INT_ERR; + dp->sect += ofs / SS(fs); /* Sector# of the directory entry */ + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */ return FR_OK; } @@ -1130,59 +1478,58 @@ FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Cou int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ ) { - DWORD clst; - UINT i; + DWORD ofs, clst; + FATFS *fs = dp->obj.fs; #if !_FS_READONLY - UINT c; + UINT n; #endif + ofs = dp->dptr + SZDIRE; /* Next entry */ + if (!dp->sect || ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) return FR_NO_FILE; /* Report EOT when offset has reached max value */ - i = dp->index + 1; - if (!(i & 0xFFFF) || !dp->sect) /* Report EOT when index has reached 65535 */ - return FR_NO_FILE; - - if (!(i % (SS(dp->fs) / SZ_DIRE))) { /* Sector changed? */ - dp->sect++; /* Next sector */ + if (ofs % SS(fs) == 0) { /* Sector changed? */ + dp->sect++; /* Next sector */ if (!dp->clust) { /* Static table */ - if (i >= dp->fs->n_rootdir) /* Report EOT if it reached end of static table */ - return FR_NO_FILE; + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */ + dp->sect = 0; return FR_NO_FILE; + } } else { /* Dynamic table */ - if (((i / (SS(dp->fs) / SZ_DIRE)) & (dp->fs->csize - 1)) == 0) { /* Cluster changed? */ - clst = get_fat(dp->fs, dp->clust); /* Get next cluster */ - if (clst <= 1) return FR_INT_ERR; - if (clst == 0xFFFFFFFF) return FR_DISK_ERR; - if (clst >= dp->fs->n_fatent) { /* If it reached end of dynamic table, */ + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst >= fs->n_fatent) { /* Reached end of dynamic table */ #if !_FS_READONLY - if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT */ - clst = create_chain(dp->fs, dp->clust); /* Stretch cluster chain */ - if (clst == 0) return FR_DENIED; /* No free cluster */ - if (clst == 1) return FR_INT_ERR; - if (clst == 0xFFFFFFFF) return FR_DISK_ERR; - /* Clean-up stretched table */ - if (sync_window(dp->fs)) return FR_DISK_ERR;/* Flush disk access window */ - mem_set(dp->fs->win, 0, SS(dp->fs)); /* Clear window buffer */ - dp->fs->winsect = clust2sect(dp->fs, clst); /* Cluster start sector */ - for (c = 0; c < dp->fs->csize; c++) { /* Fill the new cluster with 0 */ - dp->fs->wflag = 1; - if (sync_window(dp->fs)) return FR_DISK_ERR; - dp->fs->winsect++; + if (!stretch) { /* If no stretch, report EOT */ + dp->sect = 0; return FR_NO_FILE; } - dp->fs->winsect -= c; /* Rewind window offset */ + clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + /* Clean-up the stretched table */ + if (_FS_EXFAT) dp->obj.stat |= 4; /* The directory needs to be updated */ + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ + mem_set(fs->win, 0, SS(fs)); /* Clear window buffer */ + for (n = 0, fs->winsect = clust2sect(fs, clst); n < fs->csize; n++, fs->winsect++) { /* Fill the new cluster with 0 */ + fs->wflag = 1; + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; + } + fs->winsect -= n; /* Restore window offset */ #else - if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT (this is to suppress warning) */ - return FR_NO_FILE; /* Report EOT */ + if (!stretch) dp->sect = 0; /* If no stretch, report EOT (this is to suppress warning) */ + dp->sect = 0; return FR_NO_FILE; /* Report EOT */ #endif } - dp->clust = clst; /* Initialize data for new cluster */ - dp->sect = clust2sect(dp->fs, clst); + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clust2sect(fs, clst); } } } - - dp->index = (WORD)i; /* Current index */ - dp->dir = dp->fs->win + (i % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE; /* Current entry in the window */ + dp->dptr = ofs; /* Current entry */ + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */ return FR_OK; } @@ -1190,58 +1537,66 @@ FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Cou +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Directory handling - Reserve directory entry */ +/* Directory handling - Reserve a block of directory entries */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ DIR* dp, /* Pointer to the directory object */ - UINT nent /* Number of contiguous entries to allocate (1-21) */ + UINT nent /* Number of contiguous entries to allocate */ ) { FRESULT res; UINT n; + FATFS *fs = dp->obj.fs; res = dir_sdi(dp, 0); if (res == FR_OK) { n = 0; do { - res = move_window(dp->fs, dp->sect); + res = move_window(fs, dp->sect); if (res != FR_OK) break; - if (dp->dir[0] == DDEM || dp->dir[0] == 0) { /* Is it a free entry? */ +#if _FS_EXFAT + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { +#else + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { +#endif if (++n == nent) break; /* A block of contiguous free entries is found */ } else { n = 0; /* Not a blank entry. Restart to search */ } - res = dir_next(dp, 1); /* Next entry with table stretch enabled */ - } while (res == FR_OK); + res = dir_next(dp, 1); + } while (res == FR_OK); /* Next entry with table stretch enabled */ } + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ return res; } -#endif + +#endif /* !_FS_READONLY */ /*-----------------------------------------------------------------------*/ -/* Directory handling - Load/Store start cluster number */ +/* FAT: Directory handling - Load/Store start cluster number */ /*-----------------------------------------------------------------------*/ static DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ FATFS* fs, /* Pointer to the fs object */ - const BYTE* dir /* Pointer to the SFN entry */ + const BYTE* dir /* Pointer to the key entry */ ) { DWORD cl; - cl = LD_WORD(dir + DIR_FstClusLO); - if (fs->fs_type == FS_FAT32) - cl |= (DWORD)LD_WORD(dir + DIR_FstClusHI) << 16; + cl = ld_word(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) { + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; + } return cl; } @@ -1250,59 +1605,67 @@ DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ #if !_FS_READONLY static void st_clust ( - BYTE* dir, /* Pointer to the SFN entry */ + FATFS* fs, /* Pointer to the fs object */ + BYTE* dir, /* Pointer to the key entry */ DWORD cl /* Value to be set */ ) { - ST_WORD(dir + DIR_FstClusLO, cl); - ST_WORD(dir + DIR_FstClusHI, cl >> 16); + st_word(dir + DIR_FstClusLO, (WORD)cl); + if (fs->fs_type == FS_FAT32) { + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); + } } #endif - -/*-----------------------------------------------------------------------*/ -/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ -/*-----------------------------------------------------------------------*/ -#if _USE_LFN +#if _USE_LFN != 0 +/*------------------------------------------------------------------------*/ +/* FAT-LFN: LFN handling */ +/*------------------------------------------------------------------------*/ static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */ +/*--------------------------------------------------------*/ +/* FAT-LFN: Compare a part of file name with an LFN entry */ +/*--------------------------------------------------------*/ static -int cmp_lfn ( /* 1:matched, 0:not matched */ - WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ - BYTE* dir /* Pointer to the directory entry containing the part of LFN */ +int cmp_lfn ( /* 1:matched, 0:not matched */ + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ + BYTE* dir /* Pointer to the directory entry containing the part of LFN */ ) { UINT i, s; WCHAR wc, uc; - if (LD_WORD(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ - uc = LD_WORD(dir + LfnOfs[s]); /* Pick an LFN character */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ if (wc) { - if (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + if (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ return 0; /* Not matched */ + } wc = uc; } else { if (uc != 0xFFFF) return 0; /* Check filler */ } } - if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) /* Last segment matched but different length */ - return 0; + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */ return 1; /* The part of LFN matched */ } - +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT +/*-----------------------------------------------------*/ +/* FAT-LFN: Pick a part of file name from an LFN entry */ +/*-----------------------------------------------------*/ static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ @@ -1313,12 +1676,12 @@ int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ WCHAR wc, uc; - if (LD_WORD(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ - uc = LD_WORD(dir + LfnOfs[s]); /* Pick an LFN character */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ if (wc) { if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ lfnbuf[i++] = wc = uc; /* Store it */ @@ -1334,15 +1697,19 @@ int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ return 1; /* The part of LFN is valid */ } +#endif #if !_FS_READONLY +/*-----------------------------------------*/ +/* FAT-LFN: Create an entry of LFN entries */ +/*-----------------------------------------*/ static -void fit_lfn ( - const WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ - BYTE* dir, /* Pointer to the LFN entry to be processed */ - BYTE ord, /* LFN order (1-20) */ - BYTE sum /* Checksum of the corresponding SFN */ +void put_lfn ( + const WCHAR* lfn, /* Pointer to the LFN */ + BYTE* dir, /* Pointer to the LFN entry to be created */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* Checksum of the corresponding SFN */ ) { UINT i, s; @@ -1352,29 +1719,29 @@ void fit_lfn ( dir[LDIR_Chksum] = sum; /* Set checksum */ dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ dir[LDIR_Type] = 0; - ST_WORD(dir + LDIR_FstClusLO, 0); + st_word(dir + LDIR_FstClusLO, 0); i = (ord - 1) * 13; /* Get offset in the LFN working buffer */ s = wc = 0; do { - if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective character */ - ST_WORD(dir+LfnOfs[s], wc); /* Put it */ - if (!wc) wc = 0xFFFF; /* Padding characters following last character */ + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ + st_word(dir + LfnOfs[s], wc); /* Put it */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */ } while (++s < 13); - if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLEF; /* Bottom LFN part is the start of LFN sequence */ + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ dir[LDIR_Ord] = ord; /* Set the LFN order */ } -#endif -#endif - +#endif /* !_FS_READONLY */ +#endif /* _USE_LFN != 0 */ +#if _USE_LFN != 0 && !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Create numbered name */ +/* FAT-LFN: Create a Numbered SFN */ /*-----------------------------------------------------------------------*/ -#if _USE_LFN + static void gen_numname ( BYTE* dst, /* Pointer to the buffer to store numbered SFN */ @@ -1391,7 +1758,7 @@ void gen_numname ( mem_cpy(dst, src, 11); - if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ + if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ sr = seq; while (*lfn) { /* Create a CRC */ wc = *lfn++; @@ -1425,15 +1792,15 @@ void gen_numname ( dst[j++] = (i < 8) ? ns[i++] : ' '; } while (j < 8); } -#endif - +#endif /* _USE_LFN != 0 && !_FS_READONLY */ +#if _USE_LFN != 0 /*-----------------------------------------------------------------------*/ -/* Calculate checksum of an SFN entry */ +/* FAT-LFN: Calculate checksum of an SFN entry */ /*-----------------------------------------------------------------------*/ -#if _USE_LFN + static BYTE sum_sfn ( const BYTE* dir /* Pointer to the SFN entry */ @@ -1445,9 +1812,350 @@ BYTE sum_sfn ( do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); return sum; } + +#endif /* _USE_LFN != 0 */ + + + +#if _FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* exFAT: Checksum */ +/*-----------------------------------------------------------------------*/ + +static +WORD xdir_sum ( /* Get checksum of the directoly block */ + const BYTE* dir /* Directory entry block to be calculated */ +) +{ + UINT i, szblk; + WORD sum; + + + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; + for (i = sum = 0; i < szblk; i++) { + if (i == XDIR_SetSum) { /* Skip sum field */ + i++; + } else { + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; + } + } + return sum; +} + + + +static +WORD xname_sum ( /* Get check sum (to be used as hash) of the name */ + const WCHAR* name /* File name to be calculated */ +) +{ + WCHAR chr; + WORD sum = 0; + + + while ((chr = *name++) != 0) { + chr = ff_wtoupper(chr); /* File name needs to be ignored case */ + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); + } + return sum; +} + + +#if !_FS_READONLY && _USE_MKFS +static +DWORD xsum32 ( + BYTE dat, /* Data to be sumed */ + DWORD sum /* Previous value */ +) +{ + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; + return sum; +} #endif +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +/*------------------------------------------------------*/ +/* exFAT: Get object information from a directory block */ +/*------------------------------------------------------*/ + +static +void get_xdir_info ( + BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ + FILINFO* fno /* Buffer to store the extracted file information */ +) +{ + UINT di, si; + WCHAR w; +#if !_LFN_UNICODE + UINT nc; +#endif + + /* Get file name */ +#if _LFN_UNICODE + if (dirb[XDIR_NumName] <= _MAX_LFN) { + for (si = SZDIRE * 2, di = 0; di < dirb[XDIR_NumName]; si += 2, di++) { + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + w = ld_word(dirb + si); /* Get a character */ + fno->fname[di] = w; /* Store it */ + } + } else { + di = 0; /* Buffer overflow and inaccessible object */ + } +#else + for (si = SZDIRE * 2, di = nc = 0; nc < dirb[XDIR_NumName]; si += 2, nc++) { + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + w = ld_word(dirb + si); /* Get a character */ + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) { di = 0; break; } /* Could not be converted and inaccessible object */ + if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */ + fno->fname[di++] = (char)(w >> 8); + } + if (di >= _MAX_LFN) { di = 0; break; } /* Buffer overflow and inaccessible object */ + fno->fname[di++] = (char)w; + } +#endif + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object? */ + fno->fname[di] = 0; /* Terminate file name */ + + fno->altname[0] = 0; /* No SFN */ + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ +} + +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + +/*-----------------------------------*/ +/* exFAT: Get a directry entry block */ +/*-----------------------------------*/ + +static +FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ + DIR* dp /* Pointer to the reading direcotry object pointing the 85 entry */ +) +{ + FRESULT res; + UINT i, nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + + + /* Load 85 entry */ + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR; + mem_cpy(dirb, dp->dir, SZDIRE); + nent = dirb[XDIR_NumSec] + 1; + + /* Load C0 entry */ + res = dir_next(dp, 0); + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC0) return FR_INT_ERR; + mem_cpy(dirb + SZDIRE, dp->dir, SZDIRE); + + /* Load C1 entries */ + if (nent < 3 || nent > 19) return FR_NO_FILE; + i = SZDIRE * 2; nent *= SZDIRE; + do { + res = dir_next(dp, 0); + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC1) return FR_INT_ERR; + mem_cpy(dirb + i, dp->dir, SZDIRE); + i += SZDIRE; + } while (i < nent); + + /* Sanity check */ + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR; + + return FR_OK; +} + + +#if !_FS_READONLY || _FS_RPATH != 0 +/*------------------------------------------------*/ +/* exFAT: Load the object's directory entry block */ +/*------------------------------------------------*/ +static +FRESULT load_obj_dir ( + DIR* dp, /* Blank directory object to be used to access containing direcotry */ + const _FDID* obj /* Object with containing directory information */ +) +{ + FRESULT res; + + + /* Open object containing directory */ + dp->obj.fs = obj->fs; + dp->obj.sclust = obj->c_scl; + dp->obj.stat = (BYTE)obj->c_size; + dp->obj.objsize = obj->c_size & 0xFFFFFF00; + dp->blk_ofs = obj->c_ofs; + + res = dir_sdi(dp, dp->blk_ofs); /* Goto the block location */ + if (res == FR_OK) { + res = load_xdir(dp); /* Load the object's entry block */ + } + return res; +} +#endif + + +#if !_FS_READONLY +/*-----------------------------------------------*/ +/* exFAT: Store the directory block to the media */ +/*-----------------------------------------------*/ +static +FRESULT store_xdir ( + DIR* dp /* Pointer to the direcotry object */ +) +{ + FRESULT res; + UINT nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + + /* Create set sum */ + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); + nent = dirb[XDIR_NumSec] + 1; + + /* Store the set of directory to the volume */ + res = dir_sdi(dp, dp->blk_ofs); + while (res == FR_OK) { + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) break; + mem_cpy(dp->dir, dirb, SZDIRE); + dp->obj.fs->wflag = 1; + if (--nent == 0) break; + dirb += SZDIRE; + res = dir_next(dp, 0); + } + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; +} + + + +/*-------------------------------------------*/ +/* exFAT: Create a new directory enrty block */ +/*-------------------------------------------*/ + +static +void create_xdir ( + BYTE* dirb, /* Pointer to the direcotry entry block buffer */ + const WCHAR* lfn /* Pointer to the nul terminated file name */ +) +{ + UINT i; + BYTE nb, nc; + WCHAR chr; + + + mem_set(dirb, 0, 2 * SZDIRE); /* Initialize 85+C0 entry */ + dirb[XDIR_Type] = 0x85; + dirb[XDIR_Type + SZDIRE] = 0xC0; + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ + + i = SZDIRE * 2; /* C1 offset */ + nc = 0; nb = 1; chr = 1; + do { + dirb[i++] = 0xC1; dirb[i++] = 0; /* Entry type C1 */ + do { /* Fill name field */ + if (chr && (chr = lfn[nc]) != 0) nc++; /* Get a character if exist */ + st_word(dirb + i, chr); i += 2; /* Store it */ + } while (i % SZDIRE); + nb++; + } while (lfn[nc]); /* Fill next entry if any char follows */ + + dirb[XDIR_NumName] = nc; /* Set name length */ + dirb[XDIR_NumSec] = nb; /* Set number of C0+C1s */ +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_EXFAT */ + + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_read ( + DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ +) +{ + FRESULT res = FR_NO_FILE; + FATFS *fs = dp->obj.fs; + BYTE a, c; +#if _USE_LFN != 0 + BYTE ord = 0xFF, sum = 0xFF; +#endif + + while (dp->sect) { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; /* Test for the entry type */ + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of the directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + if (_USE_LABEL && vol) { + if (c == 0x83) break; /* Volume label entry? */ + } else { + if (c == 0x85) { /* Start of the file entry block? */ + dp->blk_ofs = dp->dptr; /* Get location of the block */ + res = load_xdir(dp); /* Load the entry block */ + if (res == FR_OK) { + dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */ + } + break; + } + } + } else +#endif + { /* At the FAT12/16/32 */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */ +#if _USE_LFN != 0 /* LFN configuration */ + if (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & LLEF) { /* Is it start of an LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= ~LLEF; ord = c; + dp->blk_ofs = dp->dptr; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + } + break; + } + } +#else /* Non LFN configuration */ + if (c != DDEM && c != '.' && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */ + break; + } +#endif + } + res = dir_next(dp, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */ + return res; +} + +#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ + /*-----------------------------------------------------------------------*/ @@ -1456,53 +2164,70 @@ BYTE sum_sfn ( static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ - DIR* dp /* Pointer to the directory object linked to the file name */ + DIR* dp /* Pointer to the directory object with the file name */ ) { FRESULT res; - BYTE c, *dir; -#if _USE_LFN + FATFS *fs = dp->obj.fs; + BYTE c; +#if _USE_LFN != 0 BYTE a, ord, sum; #endif res = dir_sdi(dp, 0); /* Rewind directory object */ if (res != FR_OK) return res; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + BYTE nc; + UINT di, ni; + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ -#if _USE_LFN - ord = sum = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + while ((res = dir_read(dp, 0)) == FR_OK) { /* Read an item */ + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip the comparison if hash value mismatched */ + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ + if ((di % SZDIRE) == 0) di += 2; + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; + } + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ + } + return res; + } +#endif + /* At the FAT12/16/32 */ +#if _USE_LFN != 0 + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ #endif do { - res = move_window(dp->fs, dp->sect); + res = move_window(fs, dp->sect); if (res != FR_OK) break; - dir = dp->dir; /* Ptr to the directory entry of current index */ - c = dir[DIR_Name]; + c = dp->dir[DIR_Name]; if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ -#if _USE_LFN /* LFN configuration */ - a = dir[DIR_Attr] & AM_MASK; +#if _USE_LFN != 0 /* LFN configuration */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ - ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } else { if (a == AM_LFN) { /* An LFN entry is found */ - if (dp->lfn) { + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { if (c & LLEF) { /* Is it start of LFN sequence? */ - sum = dir[LDIR_Chksum]; + sum = dp->dir[LDIR_Chksum]; c &= ~LLEF; ord = c; /* LFN start order */ - dp->lfn_idx = dp->index; /* Start index of LFN */ + dp->blk_ofs = dp->dptr; /* Start offset of LFN */ } /* Check validity of the LFN entry and compare it with given name */ - ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF; + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; } } else { /* An SFN entry is found */ - if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ - if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dir, dp->fn, 11)) break; /* SFN matched? */ - ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + if (!ord && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } } #else /* Non LFN configuration */ - if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dp->fn, 11)) /* Is it a valid entry? */ - break; + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ #endif - res = dir_next(dp, 0); /* Next entry */ + res = dir_next(dp, 0); /* Next entry */ } while (res == FR_OK); return res; @@ -1511,288 +2236,290 @@ FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ -/*-----------------------------------------------------------------------*/ -/* Read an object from the directory */ -/*-----------------------------------------------------------------------*/ -#if _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 -static -FRESULT dir_read ( - DIR* dp, /* Pointer to the directory object */ - int vol /* Filtered by 0:file/directory or 1:volume label */ -) -{ - FRESULT res; - BYTE a, c, *dir; -#if _USE_LFN - BYTE ord = 0xFF, sum = 0xFF; -#endif - - res = FR_NO_FILE; - while (dp->sect) { - res = move_window(dp->fs, dp->sect); - if (res != FR_OK) break; - dir = dp->dir; /* Ptr to the directory entry of current index */ - c = dir[DIR_Name]; - if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ - a = dir[DIR_Attr] & AM_MASK; -#if _USE_LFN /* LFN configuration */ - if (c == DDEM || (!_FS_RPATH && c == '.') || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ - ord = 0xFF; - } else { - if (a == AM_LFN) { /* An LFN entry is found */ - if (c & LLEF) { /* Is it start of LFN sequence? */ - sum = dir[LDIR_Chksum]; - c &= ~LLEF; ord = c; - dp->lfn_idx = dp->index; - } - /* Check LFN validity and capture it */ - ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF; - } else { /* An SFN entry is found */ - if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ - dp->lfn_idx = 0xFFFF; /* It has no LFN. */ - break; - } - } -#else /* Non LFN configuration */ - if (c != DDEM && (_FS_RPATH || c != '.') && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) /* Is it a valid entry? */ - break; -#endif - res = dir_next(dp, 0); /* Next entry */ - if (res != FR_OK) break; - } - - if (res != FR_OK) dp->sect = 0; - - return res; -} -#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ - - - - +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Register an object to the directory */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY + static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */ DIR* dp /* Target directory with object name to be created */ ) { FRESULT res; -#if _USE_LFN /* LFN configuration */ - UINT n, nent; - BYTE sn[12], *fn, sum; - WCHAR *lfn; + FATFS *fs = dp->obj.fs; +#if _USE_LFN != 0 /* LFN configuration */ + UINT n, nlen, nent; + BYTE sn[12], sum; - fn = dp->fn; lfn = dp->lfn; - mem_cpy(sn, fn, 12); + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ + for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ - if (_FS_RPATH && (sn[NSFLAG] & NS_DOT)) /* Cannot create dot entry */ - return FR_INVALID_NAME; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + DIR dj; + nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res != FR_OK) return res; + dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set block position */ + + if (dp->obj.stat & 4) { /* Has the sub-directory been stretched? */ + dp->obj.stat &= 3; + dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */ + res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */ + if (res != FR_OK) return res; + res = load_obj_dir(&dj, &dp->obj); + if (res != FR_OK) return res; /* Load the object status */ + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */ + st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; + res = store_xdir(&dj); /* Store the object status */ + if (res != FR_OK) return res; + } + + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */ + return FR_OK; + } +#endif + /* At the FAT12/16/32 */ + mem_cpy(sn, dp->fn, 12); if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ - fn[NSFLAG] = 0; dp->lfn = 0; /* Find only SFN */ + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ for (n = 1; n < 100; n++) { - gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */ res = dir_find(dp); /* Check if the name collides with existing SFN */ if (res != FR_OK) break; } if (n == 100) return FR_DENIED; /* Abort if too many collisions */ if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ - fn[NSFLAG] = sn[NSFLAG]; dp->lfn = lfn; + dp->fn[NSFLAG] = sn[NSFLAG]; } - if (sn[NSFLAG] & NS_LFN) { /* When LFN is to be created, allocate entries for an SFN + LFNs. */ - for (n = 0; lfn[n]; n++) ; - nent = (n + 25) / 13; - } else { /* Otherwise allocate an entry for an SFN */ - nent = 1; - } + /* Create an SFN with/without LFNs. */ + nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */ res = dir_alloc(dp, nent); /* Allocate entries */ - if (res == FR_OK && --nent) { /* Set LFN entry if needed */ - res = dir_sdi(dp, dp->index - nent); + res = dir_sdi(dp, dp->dptr - nent * SZDIRE); if (res == FR_OK) { sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ do { /* Store LFN entries in bottom first */ - res = move_window(dp->fs, dp->sect); + res = move_window(fs, dp->sect); if (res != FR_OK) break; - fit_lfn(dp->lfn, dp->dir, (BYTE)nent, sum); - dp->fs->wflag = 1; + put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); + fs->wflag = 1; res = dir_next(dp, 0); /* Next entry */ } while (res == FR_OK && --nent); } } + #else /* Non LFN configuration */ res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ + #endif - if (res == FR_OK) { /* Set SFN entry */ - res = move_window(dp->fs, dp->sect); + /* Set SFN entry */ + if (res == FR_OK) { + res = move_window(fs, dp->sect); if (res == FR_OK) { - mem_set(dp->dir, 0, SZ_DIRE); /* Clean the entry */ - mem_cpy(dp->dir, dp->fn, 11); /* Put SFN */ -#if _USE_LFN + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ +#if _USE_LFN != 0 dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ #endif - dp->fs->wflag = 1; + fs->wflag = 1; } } return res; } + #endif /* !_FS_READONLY */ - +#if !_FS_READONLY && _FS_MINIMIZE == 0 /*-----------------------------------------------------------------------*/ /* Remove an object from the directory */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY && !_FS_MINIMIZE + static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ DIR* dp /* Directory object pointing the entry to be removed */ ) { FRESULT res; -#if _USE_LFN /* LFN configuration */ - UINT i; + FATFS *fs = dp->obj.fs; +#if _USE_LFN != 0 /* LFN configuration */ + DWORD last = dp->dptr; - i = dp->index; /* SFN index */ - res = dir_sdi(dp, (dp->lfn_idx == 0xFFFF) ? i : dp->lfn_idx); /* Goto the SFN or top of the LFN entries */ + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ if (res == FR_OK) { do { - res = move_window(dp->fs, dp->sect); + res = move_window(fs, dp->sect); if (res != FR_OK) break; - mem_set(dp->dir, 0, SZ_DIRE); /* Clear and mark the entry "deleted" */ - *dp->dir = DDEM; - dp->fs->wflag = 1; - if (dp->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ - res = dir_next(dp, 0); /* Next entry */ + /* Mark an entry 'deleted' */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* At the exFAT */ + dp->dir[XDIR_Type] &= 0x7F; + } else { /* At the FAT12/16/32 */ + dp->dir[DIR_Name] = DDEM; + } + fs->wflag = 1; + if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */ + res = dir_next(dp, 0); /* Next entry */ } while (res == FR_OK); if (res == FR_NO_FILE) res = FR_INT_ERR; } - #else /* Non LFN configuration */ - res = dir_sdi(dp, dp->index); + + res = move_window(fs, dp->sect); if (res == FR_OK) { - res = move_window(dp->fs, dp->sect); - if (res == FR_OK) { - mem_set(dp->dir, 0, SZ_DIRE); /* Clear and mark the entry "deleted" */ - *dp->dir = DDEM; - dp->fs->wflag = 1; - } + dp->dir[DIR_Name] = DDEM; + fs->wflag = 1; } #endif return res; } -#endif /* !_FS_READONLY */ - + +#endif /* !_FS_READONLY && _FS_MINIMIZE == 0 */ +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 /*-----------------------------------------------------------------------*/ /* Get file information from directory entry */ /*-----------------------------------------------------------------------*/ -#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 + static void get_fileinfo ( /* No return code */ DIR* dp, /* Pointer to the directory object */ FILINFO* fno /* Pointer to the file information to be filled */ ) { - UINT i; - TCHAR *p, c; - BYTE *dir; -#if _USE_LFN - WCHAR w, *lfn; + UINT i, j; + TCHAR c; + DWORD tm; +#if _USE_LFN != 0 + WCHAR w, lfv; + FATFS *fs = dp->obj.fs; #endif - p = fno->fname; - if (dp->sect) { /* Get SFN */ - dir = dp->dir; - i = 0; - while (i < 11) { /* Copy name body and extension */ - c = (TCHAR)dir[i++]; - if (c == ' ') continue; /* Skip padding spaces */ - if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ - if (i == 9) *p++ = '.'; /* Insert a . if extension is exist */ -#if _USE_LFN - if (IsUpper(c) && (dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY))) - c += 0x20; /* To lower */ -#if _LFN_UNICODE - if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dir[i])) - c = c << 8 | dir[i++]; - c = ff_convert(c, 1); /* OEM -> Unicode */ - if (!c) c = '?'; -#endif -#endif - *p++ = c; - } - fno->fattrib = dir[DIR_Attr]; /* Attribute */ - fno->fsize = LD_DWORD(dir + DIR_FileSize); /* Size */ - fno->fdate = LD_WORD(dir + DIR_WrtDate); /* Date */ - fno->ftime = LD_WORD(dir + DIR_WrtTime); /* Time */ - } - *p = 0; /* Terminate SFN string by a \0 */ -#if _USE_LFN - if (fno->lfname) { - i = 0; p = fno->lfname; - if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) { /* Get LFN if available */ - lfn = dp->lfn; - while ((w = *lfn++) != 0) { /* Get an LFN character */ + fno->fname[0] = 0; /* Invaidate file info */ + if (!dp->sect) return; /* Exit if read pointer has reached end of directory */ + +#if _USE_LFN != 0 /* LFN configuration */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + get_xdir_info(fs->dirbuf, fno); + return; + } else +#endif + { /* At the FAT12/16/32 */ + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ + i = j = 0; + while ((w = fs->lfnbuf[j++]) != 0) { /* Get an LFN character */ #if !_LFN_UNICODE w = ff_convert(w, 0); /* Unicode -> OEM */ - if (!w) { i = 0; break; } /* No LFN if it could not be converted */ - if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ - p[i++] = (TCHAR)(w >> 8); + if (w == 0) { i = 0; break; } /* No LFN if it could not be converted */ + if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */ + fno->fname[i++] = (char)(w >> 8); + } #endif - if (i >= fno->lfsize - 1) { i = 0; break; } /* No LFN if buffer overflow */ - p[i++] = (TCHAR)w; + if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */ + fno->fname[i++] = (char)w; } + fno->fname[i] = 0; /* Terminate the LFN */ } - p[i] = 0; /* Terminate LFN string by a \0 */ } + + i = j = 0; + lfv = fno->fname[i]; /* LFN is exist if non-zero */ + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) { /* Insert a . if extension is exist */ + if (!lfv) fno->fname[j] = '.'; + fno->altname[j++] = '.'; + } +#if _LFN_UNICODE + if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dp->dir[i])) { + c = c << 8 | dp->dir[i++]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; #endif + fno->altname[j] = c; + if (!lfv) { + if (IsUpper(c) && (dp->dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY))) { + c += 0x20; /* To lower */ + } + fno->fname[j] = c; + } + j++; + } + if (!lfv) { + fno->fname[j] = 0; + if (!dp->dir[DIR_NTres]) j = 0; /* Altname is no longer needed if neither LFN nor case info is exist. */ + } + fno->altname[j] = 0; /* Terminate the SFN */ + +#else /* Non-LFN configuration */ + i = j = 0; + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) fno->fname[j++] = '.'; /* Insert a . if extension is exist */ + fno->fname[j++] = c; + } + fno->fname[j] = 0; +#endif + + fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ + tm = ld_dword(dp->dir + DIR_ModTime); /* Timestamp */ + fno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16); } + #endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ - +#if _USE_FIND && _FS_MINIMIZE <= 1 /*-----------------------------------------------------------------------*/ /* Pattern matching */ /*-----------------------------------------------------------------------*/ -#if _USE_FIND && _FS_MINIMIZE <= 1 + static WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ ) { +#if !_LFN_UNICODE WCHAR chr; -#if !_LFN_UNICODE chr = (BYTE)*(*ptr)++; /* Get a byte */ if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ - if (IsDBCS1(chr) && IsDBCS2(**ptr)) /* Get DBC 2nd byte if needed */ - chr = chr << 8 | (BYTE)*(*ptr)++; #ifdef _EXCVT if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ -#endif #else - chr = ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ + if (IsDBCS1(chr) && IsDBCS2(**ptr)) { /* Get DBC 2nd byte if needed */ + chr = chr << 8 | (BYTE)*(*ptr)++; + } #endif return chr; +#else + return ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ +#endif } static -int pattern_matching ( /* 0:mismatched, 1:matched */ +int pattern_matching ( /* 0:not matched, 1:matched */ const TCHAR* pat, /* Matching pattern */ const TCHAR* nam, /* String to be tested */ int skip, /* Number of pre-skip chars (number of ?s) */ @@ -1823,15 +2550,15 @@ int pattern_matching ( /* 0:mismatched, 1:matched */ pc = get_achar(&pp); /* Get a pattern char */ nc = get_achar(&np); /* Get a name char */ if (pc != nc) break; /* Branch mismatched? */ - if (!pc) return 1; /* Branch matched? (matched at end of both strings) */ + if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */ } get_achar(&nam); /* nam++ */ } while (inf && nc); /* Retry until end of name if infinite search is specified */ return 0; } -#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ +#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ @@ -1845,41 +2572,40 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ const TCHAR** path /* Pointer to pointer to the segment in the path string */ ) { -#if _USE_LFN /* LFN configuration */ +#if _USE_LFN != 0 /* LFN configuration */ BYTE b, cf; WCHAR w, *lfn; UINT i, ni, si, di; const TCHAR *p; /* Create LFN in Unicode */ - for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ - lfn = dp->lfn; - si = di = 0; + p = *path; lfn = dp->obj.fs->lfnbuf; si = di = 0; for (;;) { w = p[si++]; /* Get a character */ - if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ - if (di >= _MAX_LFN) /* Reject too long name */ - return FR_INVALID_NAME; + if (w < ' ') break; /* Break if end of the path name */ + if (w == '/' || w == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (di >= _MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ #if !_LFN_UNICODE w &= 0xFF; if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ b = (BYTE)p[si++]; /* Get 2nd byte */ w = (w << 8) + b; /* Create a DBC */ - if (!IsDBCS2(b)) - return FR_INVALID_NAME; /* Reject invalid sequence */ + if (!IsDBCS2(b)) return FR_INVALID_NAME; /* Reject invalid sequence */ } w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ if (!w) return FR_INVALID_NAME; /* Reject invalid code */ #endif - if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal characters for LFN */ - return FR_INVALID_NAME; + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ lfn[di++] = w; /* Store the Unicode character */ } *path = &p[si]; /* Return pointer to the next segment */ - cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ -#if _FS_RPATH + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ +#if _FS_RPATH != 0 if ((di == 1 && lfn[di - 1] == '.') || - (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot entry? */ + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ lfn[di] = 0; for (i = 0; i < 11; i++) /* Create dot name for SFN entry */ dp->fn[i] = (i < di) ? '.' : ' '; @@ -1892,8 +2618,8 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ if (w != ' ' && w != '.') break; di--; } - if (!di) return FR_INVALID_NAME; /* Reject nul string */ lfn[di] = 0; /* LFN is created */ + if (di == 0) return FR_INVALID_NAME; /* Reject nul name */ /* Create SFN in directory form */ mem_set(dp->fn, ' ', 11); @@ -1953,8 +2679,7 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ if (ni == 8) b <<= 2; - if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ - cf |= NS_LFN; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* Create LFN entry when there are composite capitals */ if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ @@ -1965,17 +2690,16 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ return FR_OK; -#else /* Non-LFN configuration */ - BYTE b, c, d, *sfn; +#else /* _USE_LFN != 0 : Non-LFN configuration */ + BYTE c, d, *sfn; UINT ni, si, i; const char *p; /* Create file name in directory form */ - for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Skip duplicated separator */ - sfn = dp->fn; + p = *path; sfn = dp->fn; mem_set(sfn, ' ', 11); - si = i = b = 0; ni = 8; -#if _FS_RPATH + si = i = 0; ni = 8; +#if _FS_RPATH != 0 if (p[si] == '.') { /* Is this a dot entry? */ for (;;) { c = (BYTE)p[si++]; @@ -1983,62 +2707,51 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ sfn[i++] = c; } if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; - *path = &p[si]; /* Return pointer to the next segment */ - sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ + *path = p + si; /* Return pointer to the next segment */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */ return FR_OK; } #endif for (;;) { c = (BYTE)p[si++]; - if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ - if (c == '.' || i >= ni) { - if (ni != 8 || c != '.') return FR_INVALID_NAME; - i = 8; ni = 11; - b <<= 2; continue; + if (c <= ' ') break; /* Break if end of the path name */ + if (c == '/' || c == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (c == '.' || i >= ni) { /* End of body or over size? */ + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Over size or invalid dot */ + i = 8; ni = 11; /* Goto extension */ + continue; } if (c >= 0x80) { /* Extended character? */ - b |= 3; /* Eliminate NT flag */ #ifdef _EXCVT c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */ #else #if !_DF1S - return FR_INVALID_NAME; /* Reject extended characters (ASCII cfg) */ + return FR_INVALID_NAME; /* Reject extended characters (ASCII only cfg) */ #endif #endif } if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false at SBCS cfg.) */ d = (BYTE)p[si++]; /* Get 2nd byte */ - if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ - return FR_INVALID_NAME; + if (!IsDBCS2(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */ sfn[i++] = c; sfn[i++] = d; } else { /* SBC */ - if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */ - return FR_INVALID_NAME; - if (IsUpper(c)) { /* ASCII large capital? */ - b |= 2; - } else { - if (IsLower(c)) { /* ASCII small capital? */ - b |= 1; c -= 0x20; - } - } + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ + if (IsLower(c)) c -= 0x20; /* To upper */ sfn[i++] = c; } } - *path = &p[si]; /* Return pointer to the next segment */ - c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + *path = p + si; /* Return pointer to the next segment */ + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ - if (!i) return FR_INVALID_NAME; /* Reject nul string */ - if (sfn[0] == DDEM) sfn[0] = RDDEM; /* When first character collides with DDEM, replace it with RDDEM */ - - if (ni == 8) b <<= 2; - if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */ - if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */ - - sfn[NSFLAG] = c; /* Store NT flag, File name is created */ + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ return FR_OK; -#endif +#endif /* _USE_LFN != 0 */ } @@ -2055,36 +2768,50 @@ FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ ) { FRESULT res; - BYTE *dir, ns; + BYTE ns; + _FDID *obj = &dp->obj; + FATFS *fs = obj->fs; -#if _FS_RPATH - if (*path == '/' || *path == '\\') { /* There is a heading separator */ - path++; dp->sclust = 0; /* Strip it and start from the root directory */ - } else { /* No heading separator */ - dp->sclust = dp->fs->cdir; /* Start from the current directory */ +#if _FS_RPATH != 0 + if (*path != '/' && *path != '\\') { /* Without heading separator */ + obj->sclust = fs->cdir; /* Start from the current directory */ + } else +#endif + { /* With heading separator */ + while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ + obj->sclust = 0; /* Start from the root directory */ + } +#if _FS_EXFAT && _FS_RPATH != 0 + if (fs->fs_type == FS_EXFAT && obj->sclust) { /* Retrieve the sub-directory status if needed */ + DIR dj; + + obj->c_scl = fs->cdc_scl; + obj->c_size = fs->cdc_size; + obj->c_ofs = fs->cdc_ofs; + res = load_obj_dir(&dj, obj); + if (res != FR_OK) return res; + obj->objsize = ld_dword(fs->dirbuf + XDIR_FileSize); + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; } -#else - if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ - path++; - dp->sclust = 0; /* Always start from the root directory */ #endif if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + dp->fn[NSFLAG] = NS_NONAME; res = dir_sdi(dp, 0); - dp->dir = 0; + } else { /* Follow path */ for (;;) { res = create_name(dp, &path); /* Get a segment name of the path */ if (res != FR_OK) break; - res = dir_find(dp); /* Find an object with the sagment name */ + res = dir_find(dp); /* Find an object with the segment name */ ns = dp->fn[NSFLAG]; if (res != FR_OK) { /* Failed to find the object */ if (res == FR_NO_FILE) { /* Object is not found */ - if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, */ - dp->sclust = 0; dp->dir = 0; /* it is the root directory and stay there */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */ if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ - res = FR_OK; /* Ended at the root directroy. Function completed. */ + dp->fn[NSFLAG] = NS_NONAME; + res = FR_OK; } else { /* Could not find the object */ if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ } @@ -2092,11 +2819,23 @@ FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ break; } if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ - dir = dp->dir; /* Follow the sub-directory */ - if (!(dir[DIR_Attr] & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + /* Get into the sub-directory */ + if (!(obj->attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ res = FR_NO_PATH; break; } - dp->sclust = ld_clust(dp->fs, dir); +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + obj->c_scl = obj->sclust; /* Save containing directory information for next dir */ + obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; + obj->c_ofs = dp->blk_ofs; + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Open next directory */ + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + } else +#endif + { + obj->sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ + } } } @@ -2155,7 +2894,7 @@ int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ #endif return vol; } -#if _FS_RPATH && _VOLUMES >= 2 +#if _FS_RPATH != 0 && _VOLUMES >= 2 vol = CurrVol; /* Current drive */ #else vol = 0; /* Drive 0 */ @@ -2172,24 +2911,24 @@ int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ /*-----------------------------------------------------------------------*/ static -BYTE check_fs ( /* 0:Valid FAT-BS, 1:Valid BS but not FAT, 2:Not a BS, 3:Disk error */ +BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */ FATFS* fs, /* File system object */ - DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ + DWORD sect /* Sector# (lba) to check if it is an FAT-VBR or not */ ) { - fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ - if (move_window(fs, sect) != FR_OK) /* Load boot record */ - return 3; + fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */ - if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ - return 2; + if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ - if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ - return 0; - if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ - return 0; - - return 1; + if (fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) { + if ((ld_dword(fs->win + BS_FilSysType) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */ + if (ld_dword(fs->win + BS_FilSysType32) == 0x33544146) return 0; /* Check "FAT3" string */ + } +#if _FS_EXFAT + if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; +#endif + return 2; } @@ -2201,9 +2940,9 @@ BYTE check_fs ( /* 0:Valid FAT-BS, 1:Valid BS but not FAT, 2:Not a BS, 3:Disk er static FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ - FATFS** rfs, /* Pointer to pointer to the found file system object */ const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ - BYTE wmode /* !=0: Check write protection for write access */ + FATFS** rfs, /* Pointer to pointer to the found file system object */ + BYTE mode /* !=0: Check write protection for write access */ ) { BYTE fmt, *pt; @@ -2227,11 +2966,13 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ ENTER_FF(fs); /* Lock the volume */ *rfs = fs; /* Return pointer to the file system object */ + mode &= ~FA_READ; /* Desired access mode, write access or not */ if (fs->fs_type) { /* If the volume has been mounted */ stat = disk_status(fs->drv); if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ - if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check write protection if needed */ + if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */ return FR_WRITE_PROTECTED; + } return FR_OK; /* The file system object is valid */ } } @@ -2242,122 +2983,172 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ fs->fs_type = 0; /* Clear the file system object */ fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */ stat = disk_initialize(fs->drv); /* Initialize the physical drive */ - if (stat & STA_NOINIT) /* Check if the initialization succeeded */ + if (stat & STA_NOINIT) { /* Check if the initialization succeeded */ return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ - if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check disk write protection if needed */ + } + if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */ return FR_WRITE_PROTECTED; + } #if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */ - if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK - || SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR; + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR; + if (SS(fs) > _MAX_SS || SS(fs) < _MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; #endif /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ bsect = 0; - fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT boot sector as SFD */ - if (fmt == 1 || (!fmt && (LD2PT(vol)))) { /* Not an FAT boot sector or forced partition number */ + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */ + if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) { /* Not an FAT-VBR or forced partition number */ for (i = 0; i < 4; i++) { /* Get partition offset */ - pt = fs->win + MBR_Table + i * SZ_PTE; - br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0; + pt = fs->win + (MBR_Table + i * SZ_PTE); + br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0; } i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ if (i) i--; do { /* Find an FAT volume */ bsect = br[i]; - fmt = bsect ? check_fs(fs, bsect) : 2; /* Check the partition */ - } while (!LD2PT(vol) && fmt && ++i < 4); + fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */ + } while (!LD2PT(vol) && fmt >= 2 && ++i < 4); } - if (fmt == 3) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ - if (fmt) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ /* An FAT volume is found. Following code initializes the file system object */ - if (LD_WORD(fs->win + BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */ - return FR_NO_FILESYSTEM; +#if _FS_EXFAT + if (fmt == 1) { + QWORD maxlba; - fasize = LD_WORD(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ - if (!fasize) fasize = LD_DWORD(fs->win + BPB_FATSz32); - fs->fsize = fasize; + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; - fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */ - if (fs->n_fats != 1 && fs->n_fats != 2) /* (Must be 1 or 2) */ - return FR_NO_FILESYSTEM; - fasize *= fs->n_fats; /* Number of sectors for FAT area */ + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT revision (Must be 1.0) */ - fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ - if (!fs->csize || (fs->csize & (fs->csize - 1))) /* (Must be power of 2) */ - return FR_NO_FILESYSTEM; + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; - fs->n_rootdir = LD_WORD(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ - if (fs->n_rootdir % (SS(fs) / SZ_DIRE)) /* (Must be sector aligned) */ - return FR_NO_FILESYSTEM; + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ + if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ - tsect = LD_WORD(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ - if (!tsect) tsect = LD_DWORD(fs->win + BPB_TotSec32); + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ - nrsv = LD_WORD(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ - if (!nrsv) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ - /* Determine the FAT sub type */ - sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIRE); /* RSV + FAT + DIR */ - if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ - nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ - if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ - fmt = FS_FAT12; - if (nclst >= MIN_FAT16) fmt = FS_FAT16; - if (nclst >= MIN_FAT32) fmt = FS_FAT32; + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ - /* Boundaries and Limits */ - fs->n_fatent = nclst + 2; /* Number of FAT entries */ - fs->volbase = bsect; /* Volume start sector */ - fs->fatbase = bsect + nrsv; /* FAT start sector */ - fs->database = bsect + sysect; /* Data start sector */ - if (fmt == FS_FAT32) { - if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ - fs->dirbase = LD_DWORD(fs->win + BPB_RootClus); /* Root directory start cluster */ - szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ - } else { - if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ - fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ - szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ - fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); - } - if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (BPB_FATSz must not be less than the size needed) */ - return FR_NO_FILESYSTEM; + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ + fs->n_fatent = nclst + 2; + + /* Boundaries and Limits */ + fs->volbase = bsect; + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); + if (maxlba < fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); + + /* Check if bitmap location is in assumption (at the first cluster) */ + if (move_window(fs, clust2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR; + for (i = 0; i < SS(fs); i += SZDIRE) { + if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break; /* 81 entry with cluster #2? */ + } + if (i == SS(fs)) return FR_NO_FILESYSTEM; +#if !_FS_READONLY + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ +#endif + fmt = FS_EXFAT; /* FAT sub-type */ + } else +#endif /* _FS_EXFAT */ + { + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ + + fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ + if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ + + tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); + + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT32; + if (nclst <= MAX_FAT16) fmt = FS_FAT16; + if (nclst <= MAX_FAT12) fmt = FS_FAT12; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->volbase = bsect; /* Volume start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + fs->database = bsect + sysect; /* Data start sector */ + if (fmt == FS_FAT32) { + if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ + } else { + if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM;/* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ #if !_FS_READONLY - /* Initialize cluster allocation information */ - fs->last_clust = fs->free_clust = 0xFFFFFFFF; - - /* Get fsinfo if available */ - fs->fsi_flag = 0x80; + /* Get FSINFO if available */ + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ + fs->fsi_flag = 0x80; #if (_FS_NOFSINFO & 3) != 3 - if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo == 1 */ - && LD_WORD(fs->win + BPB_FSInfo) == 1 - && move_window(fs, bsect + 1) == FR_OK) - { - fs->fsi_flag = 0; - if (LD_WORD(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */ - && LD_DWORD(fs->win + FSI_LeadSig) == 0x41615252 - && LD_DWORD(fs->win + FSI_StrucSig) == 0x61417272) + if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo32 == 1 */ + && ld_word(fs->win + BPB_FSInfo32) == 1 + && move_window(fs, bsect + 1) == FR_OK) { + fs->fsi_flag = 0; + if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */ + && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 + && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) + { #if (_FS_NOFSINFO & 1) == 0 - fs->free_clust = LD_DWORD(fs->win + FSI_Free_Count); + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); #endif #if (_FS_NOFSINFO & 2) == 0 - fs->last_clust = LD_DWORD(fs->win + FSI_Nxt_Free); + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); #endif + } } +#endif /* (_FS_NOFSINFO & 3) != 3 */ +#endif /* !_FS_READONLY */ } -#endif -#endif + fs->fs_type = fmt; /* FAT sub-type */ fs->id = ++Fsid; /* File system mount ID */ -#if _FS_RPATH - fs->cdir = 0; /* Set current directory to root */ +#if _USE_LFN == 1 + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ +#if _FS_EXFAT + fs->dirbuf = DirBuf; /* Static directory block working buuffer */ #endif -#if _FS_LOCK /* Clear file lock semaphores */ +#endif +#if _FS_RPATH != 0 + fs->cdir = 0; /* Initialize current directory */ +#endif +#if _FS_LOCK != 0 /* Clear file lock semaphores */ clear_lock(fs); #endif - return FR_OK; } @@ -2369,29 +3160,34 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ /*-----------------------------------------------------------------------*/ static -FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ - void* obj /* Pointer to the object FIL/DIR to check validity */ +FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ + void* dfp, /* Pointer to the FIL/DIR object to check validity */ + FATFS** fs /* Pointer to pointer to the owner file system object to return */ ) { - FIL *fil = (FIL*)obj; /* Assuming offset of .fs and .id in the FIL/DIR structure is identical */ + _FDID *obj = (_FDID*)dfp; /* Assuming .obj in the FIL/DIR is the first member */ + FRESULT res; - if (!fil || !fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id || (disk_status(fil->fs->drv) & STA_NOINIT)) - return FR_INVALID_OBJECT; - - ENTER_FF(fil->fs); /* Lock file system */ - - return FR_OK; + if (!dfp || !obj->fs || !obj->fs->fs_type || obj->fs->id != obj->id || (disk_status(obj->fs->drv) & STA_NOINIT)) { + *fs = 0; /* The object is invalid */ + res = FR_INVALID_OBJECT; + } else { + *fs = obj->fs; /* Owner file sytem object */ + ENTER_FF(obj->fs); /* Lock file system */ + res = FR_OK; + } + return res; } -/*-------------------------------------------------------------------------- +/*--------------------------------------------------------------------------- - Public Functions + Public Functions (FatFs API) ----------------------------------------------------------------------------*/ +----------------------------------------------------------------------------*/ @@ -2402,7 +3198,7 @@ FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ FRESULT f_mount ( FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/ const TCHAR* path, /* Logical drive number to be mounted/unmounted */ - BYTE opt /* 0:Do not mount (delayed mount), 1:Mount immediately */ + BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ ) { FATFS *cfs; @@ -2416,7 +3212,7 @@ FRESULT f_mount ( cfs = FatFs[vol]; /* Pointer to fs object */ if (cfs) { -#if _FS_LOCK +#if _FS_LOCK != 0 clear_lock(cfs); #endif #if _FS_REENTRANT /* Discard sync object of the current volume */ @@ -2435,7 +3231,7 @@ FRESULT f_mount ( if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */ - res = find_volume(&fs, &path, 0); /* Force mounted the volume */ + res = find_volume(&path, &fs, 0); /* Force mounted the volume */ LEAVE_FF(fs, res); } @@ -2454,126 +3250,190 @@ FRESULT f_open ( { FRESULT res; DIR dj; - BYTE *dir; - DEFINE_NAMEBUF; + FATFS *fs; #if !_FS_READONLY - DWORD dw, cl; + DWORD dw, cl, bcs, clst, sc; + FSIZE_t ofs; #endif + DEF_NAMBUF if (!fp) return FR_INVALID_OBJECT; - fp->fs = 0; /* Clear file object */ /* Get logical drive number */ -#if !_FS_READONLY - mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; - res = find_volume(&dj.fs, &path, (BYTE)(mode & ~FA_READ)); -#else - mode &= FA_READ; - res = find_volume(&dj.fs, &path, 0); -#endif + mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND; + res = find_volume(&path, &fs, mode); if (res == FR_OK) { - INIT_BUF(dj); + dj.obj.fs = fs; + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ - dir = dj.dir; #if !_FS_READONLY /* R/W configuration */ if (res == FR_OK) { - if (!dir) /* Default directory itself */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ res = FR_INVALID_NAME; -#if _FS_LOCK - else + } +#if _FS_LOCK != 0 + else { res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + } #endif } /* Create or Open a file */ if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { if (res != FR_OK) { /* No file, create new */ if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ -#if _FS_LOCK +#if _FS_LOCK != 0 res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; #else res = dir_register(&dj); #endif mode |= FA_CREATE_ALWAYS; /* File is created */ - dir = dj.dir; /* New entry */ } else { /* Any object is already existing */ - if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ res = FR_DENIED; } else { - if (mode & FA_CREATE_NEW) /* Cannot create as new file */ - res = FR_EXIST; + if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */ } } if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ dw = GET_FATTIME(); - ST_DWORD(dir + DIR_CrtTime, dw);/* Set created time */ - ST_DWORD(dir + DIR_WrtTime, dw);/* Set modified time */ - dir[DIR_Attr] = 0; /* Reset attribute */ - ST_DWORD(dir + DIR_FileSize, 0);/* Reset file size */ - cl = ld_clust(dj.fs, dir); /* Get cluster chain */ - st_clust(dir, 0); /* Reset cluster */ - dj.fs->wflag = 1; - if (cl) { /* Remove the cluster chain if exist */ - dw = dj.fs->winsect; - res = remove_chain(dj.fs, cl); - if (res == FR_OK) { - dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ - res = move_window(dj.fs, dw); +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + /* Get current allocation info */ + fp->obj.fs = fs; + fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); + fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_CrtTime, dw); /* Set created time */ + fs->dirbuf[XDIR_CrtTime10] = 0; + st_dword(fs->dirbuf + XDIR_ModTime, dw); /* Set modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + fs->dirbuf[XDIR_Attr] = AM_ARC; /* Reset attribute */ + st_dword(fs->dirbuf + XDIR_FstClus, 0); /* Reset file allocation info */ + st_qword(fs->dirbuf + XDIR_FileSize, 0); + st_qword(fs->dirbuf + XDIR_ValidFileSize, 0); + fs->dirbuf[XDIR_GenFlags] = 1; + res = store_xdir(&dj); + if (res == FR_OK && fp->obj.sclust) { /* Remove the cluster chain if exist */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */ + } + } else +#endif + { + /* Clean directory info */ + st_dword(dj.dir + DIR_CrtTime, dw); /* Set created time */ + st_dword(dj.dir + DIR_ModTime, dw); /* Set modified time */ + dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ + cl = ld_clust(fs, dj.dir); /* Get cluster chain */ + st_clust(fs, dj.dir, 0); /* Reset file allocation info */ + st_dword(dj.dir + DIR_FileSize, 0); + fs->wflag = 1; + + if (cl) { /* Remove the cluster chain if exist */ + dw = fs->winsect; + res = remove_chain(&dj.obj, cl, 0); + if (res == FR_OK) { + res = move_window(fs, dw); + fs->last_clst = cl - 1; /* Reuse the cluster hole */ + } } } } } else { /* Open an existing file */ if (res == FR_OK) { /* Following succeeded */ - if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ + if (dj.obj.attr & AM_DIR) { /* It is a directory */ res = FR_NO_FILE; } else { - if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* R/O violation */ res = FR_DENIED; + } } } } if (res == FR_OK) { if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ - mode |= FA__WRITTEN; - fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ - fp->dir_ptr = dir; -#if _FS_LOCK - fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); - if (!fp->lockid) res = FR_INT_ERR; + mode |= FA_MODIFIED; + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#if _FS_LOCK != 0 + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->obj.lockid) res = FR_INT_ERR; #endif } - -#else /* R/O configuration */ - if (res == FR_OK) { /* Follow succeeded */ - dir = dj.dir; - if (!dir) { /* Current directory itself */ +#else /* R/O configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ res = FR_INVALID_NAME; } else { - if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ + if (dj.obj.attr & AM_DIR) { /* It is a directory */ res = FR_NO_FILE; + } } } #endif - FREE_BUF(); if (res == FR_OK) { - fp->flag = mode; /* File access mode */ - fp->err = 0; /* Clear error flag */ - fp->sclust = ld_clust(dj.fs, dir); /* File start cluster */ - fp->fsize = LD_DWORD(dir + DIR_FileSize); /* File size */ - fp->fptr = 0; /* File pointer */ - fp->dsect = 0; -#if _USE_FASTSEEK - fp->cltbl = 0; /* Normal seek mode */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get allocation info */ + fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + fp->obj.c_scl = dj.obj.sclust; + fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fp->obj.c_ofs = dj.blk_ofs; + } else +#endif + { + fp->obj.sclust = ld_clust(fs, dj.dir); /* Get allocation info */ + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); + } +#if _USE_FASTSEEK + fp->cltbl = 0; /* Disable fast seek mode */ +#endif + fp->obj.fs = fs; /* Validate the file object */ + fp->obj.id = fs->id; + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ +#if !_FS_READONLY +#if !_FS_TINY + mem_set(fp->buf, 0, _MAX_SS); /* Clear sector buffer */ +#endif + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ + fp->fptr = fp->obj.objsize; /* Offset to seek */ + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ + clst = fp->obj.sclust; /* Follow the cluster chain */ + for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) { + clst = get_fat(&fp->obj, clst); + if (clst <= 1) res = FR_INT_ERR; + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; + } + fp->clust = clst; + if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */ + if ((sc = clust2sect(fs, clst)) == 0) { + res = FR_INT_ERR; + } else { + fp->sect = sc + (DWORD)(ofs / SS(fs)); +#if !_FS_TINY + if (disk_read(fs->drv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR; +#endif + } + } + } #endif - fp->fs = dj.fs; /* Validate file object */ - fp->id = fp->fs->id; } + + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ + + LEAVE_FF(fs, res); } @@ -2584,96 +3444,99 @@ FRESULT f_open ( /*-----------------------------------------------------------------------*/ FRESULT f_read ( - FIL* fp, /* Pointer to the file object */ - void* buff, /* Pointer to data buffer */ - UINT btr, /* Number of bytes to read */ - UINT* br /* Pointer to number of bytes read */ + FIL* fp, /* Pointer to the file object */ + void* buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT* br /* Pointer to number of bytes read */ ) { FRESULT res; - DWORD clst, sect, remain; - UINT rcnt, cc; - BYTE csect, *rbuff = (BYTE*)buff; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, cc, csect; + BYTE *rbuff = (BYTE*)buff; *br = 0; /* Clear read byte counter */ - - res = validate(fp); /* Check validity */ - if (res != FR_OK) LEAVE_FF(fp->fs, res); - if (fp->err) /* Check error */ - LEAVE_FF(fp->fs, (FRESULT)fp->err); - if (!(fp->flag & FA_READ)) /* Check access mode */ - LEAVE_FF(fp->fs, FR_DENIED); - remain = fp->fsize - fp->fptr; + res = validate(fp, &fs); + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + remain = fp->obj.objsize - fp->fptr; if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ for ( ; btr; /* Repeat until all data read */ rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { - if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ - csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ - if (!csect) { /* On the cluster boundary? */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ if (fp->fptr == 0) { /* On the top of the file? */ - clst = fp->sclust; /* Follow from the origin */ + clst = fp->obj.sclust; /* Follow cluster chain from the origin */ } else { /* Middle or end of the file */ #if _USE_FASTSEEK - if (fp->cltbl) + if (fp->cltbl) { clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ - else + } else #endif - clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */ + { + clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */ + } } - if (clst < 2) ABORT(fp->fs, FR_INT_ERR); - if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + if (clst < 2) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); fp->clust = clst; /* Update current cluster */ } - sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ - if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect = clust2sect(fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fs, FR_INT_ERR); sect += csect; - cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + cc = btr / SS(fs); /* When remaining bytes >= sector size, */ if (cc) { /* Read maximum contiguous sectors directly */ - if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ - cc = fp->fs->csize - csect; - if (disk_read(fp->fs->drv, rbuff, sect, cc) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) { + ABORT(fs, FR_DISK_ERR); + } #if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ #if _FS_TINY - if (fp->fs->wflag && fp->fs->winsect - sect < cc) - mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); + if (fs->wflag && fs->winsect - sect < cc) { + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); + } #else - if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) - mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + } #endif #endif - rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + rcnt = SS(fs) * cc; /* Number of bytes transferred */ continue; } #if !_FS_TINY - if (fp->dsect != sect) { /* Load data sector if not in cache */ + if (fp->sect != sect) { /* Load data sector if not in cache */ #if !_FS_READONLY - if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); - fp->flag &= ~FA__DIRTY; + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= ~FA_DIRTY; } #endif - if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */ - ABORT(fp->fs, FR_DISK_ERR); + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { /* Fill sector cache */ + ABORT(fs, FR_DISK_ERR); + } } #endif - fp->dsect = sect; + fp->sect = sect; } - rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ - if (rcnt > btr) rcnt = btr; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ #if _FS_TINY - if (move_window(fp->fs, fp->dsect) != FR_OK) /* Move sector window */ - ABORT(fp->fs, FR_DISK_ERR); - mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #else - mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #endif } - LEAVE_FF(fp->fs, FR_OK); + LEAVE_FF(fs, FR_OK); } @@ -2686,117 +3549,120 @@ FRESULT f_read ( FRESULT f_write ( FIL* fp, /* Pointer to the file object */ - const void *buff, /* Pointer to the data to be written */ + const void* buff, /* Pointer to the data to be written */ UINT btw, /* Number of bytes to write */ UINT* bw /* Pointer to number of bytes written */ ) { FRESULT res; + FATFS *fs; DWORD clst, sect; - UINT wcnt, cc; + UINT wcnt, cc, csect; const BYTE *wbuff = (const BYTE*)buff; - BYTE csect; *bw = 0; /* Clear write byte counter */ + res = validate(fp, &fs); + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ - res = validate(fp); /* Check validity */ - if (res != FR_OK) LEAVE_FF(fp->fs, res); - if (fp->err) /* Check error */ - LEAVE_FF(fp->fs, (FRESULT)fp->err); - if (!(fp->flag & FA_WRITE)) /* Check access mode */ - LEAVE_FF(fp->fs, FR_DENIED); - if (fp->fptr + btw < fp->fptr) btw = 0; /* File size cannot reach 4GB */ + /* Check fptr wrap-around (file size cannot exceed the limit on each FAT specs) */ + if ((_FS_EXFAT && fs->fs_type == FS_EXFAT && fp->fptr + btw < fp->fptr) + || (DWORD)fp->fptr + btw < (DWORD)fp->fptr) { + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); + } for ( ; btw; /* Repeat until all data written */ - wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { - if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ - csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ - if (!csect) { /* On the cluster boundary? */ + wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ if (fp->fptr == 0) { /* On the top of the file? */ - clst = fp->sclust; /* Follow from the origin */ - if (clst == 0) /* When no cluster is allocated, */ - clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ - } else { /* Middle or end of the file */ + clst = fp->obj.sclust; /* Follow from the origin */ + if (clst == 0) { /* If no cluster is allocated, */ + clst = create_chain(&fp->obj, 0); /* create a new cluster chain */ + } + } else { /* On the middle or end of the file */ #if _USE_FASTSEEK - if (fp->cltbl) + if (fp->cltbl) { clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ - else + } else #endif - clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */ + { + clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } } if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ - if (clst == 1) ABORT(fp->fs, FR_INT_ERR); - if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); fp->clust = clst; /* Update current cluster */ - if (fp->sclust == 0) fp->sclust = clst; /* Set start cluster if the first write */ + if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */ } #if _FS_TINY - if (fp->fs->winsect == fp->dsect && sync_window(fp->fs)) /* Write-back sector cache */ - ABORT(fp->fs, FR_DISK_ERR); + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ #else - if (fp->flag & FA__DIRTY) { /* Write-back sector cache */ - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); - fp->flag &= ~FA__DIRTY; + if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= ~FA_DIRTY; } #endif - sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ - if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect = clust2sect(fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fs, FR_INT_ERR); sect += csect; - cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + cc = btw / SS(fs); /* When remaining bytes >= sector size, */ if (cc) { /* Write maximum contiguous sectors directly */ - if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ - cc = fp->fs->csize - csect; - if (disk_write(fp->fs->drv, wbuff, sect, cc) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) { + ABORT(fs, FR_DISK_ERR); + } #if _FS_MINIMIZE <= 2 #if _FS_TINY - if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ - mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); - fp->fs->wflag = 0; + if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); + fs->wflag = 0; } #else - if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ - mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); - fp->flag &= ~FA__DIRTY; + if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); + fp->flag &= ~FA_DIRTY; } #endif #endif - wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + wcnt = SS(fs) * cc; /* Number of bytes transferred */ continue; } #if _FS_TINY - if (fp->fptr >= fp->fsize) { /* Avoid silly cache filling at growing edge */ - if (sync_window(fp->fs)) ABORT(fp->fs, FR_DISK_ERR); - fp->fs->winsect = sect; + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling at growing edge */ + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); + fs->winsect = sect; } #else - if (fp->dsect != sect) { /* Fill sector cache with file data */ - if (fp->fptr < fp->fsize && - disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); + if (fp->sect != sect) { /* Fill sector cache with file data */ + if (fp->fptr < fp->obj.objsize && + disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { + ABORT(fs, FR_DISK_ERR); + } } #endif - fp->dsect = sect; + fp->sect = sect; } - wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */ - if (wcnt > btw) wcnt = btw; + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ #if _FS_TINY - if (move_window(fp->fs, fp->dsect) != FR_OK) /* Move sector window */ - ABORT(fp->fs, FR_DISK_ERR); - mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ - fp->fs->wflag = 1; + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fs->wflag = 1; #else - mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ - fp->flag |= FA__DIRTY; + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fp->flag |= FA_DIRTY; #endif } - if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ - fp->flag |= FA__WRITTEN; /* Set file change flag */ + fp->flag |= FA_MODIFIED; /* Set file change flag */ - LEAVE_FF(fp->fs, FR_OK); + LEAVE_FF(fs, FR_OK); } @@ -2811,38 +3677,68 @@ FRESULT f_sync ( ) { FRESULT res; + FATFS *fs; DWORD tm; BYTE *dir; + DEF_NAMBUF - res = validate(fp); /* Check validity of the object */ + res = validate(fp, &fs); /* Check validity of the object */ if (res == FR_OK) { - if (fp->flag & FA__WRITTEN) { /* Is there any change to the file? */ + if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */ #if !_FS_TINY - if (fp->flag & FA__DIRTY) { /* Write-back cached data if needed */ - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - LEAVE_FF(fp->fs, FR_DISK_ERR); - fp->flag &= ~FA__DIRTY; + if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR); + fp->flag &= ~FA_DIRTY; } #endif /* Update the directory entry */ - res = move_window(fp->fs, fp->dir_sect); - if (res == FR_OK) { - dir = fp->dir_ptr; - dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ - ST_DWORD(dir + DIR_FileSize, fp->fsize); /* Update file size */ - st_clust(dir, fp->sclust); /* Update start cluster */ - tm = GET_FATTIME(); /* Update modified time */ - ST_DWORD(dir + DIR_WrtTime, tm); - ST_WORD(dir + DIR_LstAccDate, 0); - fp->flag &= ~FA__WRITTEN; - fp->fs->wflag = 1; - res = sync_fs(fp->fs); + tm = GET_FATTIME(); /* Modified time */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = fill_fat_chain(&fp->obj); /* Create FAT chain if needed */ + if (res == FR_OK) { + DIR dj; + + INIT_NAMBUF(fs); + res = load_obj_dir(&dj, &fp->obj); /* Load directory entry block */ + if (res == FR_OK) { + fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive bit */ + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation info */ + st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); + st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); + st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + st_dword(fs->dirbuf + XDIR_AccTime, 0); + res = store_xdir(&dj); /* Restore it to the directory */ + if (res == FR_OK) { + res = sync_fs(fs); + fp->flag &= ~FA_MODIFIED; + } + } + FREE_NAMBUF(); + } + } else +#endif + { + res = move_window(fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation info */ + st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ + st_dword(dir + DIR_ModTime, tm); /* Update modified time */ + st_word(dir + DIR_LstAccDate, 0); + fs->wflag = 1; + res = sync_fs(fs); /* Restore it to the directory */ + fp->flag &= ~FA_MODIFIED; + } } } } - LEAVE_FF(fp->fs, res); + LEAVE_FF(fs, res); } #endif /* !_FS_READONLY */ @@ -2855,27 +3751,26 @@ FRESULT f_sync ( /*-----------------------------------------------------------------------*/ FRESULT f_close ( - FIL *fp /* Pointer to the file object to be closed */ + FIL* fp /* Pointer to the file object to be closed */ ) { FRESULT res; - + FATFS *fs; #if !_FS_READONLY res = f_sync(fp); /* Flush cached data */ if (res == FR_OK) #endif { - res = validate(fp); /* Lock volume */ + res = validate(fp, &fs); /* Lock volume */ if (res == FR_OK) { -#if _FS_REENTRANT - FATFS *fs = fp->fs; -#endif -#if _FS_LOCK - res = dec_lock(fp->lockid); /* Decrement file open counter */ +#if _FS_LOCK != 0 + res = dec_lock(fp->obj.lockid); /* Decrement file open counter */ if (res == FR_OK) #endif - fp->fs = 0; /* Invalidate file object */ + { + fp->obj.fs = 0; /* Invalidate file object */ + } #if _FS_REENTRANT unlock_fs(fs, FR_OK); /* Unlock volume */ #endif @@ -2887,11 +3782,11 @@ FRESULT f_close ( +#if _FS_RPATH >= 1 /*-----------------------------------------------------------------------*/ /* Change Current Directory or Current Drive, Get Current Directory */ /*-----------------------------------------------------------------------*/ -#if _FS_RPATH >= 1 #if _VOLUMES >= 2 FRESULT f_chdrive ( const TCHAR* path /* Drive number */ @@ -2916,29 +3811,48 @@ FRESULT f_chdir ( { FRESULT res; DIR dj; - DEFINE_NAMEBUF; - + FATFS *fs; + DEF_NAMBUF /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 0); + res = find_volume(&path, &fs, 0); if (res == FR_OK) { - INIT_BUF(dj); + dj.obj.fs = fs; + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the path */ - FREE_BUF(); if (res == FR_OK) { /* Follow completed */ - if (!dj.dir) { - dj.fs->cdir = dj.sclust; /* Start directory itself */ + if (dj.fn[NSFLAG] & NS_NONAME) { + fs->cdir = dj.obj.sclust; /* It is the start directory itself */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdc_scl = dj.obj.c_scl; + fs->cdc_size = dj.obj.c_size; + fs->cdc_ofs = dj.obj.c_ofs; + } +#endif } else { - if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ - dj.fs->cdir = ld_clust(dj.fs, dj.dir); - else + if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */ + fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */ + fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fs->cdc_ofs = dj.blk_ofs; + } else +#endif + { + fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */ + } + } else { res = FR_NO_PATH; /* Reached but a file */ + } } } + FREE_NAMBUF(); if (res == FR_NO_FILE) res = FR_NO_PATH; } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } @@ -2950,51 +3864,47 @@ FRESULT f_getcwd ( { FRESULT res; DIR dj; + FATFS *fs; UINT i, n; DWORD ccl; TCHAR *tp; FILINFO fno; - DEFINE_NAMEBUF; + DEF_NAMBUF *buff = 0; /* Get logical drive number */ - res = find_volume(&dj.fs, (const TCHAR**)&buff, 0); /* Get current volume */ + res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ if (res == FR_OK) { - INIT_BUF(dj); + dj.obj.fs = fs; + INIT_NAMBUF(fs); i = len; /* Bottom of buffer (directory stack base) */ - dj.sclust = dj.fs->cdir; /* Start to follow upper directory from current directory */ - while ((ccl = dj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ - res = dir_sdi(&dj, 1); /* Get parent directory */ - if (res != FR_OK) break; - res = dir_read(&dj, 0); - if (res != FR_OK) break; - dj.sclust = ld_clust(dj.fs, dj.dir); /* Goto parent directory */ - res = dir_sdi(&dj, 0); - if (res != FR_OK) break; - do { /* Find the entry links to the child directory */ - res = dir_read(&dj, 0); + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */ + dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */ if (res != FR_OK) break; - if (ccl == ld_clust(dj.fs, dj.dir)) break; /* Found the entry */ - res = dir_next(&dj, 0); - } while (res == FR_OK); - if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ - if (res != FR_OK) break; -#if _USE_LFN - fno.lfname = buff; - fno.lfsize = i; -#endif - get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ - tp = fno.fname; -#if _USE_LFN - if (*buff) tp = buff; -#endif - for (n = 0; tp[n]; n++) ; - if (i < n + 3) { - res = FR_NOT_ENOUGH_CORE; break; + res = move_window(fs, dj.sect); + if (res != FR_OK) break; + dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child directory */ + res = dir_read(&dj, 0); + if (res != FR_OK) break; + if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + for (n = 0; fno.fname[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = fno.fname[--n]; + buff[--i] = '/'; } - while (n) buff[--i] = tp[--n]; - buff[--i] = '/'; } tp = buff; if (res == FR_OK) { @@ -3011,11 +3921,12 @@ FRESULT f_getcwd ( } } *tp = 0; - FREE_BUF(); + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } + #endif /* _FS_RPATH >= 2 */ #endif /* _FS_RPATH >= 1 */ @@ -3028,70 +3939,67 @@ FRESULT f_getcwd ( FRESULT f_lseek ( FIL* fp, /* Pointer to the file object */ - DWORD ofs /* File pointer from top of file */ + FSIZE_t ofs /* File pointer from top of file */ ) { FRESULT res; - DWORD clst, bcs, nsect, ifptr; + FATFS *fs; + DWORD clst, bcs, nsect; + FSIZE_t ifptr; #if _USE_FASTSEEK DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; #endif - - res = validate(fp); /* Check validity of the object */ - if (res != FR_OK) LEAVE_FF(fp->fs, res); - if (fp->err) /* Check error */ - LEAVE_FF(fp->fs, (FRESULT)fp->err); - + res = validate(fp, &fs); /* Check validity of the object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ #if _USE_FASTSEEK if (fp->cltbl) { /* Fast seek */ if (ofs == CREATE_LINKMAP) { /* Create CLMT */ tbl = fp->cltbl; tlen = *tbl++; ulen = 2; /* Given table size and required table size */ - cl = fp->sclust; /* Top of the chain */ + cl = fp->obj.sclust; /* Origin of the chain */ if (cl) { do { /* Get a fragment */ tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ do { pcl = cl; ncl++; - cl = get_fat(fp->fs, cl); - if (cl <= 1) ABORT(fp->fs, FR_INT_ERR); - if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + cl = get_fat(&fp->obj, cl); + if (cl <= 1) ABORT(fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); } while (cl == pcl + 1); if (ulen <= tlen) { /* Store the length and top of the fragment */ *tbl++ = ncl; *tbl++ = tcl; } - } while (cl < fp->fs->n_fatent); /* Repeat until end of chain */ + } while (cl < fs->n_fatent); /* Repeat until end of chain */ } *fp->cltbl = ulen; /* Number of items used */ - if (ulen <= tlen) + if (ulen <= tlen) { *tbl = 0; /* Terminate table */ - else + } else { res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ - + } } else { /* Fast seek */ - if (ofs > fp->fsize) /* Clip offset at the file size */ - ofs = fp->fsize; + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */ fp->fptr = ofs; /* Set file pointer */ if (ofs) { fp->clust = clmt_clust(fp, ofs - 1); - dsc = clust2sect(fp->fs, fp->clust); - if (!dsc) ABORT(fp->fs, FR_INT_ERR); - dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1); - if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { /* Refill sector cache if needed */ + dsc = clust2sect(fs, fp->clust); + if (!dsc) ABORT(fs, FR_INT_ERR); + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */ #if !_FS_TINY #if !_FS_READONLY - if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); - fp->flag &= ~FA__DIRTY; + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fp, FR_DISK_ERR); + fp->flag &= ~FA_DIRTY; } #endif - if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /* Load current sector */ - ABORT(fp->fs, FR_DISK_ERR); + if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) { /* Load current sector */ + ABORT(fs, FR_DISK_ERR); + } #endif - fp->dsect = dsc; + fp->sect = dsc; } } } @@ -3100,29 +4008,26 @@ FRESULT f_lseek ( /* Normal Seek */ { - if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ -#if !_FS_READONLY - && !(fp->flag & FA_WRITE) -#endif - ) ofs = fp->fsize; - + if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ + ofs = fp->obj.objsize; + } ifptr = fp->fptr; fp->fptr = nsect = 0; if (ofs) { - bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */ if (ifptr > 0 && (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ ofs -= fp->fptr; clst = fp->clust; } else { /* When seek to back cluster, */ - clst = fp->sclust; /* start from the first cluster */ + clst = fp->obj.sclust; /* start from the first cluster */ #if !_FS_READONLY if (clst == 0) { /* If no cluster chain, create a new chain */ - clst = create_chain(fp->fs, 0); - if (clst == 1) ABORT(fp->fs, FR_INT_ERR); - if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); - fp->sclust = clst; + clst = create_chain(&fp->obj, 0); + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->obj.sclust = clst; } #endif fp->clust = clst; @@ -3131,50 +4036,50 @@ FRESULT f_lseek ( while (ofs > bcs) { /* Cluster following loop */ #if !_FS_READONLY if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ - clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */ + clst = create_chain(&fp->obj, clst); /* Force stretch if in write mode */ if (clst == 0) { /* When disk gets full, clip file size */ ofs = bcs; break; } } else #endif - clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ - if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); - if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR); + clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */ + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); fp->clust = clst; fp->fptr += bcs; ofs -= bcs; } fp->fptr += ofs; - if (ofs % SS(fp->fs)) { - nsect = clust2sect(fp->fs, clst); /* Current sector */ - if (!nsect) ABORT(fp->fs, FR_INT_ERR); - nsect += ofs / SS(fp->fs); + if (ofs % SS(fs)) { + nsect = clust2sect(fs, clst); /* Current sector */ + if (!nsect) ABORT(fs, FR_INT_ERR); + nsect += (DWORD)(ofs / SS(fs)); } } } - if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */ + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */ #if !_FS_TINY #if !_FS_READONLY - if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); - fp->flag &= ~FA__DIRTY; + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= ~FA_DIRTY; } #endif - if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */ - ABORT(fp->fs, FR_DISK_ERR); + if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) { /* Fill sector cache */ + ABORT(fs, FR_DISK_ERR); + } #endif - fp->dsect = nsect; + fp->sect = nsect; } #if !_FS_READONLY - if (fp->fptr > fp->fsize) { /* Set file change flag if the file size is extended */ - fp->fsize = fp->fptr; - fp->flag |= FA__WRITTEN; + if (fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; } #endif } - LEAVE_FF(fp->fs, res); + LEAVE_FF(fs, res); } @@ -3190,45 +4095,59 @@ FRESULT f_opendir ( ) { FRESULT res; - FATFS* fs; - DEFINE_NAMEBUF; + FATFS *fs; + _FDID *obj; + DEF_NAMBUF if (!dp) return FR_INVALID_OBJECT; /* Get logical drive number */ - res = find_volume(&fs, &path, 0); + obj = &dp->obj; + res = find_volume(&path, &fs, 0); if (res == FR_OK) { - dp->fs = fs; - INIT_BUF(*dp); + obj->fs = fs; + INIT_NAMBUF(fs); res = follow_path(dp, path); /* Follow the path to the directory */ - FREE_BUF(); if (res == FR_OK) { /* Follow completed */ - if (dp->dir) { /* It is not the origin directory itself */ - if (dp->dir[DIR_Attr] & AM_DIR) /* The object is a sub directory */ - dp->sclust = ld_clust(fs, dp->dir); - else /* The object is a file */ + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */ + if (obj->attr & AM_DIR) { /* This object is a sub-directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + obj->c_scl = obj->sclust; /* Save containing directory inforamation */ + obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; + obj->c_ofs = dp->blk_ofs; + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object location and status */ + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + obj->sclust = ld_clust(fs, dp->dir); /* Get object location */ + } + } else { /* This object is a file */ res = FR_NO_PATH; + } } if (res == FR_OK) { - dp->id = fs->id; + obj->id = fs->id; res = dir_sdi(dp, 0); /* Rewind directory */ -#if _FS_LOCK +#if _FS_LOCK != 0 if (res == FR_OK) { - if (dp->sclust) { - dp->lockid = inc_lock(dp, 0); /* Lock the sub directory */ - if (!dp->lockid) - res = FR_TOO_MANY_OPEN_FILES; + if (obj->sclust) { + obj->lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!obj->lockid) res = FR_TOO_MANY_OPEN_FILES; } else { - dp->lockid = 0; /* Root directory need not to be locked */ + obj->lockid = 0; /* Root directory need not to be locked */ } } #endif } } + FREE_NAMBUF(); if (res == FR_NO_FILE) res = FR_NO_PATH; } - if (res != FR_OK) dp->fs = 0; /* Invalidate the directory object if function faild */ + if (res != FR_OK) obj->fs = 0; /* Invalidate the directory object if function faild */ LEAVE_FF(fs, res); } @@ -3245,19 +4164,20 @@ FRESULT f_closedir ( ) { FRESULT res; + FATFS *fs; - res = validate(dp); + res = validate(dp, &fs); if (res == FR_OK) { -#if _FS_REENTRANT - FATFS *fs = dp->fs; -#endif -#if _FS_LOCK - if (dp->lockid) /* Decrement sub-directory open counter */ - res = dec_lock(dp->lockid); +#if _FS_LOCK != 0 + if (dp->obj.lockid) { /* Decrement sub-directory open counter */ + res = dec_lock(dp->obj.lockid); + } if (res == FR_OK) #endif - dp->fs = 0; /* Invalidate directory object */ + { + dp->obj.fs = 0; /* Invalidate directory object */ + } #if _FS_REENTRANT unlock_fs(fs, FR_OK); /* Unlock volume */ #endif @@ -3278,40 +4198,34 @@ FRESULT f_readdir ( ) { FRESULT res; - DEFINE_NAMEBUF; + FATFS *fs; + DEF_NAMBUF - res = validate(dp); /* Check validity of the object */ + res = validate(dp, &fs); /* Check validity of the object */ if (res == FR_OK) { if (!fno) { res = dir_sdi(dp, 0); /* Rewind the directory object */ } else { - INIT_BUF(*dp); + INIT_NAMBUF(fs); res = dir_read(dp, 0); /* Read an item */ - if (res == FR_NO_FILE) { /* Reached end of directory */ - dp->sect = 0; - res = FR_OK; - } + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */ if (res == FR_OK) { /* A valid entry is found */ get_fileinfo(dp, fno); /* Get the object information */ res = dir_next(dp, 0); /* Increment index for next */ - if (res == FR_NO_FILE) { - dp->sect = 0; - res = FR_OK; - } + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */ } - FREE_BUF(); + FREE_NAMBUF(); } } - - LEAVE_FF(dp->fs, res); + LEAVE_FF(fs, res); } #if _USE_FIND /*-----------------------------------------------------------------------*/ -/* Find next file */ +/* Find Next File */ /*-----------------------------------------------------------------------*/ FRESULT f_findnext ( @@ -3325,19 +4239,18 @@ FRESULT f_findnext ( for (;;) { res = f_readdir(dp, fno); /* Get a directory item */ if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ -#if _USE_LFN - if (fno->lfname && pattern_matching(dp->pat, fno->lfname, 0, 0)) break; /* Test for LFN if exist */ + if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */ +#if _USE_LFN != 0 && _USE_FIND == 2 + if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */ #endif - if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for SFN */ } return res; - } /*-----------------------------------------------------------------------*/ -/* Find first file */ +/* Find First File */ /*-----------------------------------------------------------------------*/ FRESULT f_findfirst ( @@ -3352,8 +4265,9 @@ FRESULT f_findfirst ( dp->pat = pattern; /* Save pointer to pattern string */ res = f_opendir(dp, path); /* Open the target directory */ - if (res == FR_OK) + if (res == FR_OK) { res = f_findnext(dp, fno); /* Find the first item */ + } return res; } @@ -3373,25 +4287,25 @@ FRESULT f_stat ( { FRESULT res; DIR dj; - DEFINE_NAMEBUF; + DEF_NAMBUF /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 0); + res = find_volume(&path, &dj.obj.fs, 0); if (res == FR_OK) { - INIT_BUF(dj); + INIT_NAMBUF(dj.obj.fs); res = follow_path(&dj, path); /* Follow the file path */ if (res == FR_OK) { /* Follow completed */ - if (dj.dir) { /* Found an object */ - if (fno) get_fileinfo(&dj, fno); - } else { /* It is root directory */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */ res = FR_INVALID_NAME; + } else { /* Found an object */ + if (fno) get_fileinfo(&dj, fno); } } - FREE_BUF(); + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(dj.obj.fs, res); } @@ -3411,52 +4325,73 @@ FRESULT f_getfree ( FATFS *fs; DWORD nfree, clst, sect, stat; UINT i; - BYTE fat, *p; + BYTE *p; + _FDID obj; /* Get logical drive number */ - res = find_volume(fatfs, &path, 0); - fs = *fatfs; + res = find_volume(&path, &fs, 0); if (res == FR_OK) { - /* If free_clust is valid, return it without full cluster scan */ - if (fs->free_clust <= fs->n_fatent - 2) { - *nclst = fs->free_clust; + *fatfs = fs; /* Return ptr to the fs object */ + /* If free_clst is valid, return it without full cluster scan */ + if (fs->free_clst <= fs->n_fatent - 2) { + *nclst = fs->free_clst; } else { /* Get number of free clusters */ - fat = fs->fs_type; nfree = 0; - if (fat == FS_FAT12) { /* Sector unalighed entries: Search FAT via regular routine. */ - clst = 2; + if (fs->fs_type == FS_FAT12) { /* FAT12: Sector unalighed FAT entries */ + clst = 2; obj.fs = fs; do { - stat = get_fat(fs, clst); + stat = get_fat(&obj, clst); if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } if (stat == 1) { res = FR_INT_ERR; break; } if (stat == 0) nfree++; } while (++clst < fs->n_fatent); - } else { /* Sector alighed entries: Accelerate the FAT search. */ - clst = fs->n_fatent; sect = fs->fatbase; - i = 0; p = 0; - do { - if (!i) { - res = move_window(fs, sect++); - if (res != FR_OK) break; - p = fs->win; - i = SS(fs); - } - if (fat == FS_FAT16) { - if (LD_WORD(p) == 0) nfree++; - p += 2; i -= 2; - } else { - if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) nfree++; - p += 4; i -= 4; - } - } while (--clst); + } else { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan bitmap table */ + BYTE bm; + UINT b; + + clst = fs->n_fatent - 2; + sect = fs->database; + i = 0; + do { + if (i == 0 && (res = move_window(fs, sect++)) != FR_OK) break; + for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) { + if (!(bm & 1)) nfree++; + bm >>= 1; + } + i = (i + 1) % SS(fs); + } while (clst); + } else +#endif + { /* FAT16/32: Sector alighed FAT entries */ + clst = fs->n_fatent; sect = fs->fatbase; + i = 0; p = 0; + do { + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + p = fs->win; + i = SS(fs); + } + if (fs->fs_type == FS_FAT16) { + if (ld_word(p) == 0) nfree++; + p += 2; i -= 2; + } else { + if ((ld_dword(p) & 0x0FFFFFFF) == 0) nfree++; + p += 4; i -= 4; + } + } while (--clst); + } } - fs->free_clust = nfree; /* free_clust is valid */ - fs->fsi_flag |= 1; /* FSInfo is to be updated */ *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FSInfo is to be updated */ } } + LEAVE_FF(fs, res); } @@ -3472,55 +4407,49 @@ FRESULT f_truncate ( ) { FRESULT res; + FATFS *fs; DWORD ncl; - res = validate(fp); /* Check validity of the object */ - if (res == FR_OK) { - if (fp->err) { /* Check error */ - res = (FRESULT)fp->err; - } else { - if (!(fp->flag & FA_WRITE)) /* Check access mode */ - res = FR_DENIED; - } - } - if (res == FR_OK) { - if (fp->fsize > fp->fptr) { - fp->fsize = fp->fptr; /* Set file size to current R/W point */ - fp->flag |= FA__WRITTEN; - if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ - res = remove_chain(fp->fs, fp->sclust); - fp->sclust = 0; - } else { /* When truncate a part of the file, remove remaining clusters */ - ncl = get_fat(fp->fs, fp->clust); - res = FR_OK; - if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; - if (ncl == 1) res = FR_INT_ERR; - if (res == FR_OK && ncl < fp->fs->n_fatent) { - res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF); - if (res == FR_OK) res = remove_chain(fp->fs, ncl); - } + res = validate(fp, &fs); /* Check validity of the object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + if (fp->obj.objsize > fp->fptr) { + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fp->obj.sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(&fp->obj, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fs->n_fatent) { + res = remove_chain(&fp->obj, ncl, fp->clust); } + } + fp->obj.objsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA_MODIFIED; #if !_FS_TINY - if (res == FR_OK && (fp->flag & FA__DIRTY)) { - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - res = FR_DISK_ERR; - else - fp->flag &= ~FA__DIRTY; + if (res == FR_OK && (fp->flag & FA_DIRTY)) { + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fp->flag &= ~FA_DIRTY; } -#endif } - if (res != FR_OK) fp->err = (FRESULT)res; +#endif + if (res != FR_OK) ABORT(fs, res); } - LEAVE_FF(fp->fs, res); + LEAVE_FF(fs, res); } /*-----------------------------------------------------------------------*/ -/* Delete a File or Directory */ +/* Delete a File/Directory */ /*-----------------------------------------------------------------------*/ FRESULT f_unlink ( @@ -3529,60 +4458,86 @@ FRESULT f_unlink ( { FRESULT res; DIR dj, sdj; - BYTE *dir; DWORD dclst = 0; - DEFINE_NAMEBUF; + FATFS *fs; +#if _FS_EXFAT + _FDID obj; +#endif + DEF_NAMBUF /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); + res = find_volume(&path, &fs, FA_WRITE); + dj.obj.fs = fs; if (res == FR_OK) { - INIT_BUF(dj); + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ - if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) + if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { res = FR_INVALID_NAME; /* Cannot remove dot entry */ -#if _FS_LOCK - if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open object */ + } +#if _FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */ #endif if (res == FR_OK) { /* The object is accessible */ - dir = dj.dir; - if (!dir) { + if (dj.fn[NSFLAG] & NS_NONAME) { res = FR_INVALID_NAME; /* Cannot remove the origin directory */ } else { - if (dir[DIR_Attr] & AM_RDO) + if (dj.obj.attr & AM_RDO) { res = FR_DENIED; /* Cannot remove R/O object */ + } } if (res == FR_OK) { - dclst = ld_clust(dj.fs, dir); - if (dclst && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-directory ? */ -#if _FS_RPATH - if (dclst == dj.fs->cdir) { /* Is it the current directory? */ +#if _FS_EXFAT + obj.fs = fs; + if (fs->fs_type == FS_EXFAT) { + obj.sclust = dclst = ld_dword(fs->dirbuf + XDIR_FstClus); + obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + dclst = ld_clust(fs, dj.dir); + } + if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory ? */ +#if _FS_RPATH != 0 + if (dclst == fs->cdir) { /* Is it the current directory? */ res = FR_DENIED; } else #endif { - mem_cpy(&sdj, &dj, sizeof (DIR)); /* Open the sub-directory */ - sdj.sclust = dclst; - res = dir_sdi(&sdj, 2); + sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.sclust = dclst; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + sdj.obj.objsize = obj.objsize; + sdj.obj.stat = obj.stat; + } +#endif + res = dir_sdi(&sdj, 0); if (res == FR_OK) { - res = dir_read(&sdj, 0); /* Read an item (excluding dot entries) */ - if (res == FR_OK) res = FR_DENIED; /* Not empty? (cannot remove) */ - if (res == FR_NO_FILE) res = FR_OK; /* Empty? (can remove) */ + res = dir_read(&sdj, 0); /* Read an item */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? */ } } } } if (res == FR_OK) { - res = dir_remove(&dj); /* Remove the directory entry */ - if (res == FR_OK && dclst) /* Remove the cluster chain if exist */ - res = remove_chain(dj.fs, dclst); - if (res == FR_OK) res = sync_fs(dj.fs); + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst) { /* Remove the cluster chain if exist */ +#if _FS_EXFAT + res = remove_chain(&obj, dclst, 0); +#else + res = remove_chain(&dj.obj, dclst, 0); +#endif + } + if (res == FR_OK) res = sync_fs(fs); } } - FREE_BUF(); + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } @@ -3598,70 +4553,191 @@ FRESULT f_mkdir ( { FRESULT res; DIR dj; - BYTE *dir, n; - DWORD dsc, dcl, pcl, tm = GET_FATTIME(); - DEFINE_NAMEBUF; + FATFS *fs; + BYTE *dir; + UINT n; + DWORD dsc, dcl, pcl, tm; + DEF_NAMBUF /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); + res = find_volume(&path, &fs, FA_WRITE); + dj.obj.fs = fs; if (res == FR_OK) { - INIT_BUF(dj); + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ - if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { res = FR_INVALID_NAME; + } if (res == FR_NO_FILE) { /* Can create a new directory */ - dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ + dcl = create_chain(&dj.obj, 0); /* Allocate a cluster for the new directory table */ + dj.obj.objsize = (DWORD)fs->csize * SS(fs); res = FR_OK; if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ if (dcl == 1) res = FR_INT_ERR; if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; - if (res == FR_OK) /* Flush FAT */ - res = sync_window(dj.fs); + if (res == FR_OK) res = sync_window(fs); /* Flush FAT */ + tm = GET_FATTIME(); if (res == FR_OK) { /* Initialize the new directory table */ - dsc = clust2sect(dj.fs, dcl); - dir = dj.fs->win; - mem_set(dir, 0, SS(dj.fs)); - mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ - dir[DIR_Name] = '.'; - dir[DIR_Attr] = AM_DIR; - ST_DWORD(dir + DIR_WrtTime, tm); - st_clust(dir, dcl); - mem_cpy(dir + SZ_DIRE, dir, SZ_DIRE); /* Create ".." entry */ - dir[SZ_DIRE + 1] = '.'; pcl = dj.sclust; - if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) - pcl = 0; - st_clust(dir + SZ_DIRE, pcl); - for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ - dj.fs->winsect = dsc++; - dj.fs->wflag = 1; - res = sync_window(dj.fs); + dsc = clust2sect(fs, dcl); + dir = fs->win; + mem_set(dir, 0, SS(fs)); + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + st_dword(dir + DIR_ModTime, tm); + st_clust(fs, dir, dcl); + mem_cpy(dir + SZDIRE, dir, SZDIRE); /* Create ".." entry */ + dir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; + if (fs->fs_type == FS_FAT32 && pcl == fs->dirbase) pcl = 0; + st_clust(fs, dir + SZDIRE, pcl); + } + for (n = fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + fs->winsect = dsc++; + fs->wflag = 1; + res = sync_window(fs); if (res != FR_OK) break; - mem_set(dir, 0, SS(dj.fs)); + mem_set(dir, 0, SS(fs)); } } if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ - if (res != FR_OK) { - remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */ + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */ + st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize); /* File size needs to be valid */ + st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize); + fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag (contiguous) */ + fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ + res = store_xdir(&dj); + } else +#endif + { + dir = dj.dir; + st_dword(dir + DIR_ModTime, tm); /* Created time */ + st_clust(fs, dir, dcl); /* Table start cluster */ + dir[DIR_Attr] = AM_DIR; /* Attribute */ + fs->wflag = 1; + } + if (res == FR_OK) res = sync_fs(fs); } else { - dir = dj.dir; - dir[DIR_Attr] = AM_DIR; /* Attribute */ - ST_DWORD(dir + DIR_WrtTime, tm); /* Created time */ - st_clust(dir, dcl); /* Table start cluster */ - dj.fs->wflag = 1; - res = sync_fs(dj.fs); + remove_chain(&dj.obj, dcl, 0); /* Could not register, remove cluster chain */ } } - FREE_BUF(); + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } +/*-----------------------------------------------------------------------*/ +/* Rename a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object name to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + FATFS *fs; + BYTE buf[_FS_EXFAT ? SZDIRE * 2 : 24], *dir; + DWORD dw; + DEF_NAMBUF + + + get_ldnumber(&path_new); /* Ignore drive number of new name */ + res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive number of the old object */ + if (res == FR_OK) { + djo.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&djo, path_old); /* Check old object */ + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ +#if _FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&djo, 2); +#endif + if (res == FR_OK) { /* Object to be renamed is found */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At exFAT */ + BYTE nf, nn; + WORD nh; + + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ + mem_cpy(&djn, &djo, sizeof djo); + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) res = FR_EXIST; /* Is new name already in use? */ + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; + nh = ld_word(fs->dirbuf + XDIR_NameHash); + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); + fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; + st_word(fs->dirbuf + XDIR_NameHash, nh); +/* Start of critical section where any interruption can cause a cross-link */ + res = store_xdir(&djn); + } + } + } else +#endif + { /* At FAT12/FAT16/FAT32 */ + mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about the object except name */ + mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) res = FR_EXIST; /* Is new name already in use? */ + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy information about object except name */ + mem_cpy(dir + 13, buf + 2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */ + dw = clust2sect(fs, ld_clust(fs, dir)); + if (!dw) { + res = FR_INT_ERR; + } else { +/* Start of critical section where any interruption can cause a cross-link */ + res = move_window(fs, dw); + dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(fs, dir, djn.obj.sclust); + fs->wflag = 1; + } + } + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) { + res = sync_fs(fs); + } + } +/* End of critical section */ + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +#if _USE_CHMOD && !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Change Attribute */ /*-----------------------------------------------------------------------*/ @@ -3674,109 +4750,34 @@ FRESULT f_chmod ( { FRESULT res; DIR dj; - BYTE *dir; - DEFINE_NAMEBUF; + FATFS *fs; + DEF_NAMBUF - res = find_volume(&dj.fs, &path, 1); /* Get logical drive number */ + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive number */ + dj.obj.fs = fs; if (res == FR_OK) { - INIT_BUF(dj); - res = follow_path(&dj, path); /* Follow the file path */ - FREE_BUF(); - if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) - res = FR_INVALID_NAME; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ if (res == FR_OK) { - dir = dj.dir; - if (!dir) { /* Is it a root directory? */ - res = FR_INVALID_NAME; - } else { /* File or sub directory */ - mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ - dir[DIR_Attr] = (attr & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ - dj.fs->wflag = 1; - res = sync_fs(dj.fs); - } - } - } - - LEAVE_FF(dj.fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Rename File/Directory */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_rename ( - const TCHAR* path_old, /* Pointer to the object to be renamed */ - const TCHAR* path_new /* Pointer to the new name */ -) -{ - FRESULT res; - DIR djo, djn; - BYTE buf[21], *dir; - DWORD dw; - DEFINE_NAMEBUF; - - - /* Get logical drive number of the source object */ - res = find_volume(&djo.fs, &path_old, 1); - if (res == FR_OK) { - djn.fs = djo.fs; - INIT_BUF(djo); - res = follow_path(&djo, path_old); /* Check old object */ - if (_FS_RPATH && res == FR_OK && (djo.fn[NSFLAG] & NS_DOT)) - res = FR_INVALID_NAME; -#if _FS_LOCK - if (res == FR_OK) res = chk_lock(&djo, 2); + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + res = store_xdir(&dj); + } else #endif - if (res == FR_OK) { /* Old object is found */ - if (!djo.dir) { /* Is root dir? */ - res = FR_NO_FILE; - } else { - mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about object except name */ - mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ - if (get_ldnumber(&path_new) >= 0) /* Snip drive number off and ignore it */ - res = follow_path(&djn, path_new); /* and make sure if new object name is not conflicting */ - else - res = FR_INVALID_DRIVE; - if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ - if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ - res = dir_register(&djn); /* Register the new entry */ - if (res == FR_OK) { -/* Start of critical section where any interruption can cause a cross-link */ - dir = djn.dir; /* Copy information about object except name */ - mem_cpy(dir + 13, buf + 2, 19); - dir[DIR_Attr] = buf[0] | AM_ARC; - djo.fs->wflag = 1; - if ((dir[DIR_Attr] & AM_DIR) && djo.sclust != djn.sclust) { /* Update .. entry in the sub-directory if needed */ - dw = clust2sect(djo.fs, ld_clust(djo.fs, dir)); - if (!dw) { - res = FR_INT_ERR; - } else { - res = move_window(djo.fs, dw); - dir = djo.fs->win + SZ_DIRE * 1; /* Ptr to .. entry */ - if (res == FR_OK && dir[1] == '.') { - st_clust(dir, djn.sclust); - djo.fs->wflag = 1; - } - } - } - if (res == FR_OK) { - res = dir_remove(&djo); /* Remove old entry */ - if (res == FR_OK) - res = sync_fs(djo.fs); - } -/* End of critical section */ - } - } + { + dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + fs->wflag = 1; } + if (res == FR_OK) res = sync_fs(fs); } - FREE_BUF(); + FREE_NAMBUF(); } - LEAVE_FF(djo.fs, res); + LEAVE_FF(fs, res); } @@ -3793,45 +4794,42 @@ FRESULT f_utime ( { FRESULT res; DIR dj; - BYTE *dir; - DEFINE_NAMEBUF; + FATFS *fs; + DEF_NAMBUF - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive number */ + dj.obj.fs = fs; if (res == FR_OK) { - INIT_BUF(dj); + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ - FREE_BUF(); - if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) - res = FR_INVALID_NAME; + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ if (res == FR_OK) { - dir = dj.dir; - if (!dir) { /* Root directory */ - res = FR_INVALID_NAME; - } else { /* File or sub-directory */ - ST_WORD(dir + DIR_WrtTime, fno->ftime); - ST_WORD(dir + DIR_WrtDate, fno->fdate); - dj.fs->wflag = 1; - res = sync_fs(dj.fs); +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + res = store_xdir(&dj); + } else +#endif + { + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + fs->wflag = 1; } + if (res == FR_OK) res = sync_fs(fs); } + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } -#endif /* !_FS_READONLY */ -#endif /* _FS_MINIMIZE == 0 */ -#endif /* _FS_MINIMIZE <= 1 */ -#endif /* _FS_MINIMIZE <= 2 */ - +#endif /* _USE_CHMOD && !_FS_READONLY */ #if _USE_LABEL /*-----------------------------------------------------------------------*/ -/* Get volume label */ +/* Get Volume Label */ /*-----------------------------------------------------------------------*/ FRESULT f_getlabel ( @@ -3842,63 +4840,85 @@ FRESULT f_getlabel ( { FRESULT res; DIR dj; - UINT i, j; -#if _USE_LFN && _LFN_UNICODE + FATFS *fs; + UINT si, di; +#if _LFN_UNICODE || _FS_EXFAT WCHAR w; #endif - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 0); + res = find_volume(&path, &fs, 0); /* Get volume label */ if (res == FR_OK && label) { - dj.sclust = 0; /* Open root directory */ + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ res = dir_sdi(&dj, 0); if (res == FR_OK) { - res = dir_read(&dj, 1); /* Get an entry with AM_VOL */ - if (res == FR_OK) { /* A volume label is exist */ -#if _USE_LFN && _LFN_UNICODE - i = j = 0; - do { - w = (i < 11) ? dj.dir[i++] : ' '; - if (IsDBCS1(w) && i < 11 && IsDBCS2(dj.dir[i])) - w = w << 8 | dj.dir[i++]; - label[j++] = ff_convert(w, 1); /* OEM -> Unicode */ - } while (j < 11); + res = dir_read(&dj, 1); /* Find a volume label entry */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + for (si = di = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ + w = ld_word(dj.dir + XDIR_Label + si * 2); +#if _LFN_UNICODE + label[di++] = w; #else - mem_cpy(label, dj.dir, 11); + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) w = '?'; /* Replace wrong character */ + if (_DF1S && w >= 0x100) label[di++] = (char)(w >> 8); + label[di++] = (char)w; #endif - j = 11; - do { - label[j] = 0; - if (!j) break; - } while (label[--j] == ' '); - } - if (res == FR_NO_FILE) { /* No label, return nul string */ - label[0] = 0; - res = FR_OK; + } + label[di] = 0; + } else +#endif + { + si = di = 0; /* Extract volume label from AM_VOL entry with code comversion */ + do { +#if _LFN_UNICODE + w = (si < 11) ? dj.dir[si++] : ' '; + if (IsDBCS1(w) && si < 11 && IsDBCS2(dj.dir[si])) { + w = w << 8 | dj.dir[si++]; + } + label[di++] = ff_convert(w, 1); /* OEM -> Unicode */ +#else + label[di++] = dj.dir[si++]; +#endif + } while (di < 11); + do { /* Truncate trailing spaces */ + label[di] = 0; + if (di == 0) break; + } while (label[--di] == ' '); + } } } + if (res == FR_NO_FILE) { /* No label entry and return nul string */ + label[0] = 0; + res = FR_OK; + } } /* Get volume serial number */ if (res == FR_OK && vsn) { - res = move_window(dj.fs, dj.fs->volbase); + res = move_window(fs, fs->volbase); if (res == FR_OK) { - i = dj.fs->fs_type == FS_FAT32 ? BS_VolID32 : BS_VolID; - *vsn = LD_DWORD(&dj.fs->win[i]); + switch (fs->fs_type) { + case FS_EXFAT: di = BPB_VolIDEx; break; + case FS_FAT32: di = BS_VolID32; break; + default: di = BS_VolID; + } + *vsn = ld_dword(fs->win + di); } } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } #if !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Set volume label */ +/* Set Volume Label */ /*-----------------------------------------------------------------------*/ FRESULT f_setlabel ( @@ -3907,84 +4927,115 @@ FRESULT f_setlabel ( { FRESULT res; DIR dj; - BYTE vn[11]; - UINT i, j, sl; + FATFS *fs; + BYTE dirvn[22]; + UINT i, j, slen; WCHAR w; - DWORD tm; + static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F"; /* Get logical drive number */ - res = find_volume(&dj.fs, &label, 1); - if (res) LEAVE_FF(dj.fs, res); + res = find_volume(&label, &fs, FA_WRITE); + if (res != FR_OK) LEAVE_FF(fs, res); + dj.obj.fs = fs; - /* Create a volume label in directory form */ - vn[0] = 0; - for (sl = 0; label[sl]; sl++) ; /* Get name length */ - for ( ; sl && label[sl - 1] == ' '; sl--) ; /* Remove trailing spaces */ - if (sl) { /* Create volume label in directory form */ - i = j = 0; - do { -#if _USE_LFN && _LFN_UNICODE - w = ff_convert(ff_wtoupper(label[i++]), 0); + /* Get length of given volume label */ + for (slen = 0; (UINT)label[slen] >= ' '; slen++) ; /* Get name length */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + for (i = j = 0; i < slen; ) { /* Create volume label in directory form */ + w = label[i++]; +#if !_LFN_UNICODE + if (IsDBCS1(w)) { + w = (i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; + } + w = ff_convert(w, 1); +#endif + if (w == 0 || chk_chr(badchr, w) || j == 22) { /* Check validity check validity of the volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + st_word(dirvn + j, w); j += 2; + } + slen = j; + } else +#endif + { /* At the FAT12/16/32 */ + for ( ; slen && label[slen - 1] == ' '; slen--) ; /* Remove trailing spaces */ + if (slen) { /* Is there a volume label to be set? */ + dirvn[0] = 0; i = j = 0; /* Create volume label in directory form */ + do { +#if _LFN_UNICODE + w = ff_convert(ff_wtoupper(label[i++]), 0); #else - w = (BYTE)label[i++]; - if (IsDBCS1(w)) - w = (j < 10 && i < sl && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; -#if _USE_LFN - w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); + w = (BYTE)label[i++]; + if (IsDBCS1(w)) { + w = (j < 10 && i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; + } +#if _USE_LFN != 0 + w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); #else - if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ + if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ #ifdef _EXCVT - if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ + if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ #else - if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */ + if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */ #endif #endif #endif - if (!w || chk_chr("\"*+,.:;<=>\?[]|\x7F", w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) /* Reject invalid characters for volume label */ - LEAVE_FF(dj.fs, FR_INVALID_NAME); - if (w >= 0x100) vn[j++] = (BYTE)(w >> 8); - vn[j++] = (BYTE)w; - } while (i < sl); - while (j < 11) vn[j++] = ' '; /* Fill remaining name field */ - if (vn[0] == DDEM) LEAVE_FF(dj.fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + if (w == 0 || chk_chr(badchr, w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + if (w >= 0x100) dirvn[j++] = (BYTE)(w >> 8); + dirvn[j++] = (BYTE)w; + } while (i < slen); + while (j < 11) dirvn[j++] = ' '; /* Fill remaining name field */ + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + } } /* Set volume label */ - dj.sclust = 0; /* Open root directory */ + dj.obj.sclust = 0; /* Open root directory */ res = dir_sdi(&dj, 0); if (res == FR_OK) { - res = dir_read(&dj, 1); /* Get an entry with AM_VOL */ - if (res == FR_OK) { /* A volume label is found */ - if (vn[0]) { - mem_cpy(dj.dir, vn, 11); /* Change the volume label name */ - tm = GET_FATTIME(); - ST_DWORD(dj.dir + DIR_WrtTime, tm); + res = dir_read(&dj, 1); /* Get volume label entry */ + if (res == FR_OK) { + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_NumLabel] = slen / 2; /* Change the volume label */ + mem_cpy(dj.dir + XDIR_Label, dirvn, slen); } else { - dj.dir[0] = DDEM; /* Remove the volume label */ + if (slen) { + mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + } else { + dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ + } } - dj.fs->wflag = 1; - res = sync_fs(dj.fs); - } else { /* No volume label is found or error */ + fs->wflag = 1; + res = sync_fs(fs); + } else { /* No volume label entry is found or error */ if (res == FR_NO_FILE) { res = FR_OK; - if (vn[0]) { /* Create volume label as new */ - res = dir_alloc(&dj, 1); /* Allocate an entry for volume label */ + if (slen) { /* Create a volume label entry */ + res = dir_alloc(&dj, 1); /* Allocate an entry */ if (res == FR_OK) { - mem_set(dj.dir, 0, SZ_DIRE); /* Set volume label */ - mem_cpy(dj.dir, vn, 11); - dj.dir[DIR_Attr] = AM_VOL; - tm = GET_FATTIME(); - ST_DWORD(dj.dir + DIR_WrtTime, tm); - dj.fs->wflag = 1; - res = sync_fs(dj.fs); + mem_set(dj.dir, 0, SZDIRE); /* Clear the entry */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */ + dj.dir[XDIR_NumLabel] = slen / 2; + mem_cpy(dj.dir + XDIR_Label, dirvn, slen); + } else { + dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ + mem_cpy(dj.dir, dirvn, 11); + } + fs->wflag = 1; + res = sync_fs(fs); } } } } } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } #endif /* !_FS_READONLY */ @@ -3992,10 +5043,94 @@ FRESULT f_setlabel ( +#if _USE_EXPAND && !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Forward data to the stream directly (available on only tiny cfg) */ +/* Allocate a Contiguous Blocks to the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_expand ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t fsz, /* File size to be expanded to */ + BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, stcl, scl, ncl, tcl, lclst; + + + res = validate(fp, &fs); /* Check validity of the object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */ +#endif + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */ + stcl = fs->last_clst; lclst = 0; + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */ + if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */ + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) { + if (opt) { + res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */ + lclst = scl + tcl - 1; + } else { + lclst = scl - 1; + } + } + } else +#endif + { + scl = clst = stcl; ncl = 0; + for (;;) { /* Find a contiguous cluster block */ + n = get_fat(&fp->obj, clst); + if (++clst >= fs->n_fatent) clst = 2; + if (n == 1) { res = FR_INT_ERR; break; } + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (n == 0) { /* Is it a free cluster? */ + if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */ + } else { + scl = clst; ncl = 0; /* Not a free cluster */ + } + if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */ + } + if (res == FR_OK) { + if (opt) { + for (clst = scl; tcl; clst++, tcl--) { /* Create a cluster chain on the FAT */ + res = put_fat(fs, clst, (tcl == 1) ? 0xFFFFFFFF : clst + 1); + if (res != FR_OK) break; + lclst = clst; + } + } else { + lclst = scl - 1; + } + } + } + + if (opt && res == FR_OK) { + fs->last_clst = lclst; /* Set suggested start cluster to start next */ + fp->obj.sclust = scl; /* Update object allocation information */ + fp->obj.objsize = fsz; + if (_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ + fp->flag |= FA_MODIFIED; + } + + LEAVE_FF(fs, res); +} + +#endif /* _USE_EXPAND && !_FS_READONLY */ + + + +#if _USE_FORWARD +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly */ /*-----------------------------------------------------------------------*/ -#if _USE_FORWARD && _FS_TINY FRESULT f_forward ( FIL* fp, /* Pointer to the file object */ @@ -4005,48 +5140,59 @@ FRESULT f_forward ( ) { FRESULT res; - DWORD remain, clst, sect; - UINT rcnt; - BYTE csect; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, csect; + BYTE *dbuf; *bf = 0; /* Clear transfer byte counter */ + res = validate(fp, &fs); /* Check validity of the object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ - res = validate(fp); /* Check validity of the object */ - if (res != FR_OK) LEAVE_FF(fp->fs, res); - if (fp->err) /* Check error */ - LEAVE_FF(fp->fs, (FRESULT)fp->err); - if (!(fp->flag & FA_READ)) /* Check access mode */ - LEAVE_FF(fp->fs, FR_DENIED); - - remain = fp->fsize - fp->fptr; + remain = fp->obj.objsize - fp->fptr; if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ - for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */ + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */ fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { - csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ - if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ - if (!csect) { /* On the cluster boundary? */ + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + if (csect == 0) { /* On the cluster boundary? */ clst = (fp->fptr == 0) ? /* On the top of the file? */ - fp->sclust : get_fat(fp->fs, fp->clust); - if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); - if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->obj.sclust : get_fat(&fp->obj, fp->clust); + if (clst <= 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); fp->clust = clst; /* Update current cluster */ } } - sect = clust2sect(fp->fs, fp->clust); /* Get current data sector */ - if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect = clust2sect(fs, fp->clust); /* Get current data sector */ + if (!sect) ABORT(fs, FR_INT_ERR); sect += csect; - if (move_window(fp->fs, sect) != FR_OK) /* Move sector window */ - ABORT(fp->fs, FR_DISK_ERR); - fp->dsect = sect; - rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ - if (rcnt > btf) rcnt = btf; - rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); - if (!rcnt) ABORT(fp->fs, FR_INT_ERR); +#if _FS_TINY + if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */ + dbuf = fs->win; +#else + if (fp->sect != sect) { /* Fill sector cache with file data */ +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= ~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + } + dbuf = fp->buf; +#endif + fp->sect = sect; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */ + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */ + if (!rcnt) ABORT(fs, FR_INT_ERR); } - LEAVE_FF(fp->fs, FR_OK); + LEAVE_FF(fs, FR_OK); } #endif /* _USE_FORWARD */ @@ -4054,249 +5200,452 @@ FRESULT f_forward ( #if _USE_MKFS && !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Create file system on the logical drive */ +/* Create FAT file system on the logical drive */ /*-----------------------------------------------------------------------*/ -#define N_ROOTDIR 512 /* Number of root directory entries for FAT12/16 */ -#define N_FATS 1 /* Number of FATs (1 or 2) */ - FRESULT f_mkfs ( const TCHAR* path, /* Logical drive number */ - BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ - UINT au /* Size of allocation unit in unit of byte or sector */ + BYTE opt, /* Format option */ + DWORD au, /* Size of allocation unit [byte] */ + void* work, /* Pointer to working buffer */ + UINT len /* Size of working buffer */ ) { - static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0}; - static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; + const UINT n_fats = 1; /* Number of FATs for FAT12/16/32 volume (1 or 2) */ + const UINT n_rootdir = 512; /* Number of root directory entries for FAT12/16 volume */ + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT12/16 volume (4KS unit) */ + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128KS unit) */ + BYTE fmt, sys, *buf, *pte, pdrv, part; + WORD ss; + DWORD n, pau, n_clst, sz_blk, sect, szb_buf, sz_buf; + DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ + DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ + UINT i, ns; int vol; - BYTE fmt, md, sys, *tbl, pdrv, part; - DWORD n_clst, vs, n, wsect; - UINT i; - DWORD b_vol, b_fat, b_dir, b_data; /* LBA */ - DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ - FATFS *fs; DSTATUS stat; -#if _USE_TRIM - DWORD eb[2]; +#if _USE_TRIM || _FS_EXFAT + DWORD tbl[3]; #endif /* Check mounted drive and clear work area */ - if (sfd > 1) return FR_INVALID_PARAMETER; - vol = get_ldnumber(&path); + vol = get_ldnumber(&path); /* Get target logical drive */ if (vol < 0) return FR_INVALID_DRIVE; - fs = FatFs[vol]; - if (!fs) return FR_NOT_ENABLED; - fs->fs_type = 0; + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear mounted volume */ pdrv = LD2PD(vol); /* Physical drive */ - part = LD2PT(vol); /* Partition (0:auto detect, 1-4:get from partition table)*/ + part = LD2PT(vol); /* Partition (0:create as new, 1-4:get by partition table) */ - /* Get disk statics */ + /* Check physical drive status */ stat = disk_initialize(pdrv); if (stat & STA_NOINIT) return FR_NOT_READY; if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; -#if _MAX_SS != _MIN_SS /* Get disk sector size */ - if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_SS) - return FR_DISK_ERR; + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; +#if _MAX_SS != _MIN_SS /* Get sector size of the medium */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; + if (ss > _MAX_SS || ss < _MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; +#else + ss = _MAX_SS; #endif - if (_MULTI_PARTITION && part) { + if ((au != 0 && au < ss) || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */ + au /= ss; /* Cluster size in byte to in sector */ + if (au > 32768) return FR_INVALID_PARAMETER; + + /* Set size and pointer of the working buffer */ + buf = (BYTE*)work; /* Use given working buffer */ + if (len < ss) return FR_MKFS_ABORTED; + szb_buf = len & ~(ss - 1); /* Round-down by sector size [byte] */ + sz_buf = szb_buf / ss; /* Size of sector buffer [sector] */ + + /* Determine where the volume to be located (b_vol, sz_vol) */ + if (_MULTI_PARTITION && part != 0) { /* Get partition information from partition table in the MBR */ - if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; - if (LD_WORD(fs->win + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; - tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; - if (!tbl[4]) return FR_MKFS_ABORTED; /* No partition? */ - b_vol = LD_DWORD(tbl + 8); /* Volume start sector */ - n_vol = LD_DWORD(tbl + 12); /* Volume size */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Load MBR */ + if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check MBR is valid */ + pte = buf + (MBR_Table + (part - 1) * SZ_PTE); + if (!pte[PTE_System]) return FR_MKFS_ABORTED; /* No partition? */ + b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */ } else { - /* Create a partition in this function */ - if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) - return FR_DISK_ERR; - b_vol = (sfd) ? 0 : 63; /* Volume start sector */ - n_vol -= b_vol; /* Volume size */ + /* Create a single-partition in this function */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR; + b_vol = (opt & FM_SFD) ? 0 : 63; /* Volume start sector */ + if (sz_vol < b_vol) return FR_MKFS_ABORTED; + sz_vol -= b_vol; /* Volume size */ } + if (sz_vol < 128) return FR_MKFS_ABORTED; /* Check volume size (>=128s) */ - if (au & (au - 1)) au = 0; - if (!au) { /* AU auto selection */ - vs = n_vol / (2000 / (SS(fs) / 512)); - for (i = 0; vs < vst[i]; i++) ; - au = cst[i]; + /* Pre-determine the FAT type by argument */ + do { + if (_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */ + if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au >= 256) { /* exFAT only, vol >= 64Ms or au >= 256s ? */ + fmt = FS_EXFAT; break; + } + } + if (au >= 256) return FR_INVALID_PARAMETER; /* Too large au for FAT/FAT32 */ + if (opt & FM_FAT32) { /* FAT32 possible? */ + if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ + fmt = FS_FAT32; break; + } + } + if (!(opt & FM_FAT)) return FR_INVALID_PARAMETER; /* no-FAT? */ + fmt = FS_FAT16; + } while (0); + +#if _FS_EXFAT + if (fmt == FS_EXFAT) { /* Create an exFAT volume */ + DWORD sum, szb_bit, szb_case; + WCHAR ch, si; + UINT j, st; + BYTE b; + + if (sz_vol < 0x1000) return FR_MKFS_ABORTED; /* Too small volume? */ +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Determine FAT location, data location and number of clusters */ + if (!au) { /* au auto-selection */ + au = 8; + if (sz_vol >= 0x80000) au = 64; /* >= 512KS */ + if (sz_vol >= 0x4000000) au = 256; /* >= 64MS */ + } + b_fat = b_vol + 32; /* FAT start at offset 32 */ + sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Numbef of FAT sectors */ + b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */ + if (b_data >= sz_vol / 2) return FR_MKFS_ABORTED; /* Too small volume? */ + n_clst = (sz_vol - (b_data - b_vol)) / au; /* Nunber of clusters */ + if (n_clst <16) return FR_MKFS_ABORTED; /* Too few clusters? */ + if (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED; /* Too many clusters? */ + + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of bitmap clusters */ + tbl[2] = 1; /* Number of rootdir clusters */ + + /* Create a compressed up-case table */ + sect = b_data + au * tbl[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ + st = si = i = j = szb_case = 0; + do { + switch (st) { + case 0: + ch = ff_wtoupper(si); /* Get an up-case char */ + if (ch != si) { + si++; break; /* Store the up-case char if exist */ + } + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ + if (j >= 128) { + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */ + } + st = 1; /* Do not compress short run */ + /* continue */ + case 1: + ch = si++; /* Fill the short run */ + if (--j == 0) st = 0; + break; + default: + ch = (WCHAR)j; si += j; /* Number of chars to skip */ + st = 0; + } + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); + i += 2; szb_case += 2; + if (!si || i == szb_buf) { /* Write buffered data when buffer full or end of process */ + ns = (i + ss - 1) / ss; + if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR; + sect += ns; i = 0; + } + } while (si); + tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case clusters */ + + /* Initialize the allocation bitmap */ + mem_set(buf, 0, szb_buf); /* Set in-use flags of bitmap, up-case and root dir */ + for (i = 0, n = tbl[0] + tbl[1] + tbl[2]; n >= 8; buf[i++] = 0xFF, n -= 8) ; + for (b = 1; n; buf[i] |= b, b <<= 1, n--) ; + sect = b_data; n = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of the sectors */ + do { /* Fill allocation bitmap sectors */ + ns = (n > sz_buf) ? sz_buf : n; + if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR; + sect += ns; + mem_set(buf, 0, ss); + } while (n -= ns); + + /* Initialize the FAT */ + st_qword(buf, 0xFFFFFFFFFFFFFFF8); /* Entry 0 and 1 */ + for (j = 0, i = 2; j < 3; j++) { /* Set entries of bitmap, up-case and root dir */ + for (n = tbl[j]; n; n--) { + st_dword(buf + i * 4, (n >= 2) ? i + 1 : 0xFFFFFFFF); + i++; + } + } + sect = b_fat; n = sz_fat; /* Start of FAT and number of the sectors */ + do { /* Fill FAT sectors */ + ns = (n > sz_buf) ? sz_buf : n; + if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR; + sect += ns; + mem_set(buf, 0, ss); + } while (n -= ns); + + /* Initialize the root directory */ + mem_set(buf, 0, ss); + buf[SZDIRE * 0 + 0] = 0x83; /* 83 entry (volume label) */ + buf[SZDIRE * 1 + 0] = 0x81; /* 81 entry (allocation bitmap) */ + st_dword(buf + SZDIRE * 1 + 20, 2); + st_dword(buf + SZDIRE * 1 + 24, szb_bit); + buf[SZDIRE * 2 + 0] = 0x82; /* 82 entry (up-case table) */ + st_dword(buf + SZDIRE * 2 + 4, sum); + st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); + st_dword(buf + SZDIRE * 2 + 24, szb_case); + sect = b_data + au * (tbl[0] + tbl[1]); n = au; /* Start of directory and number of the sectors */ + do { /* Fill root direcotry sectors */ + ns = (n > sz_buf) ? sz_buf : n; + if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR; + sect += ns; + mem_set(buf, 0, ss); + } while (n -= ns); + + /* Create two set of the exFAT VBR blocks */ + sect = b_vol; + for (n = 0; n < 2; n++) { + /* Main record (+0) */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ + st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ + st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ + st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */ + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */ + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ + st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FSVerEx, 0x100); /* File system version (1.00) */ + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ + for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ + buf[BPB_NumFATsEx] = 1; /* Number of FATs */ + buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ + st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); + } + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + /* Extended bootstrap record (+1..+8) */ + mem_set(buf, 0, ss); + st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ + for (j = 1; j < 9; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* OEM/Reserved record (+9..+10) */ + mem_set(buf, 0, ss); + for ( ; j < 11; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* Sum record (+11) */ + for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + + } else +#endif + { /* Create an FAT12/16/32 volume */ + do { + pau = au; + /* Pre-determine number of clusters and FAT sub-type */ + if (fmt == FS_FAT32) { /* FAT32 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x20000; /* Volume size in unit of 128KS */ + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; /* Number of clusters */ + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 32; /* Number of reserved sectors */ + sz_dir = 0; /* No static directory */ + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED; + } else { /* FAT12/16 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x1000; /* Volume size in unit of 4KS */ + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; + if (n_clst > MAX_FAT12) { + n = n_clst * 2 + 4; /* FAT size [byte] */ + } else { + fmt = FS_FAT12; + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */ + } + sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 1; /* Number of reserved sectors */ + sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */ + } + b_fat = b_vol + sz_rsv; /* FAT base */ + b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */ + + /* Align data base to erase block boundary (for flash memory media) */ + n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */ + if (fmt == FS_FAT32) { /* FAT32: Move FAT base */ + sz_rsv += n; b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + sz_fat += n / n_fats; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + if (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; + if (fmt == FS_FAT32) { + if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ + if (!au && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT16) { + if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ + if (!au && (pau * 2) <= 64) { + au = pau * 2; continue; /* Adjust cluster size and retry */ + } + if ((opt & FM_FAT32)) { + fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */ + } + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED; /* Too many clusters for FAT12 */ + + /* Ok, it is the valid cluster configuration */ + break; + } while (1); + +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Create FAT VBR */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ + buf[BPB_NumFATs] = n_fats; /* Number of FATs */ + st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ + if (sz_vol < 0x10000) { + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ + } else { + st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 12-bit LBA */ + } + buf[BPB_Media] = 0xF8; /* Media descriptor */ + st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ + st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ + st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ + if (fmt == FS_FAT32) { + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ + st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ + st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ + buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ + buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the VBR sector */ + + /* Create FSINFO record if needed */ + if (fmt == FS_FAT32) { + disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ + mem_set(buf, 0, ss); + st_dword(buf + FSI_LeadSig, 0x41615252); + st_dword(buf + FSI_StrucSig, 0x61417272); + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + st_word(buf + BS_55AA, 0xAA55); + disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ + disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ + } + + /* Initialize FAT area */ + mem_set(buf, 0, szb_buf); + sect = b_fat; /* Start sector */ + for (i = 0; i < n_fats; i++) { /* Initialize FATs each */ + if (fmt == FS_FAT32) { + st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */ + st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */ + st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */ + } else { + st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */ + } + n = sz_fat; /* Sector count of a FAT */ + do { /* Fill FAT sectors */ + ns = (n > sz_buf) ? sz_buf : n; + if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR; + sect += ns; + mem_set(buf, 0, ss); + } while (n -= ns); + } + + /* Initialize root directory (fill with zero) */ + n = (fmt == FS_FAT32) ? pau : sz_dir; /* Sector count of root directory */ + do { + ns = (n > sz_buf) ? sz_buf : n; + if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR; + sect += ns; + } while (n -= ns); } - if (au >= _MIN_SS) au /= SS(fs); /* Number of sectors per cluster */ - if (!au) au = 1; - if (au > 128) au = 128; - - /* Pre-compute number of clusters and FAT sub-type */ - n_clst = n_vol / au; - fmt = FS_FAT12; - if (n_clst >= MIN_FAT16) fmt = FS_FAT16; - if (n_clst >= MIN_FAT32) fmt = FS_FAT32; - - /* Determine offset and size of FAT structure */ - if (fmt == FS_FAT32) { - n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); - n_rsv = 32; - n_dir = 0; - } else { - n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4; - n_fat = (n_fat + SS(fs) - 1) / SS(fs); - n_rsv = 1; - n_dir = (DWORD)N_ROOTDIR * SZ_DIRE / SS(fs); - } - b_fat = b_vol + n_rsv; /* FAT area start sector */ - b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */ - b_data = b_dir + n_dir; /* Data area start sector */ - if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ - - /* Align data start sector to erase block boundary (for flash memory media) */ - if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1; - n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */ - n = (n - b_data) / N_FATS; - if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */ - n_rsv += n; - b_fat += n; - } else { /* FAT12/16: Expand FAT size */ - n_fat += n; - } - - /* Determine number of clusters and final check of validity of the FAT sub-type */ - n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au; - if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16) - || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) - return FR_MKFS_ABORTED; /* Determine system ID in the partition table */ - if (fmt == FS_FAT32) { - sys = 0x0C; /* FAT32X */ + if (_FS_EXFAT && fmt == FS_EXFAT) { + sys = 0x07; /* HPFS/NTFS/exFAT */ } else { - if (fmt == FS_FAT12 && n_vol < 0x10000) { - sys = 0x01; /* FAT12(<65536) */ + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ } else { - sys = (n_vol < 0x10000) ? 0x04 : 0x06; /* FAT16(<65536) : FAT12/16(>=65536) */ + if (sz_vol >= 0x10000) { + sys = 0x06; /* FAT12/16 (>=64KS) */ + } else { + sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 (<64KS) : FAT12 (<64KS) */ + } } } - if (_MULTI_PARTITION && part) { + if (_MULTI_PARTITION && part != 0) { /* Update system ID in the partition table */ - tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; - tbl[4] = sys; - if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to teh MBR */ - return FR_DISK_ERR; - md = 0xF8; + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Read the MBR */ + buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system type */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it back to the MBR */ } else { - if (sfd) { /* No partition table (SFD) */ - md = 0xF0; - } else { /* Create partition table (FDISK) */ - mem_set(fs->win, 0, SS(fs)); - tbl = fs->win + MBR_Table; /* Create partition table for single partition in the drive */ - tbl[1] = 1; /* Partition start head */ - tbl[2] = 1; /* Partition start sector */ - tbl[3] = 0; /* Partition start cylinder */ - tbl[4] = sys; /* System type */ - tbl[5] = 254; /* Partition end head */ - n = (b_vol + n_vol) / 63 / 255; - tbl[6] = (BYTE)(n >> 2 | 63); /* Partition end sector */ - tbl[7] = (BYTE)n; /* End cylinder */ - ST_DWORD(tbl + 8, 63); /* Partition start in LBA */ - ST_DWORD(tbl + 12, n_vol); /* Partition size in LBA */ - ST_WORD(fs->win + BS_55AA, 0xAA55); /* MBR signature */ - if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to the MBR */ - return FR_DISK_ERR; - md = 0xF8; + if (!(opt & FM_SFD)) { + /* Create partition table in FDISK format */ + mem_set(buf, 0, ss); + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ + pte = buf + MBR_Table; /* Create partition table for single partition in the drive */ + pte[PTE_Boot] = 0; /* Boot indicator */ + pte[PTE_StHead] = 1; /* Start head */ + pte[PTE_StSec] = 1; /* Start sector */ + pte[PTE_StCyl] = 0; /* Start cylinder */ + pte[PTE_System] = sys; /* System type */ + n = (b_vol + sz_vol) / (63 * 255); /* (End CHS is incorrect) */ + pte[PTE_EdHead] = 254; /* End head */ + pte[PTE_EdSec] = (BYTE)(n >> 2 | 63); /* End sector */ + pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */ + st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */ + st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */ } } - /* Create BPB in the VBR */ - tbl = fs->win; /* Clear sector */ - mem_set(tbl, 0, SS(fs)); - mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ - i = SS(fs); /* Sector size */ - ST_WORD(tbl + BPB_BytsPerSec, i); - tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ - ST_WORD(tbl + BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ - tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ - i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of root directory entries */ - ST_WORD(tbl + BPB_RootEntCnt, i); - if (n_vol < 0x10000) { /* Number of total sectors */ - ST_WORD(tbl + BPB_TotSec16, n_vol); - } else { - ST_DWORD(tbl + BPB_TotSec32, n_vol); - } - tbl[BPB_Media] = md; /* Media descriptor */ - ST_WORD(tbl + BPB_SecPerTrk, 63); /* Number of sectors per track */ - ST_WORD(tbl + BPB_NumHeads, 255); /* Number of heads */ - ST_DWORD(tbl + BPB_HiddSec, b_vol); /* Hidden sectors */ - n = GET_FATTIME(); /* Use current time as VSN */ - if (fmt == FS_FAT32) { - ST_DWORD(tbl + BS_VolID32, n); /* VSN */ - ST_DWORD(tbl + BPB_FATSz32, n_fat); /* Number of sectors per FAT */ - ST_DWORD(tbl + BPB_RootClus, 2); /* Root directory start cluster (2) */ - ST_WORD(tbl + BPB_FSInfo, 1); /* FSINFO record offset (VBR + 1) */ - ST_WORD(tbl + BPB_BkBootSec, 6); /* Backup boot record offset (VBR + 6) */ - tbl[BS_DrvNum32] = 0x80; /* Drive number */ - tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ - mem_cpy(tbl + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ - } else { - ST_DWORD(tbl + BS_VolID, n); /* VSN */ - ST_WORD(tbl + BPB_FATSz16, n_fat); /* Number of sectors per FAT */ - tbl[BS_DrvNum] = 0x80; /* Drive number */ - tbl[BS_BootSig] = 0x29; /* Extended boot signature */ - mem_cpy(tbl + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ - } - ST_WORD(tbl + BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */ - if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK) /* Write it to the VBR sector */ - return FR_DISK_ERR; - if (fmt == FS_FAT32) /* Write it to the backup VBR if needed (VBR + 6) */ - disk_write(pdrv, tbl, b_vol + 6, 1); + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR; - /* Initialize FAT area */ - wsect = b_fat; - for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */ - mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ - n = md; /* Media descriptor byte */ - if (fmt != FS_FAT32) { - n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; - ST_DWORD(tbl + 0, n); /* Reserve cluster #0-1 (FAT12/16) */ - } else { - n |= 0xFFFFFF00; - ST_DWORD(tbl + 0, n); /* Reserve cluster #0-1 (FAT32) */ - ST_DWORD(tbl + 4, 0xFFFFFFFF); - ST_DWORD(tbl + 8, 0x0FFFFFFF); /* Reserve cluster #2 for root directory */ - } - if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) - return FR_DISK_ERR; - mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */ - for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector writes */ - if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) - return FR_DISK_ERR; - } - } - - /* Initialize root directory */ - i = (fmt == FS_FAT32) ? au : (UINT)n_dir; - do { - if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) - return FR_DISK_ERR; - } while (--i); - -#if _USE_TRIM /* Erase data area if needed */ - { - eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1; - disk_ioctl(pdrv, CTRL_TRIM, eb); - } -#endif - - /* Create FSINFO if needed */ - if (fmt == FS_FAT32) { - ST_DWORD(tbl + FSI_LeadSig, 0x41615252); - ST_DWORD(tbl + FSI_StrucSig, 0x61417272); - ST_DWORD(tbl + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ - ST_DWORD(tbl + FSI_Nxt_Free, 2); /* Last allocated cluster# */ - ST_WORD(tbl + BS_55AA, 0xAA55); - disk_write(pdrv, tbl, b_vol + 1, 1); /* Write original (VBR + 1) */ - disk_write(pdrv, tbl, b_vol + 7, 1); /* Write backup (VBR + 7) */ - } - - return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; + return FR_OK; } @@ -4308,7 +5657,7 @@ FRESULT f_mkfs ( FRESULT f_fdisk ( BYTE pdrv, /* Physical drive number */ - const DWORD szt[], /* Pointer to the size table for each partitions */ + const DWORD* szt, /* Pointer to the size table for each partitions */ void* work /* Pointer to the working buffer */ ) { @@ -4323,7 +5672,7 @@ FRESULT f_fdisk ( if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; - /* Determine CHS in the table regardless of the drive geometry */ + /* Determine the CHS without any care of the drive geometry */ for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; if (n == 256) n--; e_hd = n - 1; @@ -4355,19 +5704,18 @@ FRESULT f_fdisk ( p[5] = e_hd; /* End head */ p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ p[7] = (BYTE)e_cyl; /* End cylinder */ - ST_DWORD(p + 8, s_part); /* Start sector in LBA */ - ST_DWORD(p + 12, sz_part); /* Partition size */ + st_dword(p + 8, s_part); /* Start sector in LBA */ + st_dword(p + 12, sz_part); /* Partition size */ /* Next partition */ b_cyl += p_cyl; } - ST_WORD(p, 0xAA55); + st_word(p, 0xAA55); /* Write it to the MBR */ return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; } - #endif /* _MULTI_PARTITION */ #endif /* _USE_MKFS && !_FS_READONLY */ @@ -4392,7 +5740,7 @@ TCHAR* f_gets ( while (n < len - 1) { /* Read characters until buffer gets filled */ -#if _USE_LFN && _LFN_UNICODE +#if _LFN_UNICODE #if _STRF_ENCODE == 3 /* Read a character in UTF-8 */ f_read(fp, s, 1, &rc); if (rc != 1) break; @@ -4459,14 +5807,14 @@ TCHAR* f_gets ( /*-----------------------------------------------------------------------*/ typedef struct { - FIL* fp; - int idx, nchr; - BYTE buf[64]; + FIL *fp; /* Ptr to the writing file */ + int idx, nchr; /* Write index of buf[] (-1:error), number of chars written */ + BYTE buf[64]; /* Write buffer */ } putbuff; static -void putc_bfd ( +void putc_bfd ( /* Buffered write with code conversion */ putbuff* pb, TCHAR c ) @@ -4475,13 +5823,14 @@ void putc_bfd ( int i; - if (_USE_STRFUNC == 2 && c == '\n') /* LF -> CRLF conversion */ + if (_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */ putc_bfd(pb, '\r'); + } - i = pb->idx; /* Buffer write index (-1:error) */ + i = pb->idx; /* Write index of pb->buf[] */ if (i < 0) return; -#if _USE_LFN && _LFN_UNICODE +#if _LFN_UNICODE #if _STRF_ENCODE == 3 /* Write a character in UTF-8 */ if (c < 0x80) { /* 7-bit */ pb->buf[i++] = (BYTE)c; @@ -4520,6 +5869,31 @@ void putc_bfd ( } +static +int putc_flush ( /* Flush left characters in the buffer */ + putbuff* pb +) +{ + UINT nw; + + if ( pb->idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK + && (UINT)pb->idx == nw) return pb->nchr; + return EOF; +} + + +static +void putc_init ( /* Initialize write buffer */ + putbuff* pb, + FIL* fp +) +{ + pb->fp = fp; + pb->nchr = pb->idx = 0; +} + + int f_putc ( TCHAR c, /* A character to be output */ @@ -4527,18 +5901,11 @@ int f_putc ( ) { putbuff pb; - UINT nw; - pb.fp = fp; /* Initialize output buffer */ - pb.nchr = pb.idx = 0; - - putc_bfd(&pb, c); /* Put a character */ - - if ( pb.idx >= 0 /* Flush buffered characters to the file */ - && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK - && (UINT)pb.idx == nw) return pb.nchr; - return EOF; + putc_init(&pb, fp); + putc_bfd(&pb, c); /* Put the character */ + return putc_flush(&pb); } @@ -4554,19 +5921,11 @@ int f_puts ( ) { putbuff pb; - UINT nw; - pb.fp = fp; /* Initialize output buffer */ - pb.nchr = pb.idx = 0; - - while (*str) /* Put the string */ - putc_bfd(&pb, *str++); - - if ( pb.idx >= 0 /* Flush buffered characters to the file */ - && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK - && (UINT)pb.idx == nw) return pb.nchr; - return EOF; + putc_init(&pb, fp); + while (*str) putc_bfd(&pb, *str++); /* Put the string */ + return putc_flush(&pb); } @@ -4583,15 +5942,14 @@ int f_printf ( ) { va_list arp; - BYTE f, r; - UINT nw, i, j, w; - DWORD v; - TCHAR c, d, s[16], *p; putbuff pb; + BYTE f, r; + UINT i, j, w; + DWORD v; + TCHAR c, d, str[32], *p; - pb.fp = fp; /* Initialize output buffer */ - pb.nchr = pb.idx = 0; + putc_init(&pb, fp); va_start(arp, fmt); @@ -4656,21 +6014,18 @@ int f_printf ( do { d = (TCHAR)(v % r); v /= r; if (d > 9) d += (c == 'x') ? 0x27 : 0x07; - s[i++] = d + '0'; - } while (v && i < sizeof s / sizeof s[0]); - if (f & 8) s[i++] = '-'; + str[i++] = d + '0'; + } while (v && i < sizeof str / sizeof str[0]); + if (f & 8) str[i++] = '-'; j = i; d = (f & 1) ? '0' : ' '; while (!(f & 2) && j++ < w) putc_bfd(&pb, d); - do putc_bfd(&pb, s[--i]); while (i); + do putc_bfd(&pb, str[--i]); while (i); while (j++ < w) putc_bfd(&pb, d); } va_end(arp); - if ( pb.idx >= 0 /* Flush buffered characters to the file */ - && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK - && (UINT)pb.idx == nw) return pb.nchr; - return EOF; + return putc_flush(&pb); } #endif /* !_FS_READONLY */ diff --git a/firmware/chibios-portapack/ext/fatfs/src/ff.h b/firmware/chibios-portapack/ext/fatfs/src/ff.h index 917908d2..5984c8eb 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ff.h +++ b/firmware/chibios-portapack/ext/fatfs/src/ff.h @@ -1,11 +1,13 @@ -/*---------------------------------------------------------------------------/ -/ FatFs - FAT file system module include R0.11a (C)ChaN, 2015 -/----------------------------------------------------------------------------/ -/ FatFs module is a free software that opened under license policy of -/ following conditions. +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT file system module R0.12a / +/-----------------------------------------------------------------------------/ / -/ Copyright (C) 2015, ChaN, all right reserved. +/ Copyright (C) 2016, ChaN, all right reserved. / +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + / 1. Redistributions of source code must retain the above copyright notice, / this condition and the following disclaimer. / @@ -13,11 +15,11 @@ / and any warranties related to this software are DISCLAIMED. / The copyright owner or contributors be NOT LIABLE for any damages caused / by use of this software. -/---------------------------------------------------------------------------*/ +/----------------------------------------------------------------------------*/ #ifndef _FATFS -#define _FATFS 64180 /* Revision ID */ +#define _FATFS 80186 /* Revision ID */ #ifdef __cplusplus extern "C" { @@ -25,6 +27,7 @@ extern "C" { #include "integer.h" /* Basic integer types */ #include "ffconf.h" /* FatFs configuration options */ + #if _FATFS != _FFCONF #error Wrong configuration file (ffconf.h). #endif @@ -52,8 +55,8 @@ extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ /* Type of path name strings on FatFs API */ -#if _LFN_UNICODE /* Unicode string */ -#if !_USE_LFN +#if _LFN_UNICODE /* Unicode (UTF-16) string */ +#if _USE_LFN == 0 #error _LFN_UNICODE must be 0 at non-LFN cfg. #endif #ifndef _INC_TCHAR @@ -61,14 +64,25 @@ typedef WCHAR TCHAR; #define _T(x) L ## x #define _TEXT(x) L ## x #endif - #else /* ANSI/OEM string */ #ifndef _INC_TCHAR typedef char TCHAR; #define _T(x) x #define _TEXT(x) x #endif +#endif + + +/* Type of file size variables */ + +#if _FS_EXFAT +#if _USE_LFN == 0 +#error LFN must be enabled when enable exFAT +#endif +typedef QWORD FSIZE_t; +#else +typedef DWORD FSIZE_t; #endif @@ -76,60 +90,87 @@ typedef char TCHAR; /* File system object structure (FATFS) */ typedef struct { - BYTE fs_type; /* FAT sub-type (0:Not mounted) */ + BYTE fs_type; /* File system type (0:N/A) */ BYTE drv; /* Physical drive number */ - BYTE csize; /* Sectors per cluster (1,2,4...128) */ - BYTE n_fats; /* Number of FAT copies (1 or 2) */ + BYTE n_fats; /* Number of FATs (1 or 2) */ BYTE wflag; /* win[] flag (b0:dirty) */ BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ WORD id; /* File system mount ID */ WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ + WORD csize; /* Cluster size [sectors] */ #if _MAX_SS != _MIN_SS - WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */ + WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ +#endif +#if _USE_LFN != 0 + WCHAR* lfnbuf; /* LFN working buffer */ +#endif +#if _FS_EXFAT + BYTE* dirbuf; /* Directory entry block scratchpad buffer */ #endif #if _FS_REENTRANT _SYNC_t sobj; /* Identifier of sync object */ #endif #if !_FS_READONLY - DWORD last_clust; /* Last allocated cluster */ - DWORD free_clust; /* Number of free clusters */ + DWORD last_clst; /* Last allocated cluster */ + DWORD free_clst; /* Number of free clusters */ #endif -#if _FS_RPATH +#if _FS_RPATH != 0 DWORD cdir; /* Current directory start cluster (0:root) */ +#if _FS_EXFAT + DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ + DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ + DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ #endif - DWORD n_fatent; /* Number of FAT entries, = number of clusters + 2 */ - DWORD fsize; /* Sectors per FAT */ - DWORD volbase; /* Volume start sector */ - DWORD fatbase; /* FAT start sector */ - DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */ - DWORD database; /* Data start sector */ +#endif + DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ + DWORD fsize; /* Size of an FAT [sectors] */ + DWORD volbase; /* Volume base sector */ + DWORD fatbase; /* FAT base sector */ + DWORD dirbase; /* Root directory base sector/cluster */ + DWORD database; /* Data base sector */ DWORD winsect; /* Current sector appearing in the win[] */ BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ } FATFS; +/* Object ID and allocation information (_FDID) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE attr; /* Object attribute */ + BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */ + DWORD sclust; /* Object start cluster (0:no cluster or root directory) */ + FSIZE_t objsize; /* Object size (valid when sclust != 0) */ +#if _FS_EXFAT + DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */ + DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ + DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ + DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0) */ +#endif +#if _FS_LOCK != 0 + UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ +#endif +} _FDID; + + + /* File object structure (FIL) */ typedef struct { - FATFS* fs; /* Pointer to the related file system object (**do not change order**) */ - WORD id; /* Owner file system mount ID (**do not change order**) */ - BYTE flag; /* Status flags */ + _FDID obj; /* Object identifier */ + BYTE flag; /* File status flags */ BYTE err; /* Abort flag (error code) */ - DWORD fptr; /* File read/write pointer (Zeroed on file open) */ - DWORD fsize; /* File size */ - DWORD sclust; /* File start cluster (0:no cluster chain, always 0 when fsize is 0) */ - DWORD clust; /* Current cluster of fpter (not valid when fprt is 0) */ - DWORD dsect; /* Sector number appearing in buf[] (0:invalid) */ + FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ + DWORD clust; /* Current cluster of fpter (invalid when fprt is 0) */ + DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ #if !_FS_READONLY DWORD dir_sect; /* Sector number containing the directory entry */ BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ #endif #if _USE_FASTSEEK - DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */ -#endif -#if _FS_LOCK - UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ + DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ #endif #if !_FS_TINY BYTE buf[_MAX_SS]; /* File private data read/write window */ @@ -141,23 +182,17 @@ typedef struct { /* Directory object structure (DIR) */ typedef struct { - FATFS* fs; /* Pointer to the owner file system object (**do not change order**) */ - WORD id; /* Owner file system mount ID (**do not change order**) */ - WORD index; /* Current read/write index number */ - DWORD sclust; /* Table start cluster (0:Root dir) */ + _FDID obj; /* Object identifier */ + DWORD dptr; /* Current read/write offset */ DWORD clust; /* Current cluster */ DWORD sect; /* Current sector */ - BYTE* dir; /* Pointer to the current SFN entry in the win[] */ - BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ -#if _FS_LOCK - UINT lockid; /* File lock ID (index of file semaphore table Files[]) */ -#endif -#if _USE_LFN - WCHAR* lfn; /* Pointer to the LFN working buffer */ - WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */ + BYTE* dir; /* Pointer to the directory item in the win[] */ + BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ +#if _USE_LFN != 0 + DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ #endif #if _USE_FIND - const TCHAR* pat; /* Pointer to the name matching pattern */ + const TCHAR* pat; /* Pointer to the name matching pattern */ #endif } DIR; @@ -166,14 +201,15 @@ typedef struct { /* File information structure (FILINFO) */ typedef struct { - DWORD fsize; /* File size */ - WORD fdate; /* Last modified date */ - WORD ftime; /* Last modified time */ - BYTE fattrib; /* Attribute */ - TCHAR fname[13]; /* Short file name (8.3 format) */ -#if _USE_LFN - TCHAR* lfname; /* Pointer to the LFN buffer */ - UINT lfsize; /* Size of LFN buffer in TCHAR */ + FSIZE_t fsize; /* File size */ + WORD fdate; /* Modified date */ + WORD ftime; /* Modified time */ + BYTE fattrib; /* File attribute */ +#if _USE_LFN != 0 + TCHAR altname[13]; /* Altenative file name */ + TCHAR fname[_MAX_LFN + 1]; /* Primary file name */ +#else + TCHAR fname[13]; /* File name */ #endif } FILINFO; @@ -196,7 +232,7 @@ typedef enum { FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ FR_NOT_ENABLED, /* (12) The volume has no work area */ FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ - FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ @@ -211,12 +247,11 @@ typedef enum { FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ FRESULT f_close (FIL* fp); /* Close an open file object */ -FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */ -FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */ -FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ -FRESULT f_lseek (FIL* fp, DWORD ofs); /* Move file pointer of a file object */ -FRESULT f_truncate (FIL* fp); /* Truncate file */ -FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */ +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ +FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ +FRESULT f_truncate (FIL* fp); /* Truncate the file */ +FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ FRESULT f_closedir (DIR* dp); /* Close an open directory */ FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ @@ -226,26 +261,28 @@ FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ -FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */ -FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change times-tamp of the file/dir */ +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ FRESULT f_chdir (const TCHAR* path); /* Change current directory */ FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ +FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ -FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */ -FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */ +FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ -#define f_eof(fp) ((int)((fp)->fptr == (fp)->fsize)) +#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) #define f_error(fp) ((fp)->err) #define f_tell(fp) ((fp)->fptr) -#define f_size(fp) ((fp)->fsize) +#define f_size(fp) ((fp)->obj.objsize) #define f_rewind(fp) f_lseek((fp), 0) #define f_rewinddir(dp) f_readdir((dp), 0) @@ -265,7 +302,7 @@ DWORD get_fattime (void); #endif /* Unicode support functions */ -#if _USE_LFN /* Unicode - OEM code conversion */ +#if _USE_LFN != 0 /* Unicode - OEM code conversion */ WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */ WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */ #if _USE_LFN == 3 /* Memory functions */ @@ -289,60 +326,39 @@ int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */ /* Flags and offset address */ -/* File access control and file status flags (FIL.flag) */ - +/* File access mode and open method flags (3rd argument of f_open) */ #define FA_READ 0x01 -#define FA_OPEN_EXISTING 0x00 - -#if !_FS_READONLY #define FA_WRITE 0x02 +#define FA_OPEN_EXISTING 0x00 #define FA_CREATE_NEW 0x04 #define FA_CREATE_ALWAYS 0x08 #define FA_OPEN_ALWAYS 0x10 -#define FA__WRITTEN 0x20 -#define FA__DIRTY 0x40 -#endif +#define FA_OPEN_APPEND 0x30 +/* Fast seek controls (2nd argument of f_lseek) */ +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) -/* FAT sub type (FATFS.fs_type) */ +/* Format options (2nd argument of f_mkfs) */ +#define FM_FAT 0x01 +#define FM_FAT32 0x02 +#define FM_EXFAT 0x04 +#define FM_ANY 0x07 +#define FM_SFD 0x08 +/* Filesystem type (FATFS.fs_type) */ #define FS_FAT12 1 #define FS_FAT16 2 #define FS_FAT32 3 +#define FS_EXFAT 4 - -/* File attribute bits for directory entry */ - +/* File attribute bits for directory entry (FILINFO.fattrib) */ #define AM_RDO 0x01 /* Read only */ #define AM_HID 0x02 /* Hidden */ #define AM_SYS 0x04 /* System */ -#define AM_VOL 0x08 /* Volume label */ -#define AM_LFN 0x0F /* LFN entry */ #define AM_DIR 0x10 /* Directory */ #define AM_ARC 0x20 /* Archive */ -#define AM_MASK 0x3F /* Mask of defined bits */ -/* Fast seek feature */ -#define CREATE_LINKMAP 0xFFFFFFFF - - - -/*--------------------------------*/ -/* Multi-byte word access macros */ - -#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ -#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) -#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) -#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) -#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) -#else /* Use byte-by-byte access to the FAT structure */ -#define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) -#define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr)) -#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8) -#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24) -#endif - #ifdef __cplusplus } #endif diff --git a/firmware/chibios-portapack/ext/fatfs/src/ffconf_template.h b/firmware/chibios-portapack/ext/fatfs/src/ffconf_template.h index bbe74e43..8db3d7cb 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ffconf_template.h +++ b/firmware/chibios-portapack/ext/fatfs/src/ffconf_template.h @@ -1,8 +1,8 @@ /*---------------------------------------------------------------------------/ -/ FatFs - FAT file system module configuration file R0.11a (C)ChaN, 2015 +/ FatFs - FAT file system module configuration file /---------------------------------------------------------------------------*/ -#define _FFCONF 64180 /* Revision ID */ +#define _FFCONF 80186 /* Revision ID */ /*---------------------------------------------------------------------------/ / Function Configurations @@ -19,8 +19,8 @@ /* This option defines minimization level to remove some basic API functions. / / 0: All basic functions are enabled. -/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(), -/ f_truncate() and f_rename() function are removed. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() +/ are removed. / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. / 3: f_lseek() function is removed in addition to 2. */ @@ -35,8 +35,8 @@ #define _USE_FIND 0 -/* This option switches filtered directory read feature and related functions, -/ f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */ +/* This option switches filtered directory read functions, f_findfirst() and +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ #define _USE_MKFS 0 @@ -44,7 +44,16 @@ #define _USE_FASTSEEK 0 -/* This option switches fast seek feature. (0:Disable or 1:Enable) */ +/* This option switches fast seek function. (0:Disable or 1:Enable) */ + + +#define _USE_EXPAND 0 +/* This option switches f_expand function. (0:Disable or 1:Enable) */ + + +#define _USE_CHMOD 0 +/* This option switches attribute manipulation functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ #define _USE_LABEL 0 @@ -53,8 +62,7 @@ #define _USE_FORWARD 0 -/* This option switches f_forward() function. (0:Disable or 1:Enable) -/ To enable it, also _FS_TINY need to be set to 1. */ +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ /*---------------------------------------------------------------------------/ @@ -92,28 +100,30 @@ #define _USE_LFN 0 #define _MAX_LFN 255 -/* The _USE_LFN option switches the LFN feature. +/* The _USE_LFN switches the support of long file name (LFN). / -/ 0: Disable LFN feature. _MAX_LFN has no effect. +/ 0: Disable support of LFN. _MAX_LFN has no effect. / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. / 2: Enable LFN with dynamic working buffer on the STACK. / 3: Enable LFN with dynamic working buffer on the HEAP. / -/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must -/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. +/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added +/ to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and +/ additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255. +/ It should be set 255 to support full featured LFN operations. / When use stack for the working buffer, take care on stack overflow. When use heap / memory for the working buffer, memory management functions, ff_memalloc() and / ff_memfree(), must be added to the project. */ #define _LFN_UNICODE 0 -/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode) -/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE -/ to 1. This option also affects behavior of string I/O functions. */ +/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) +/ To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. +/ This option also affects behavior of string I/O functions. */ #define _STRF_ENCODE 3 -/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to +/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to / be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). / / 0: ANSI/OEM @@ -121,17 +131,16 @@ / 2: UTF-16BE / 3: UTF-8 / -/ When _LFN_UNICODE is 0, this option has no effect. */ +/ This option has no effect when _LFN_UNICODE == 0. */ #define _FS_RPATH 0 -/* This option configures relative path feature. +/* This option configures support of relative path. / -/ 0: Disable relative path feature and remove related functions. -/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available. +/ 0: Disable relative path and remove related functions. +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. / 2: f_getcwd() function is available in addition to 1. -/ -/ Note that directory items read via f_readdir() are affected by this option. */ +*/ /*---------------------------------------------------------------------------/ @@ -143,8 +152,8 @@ #define _STR_VOLUME_ID 0 -#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3" -/* _STR_VOLUME_ID option switches string volume ID feature. +#define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" +/* _STR_VOLUME_ID switches string support of volume ID. / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive / number in the path name. _VOLUME_STRS defines the drive ID strings for each / logical drives. Number of items must be equal to _VOLUMES. Valid characters for @@ -152,11 +161,12 @@ #define _MULTI_PARTITION 0 -/* This option switches multi-partition feature. By default (0), each logical drive -/ number is bound to the same physical drive number and only an FAT volume found on -/ the physical drive will be mounted. When multi-partition feature is enabled (1), -/ each logical drive number is bound to arbitrary physical drive and partition -/ listed in the VolToPart[]. Also f_fdisk() funciton will be available. */ +/* This option switches support of multi-partition on a physical drive. +/ By default (0), each logical drive number is bound to the same physical drive +/ number and only an FAT volume found on the physical drive will be mounted. +/ When multi-partition is enabled (1), each logical drive number can be bound to +/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() +/ funciton will be available. */ #define _MIN_SS 512 @@ -170,8 +180,8 @@ #define _USE_TRIM 0 -/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable) -/ To enable Trim feature, also CTRL_TRIM command should be implemented to the +/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable) +/ To enable Trim function, also CTRL_TRIM command should be implemented to the / disk_ioctl() function. */ @@ -194,46 +204,51 @@ #define _FS_TINY 0 /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) -/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS -/ bytes. Instead of private sector buffer eliminated from the file object, -/ common sector buffer in the file system object (FATFS) is used for the file -/ data transfer. */ +/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the file system object (FATFS) is used for the file data transfer. */ + + +#define _FS_EXFAT 0 +/* This option switches support of exFAT file system in addition to the traditional +/ FAT file system. (0:Disable or 1:Enable) To enable exFAT, also LFN must be enabled. +/ Note that enabling exFAT discards C89 compatibility. */ #define _FS_NORTC 0 #define _NORTC_MON 1 #define _NORTC_MDAY 1 -#define _NORTC_YEAR 2015 -/* The _FS_NORTC option switches timestamp feature. If the system does not have -/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable -/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp -/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR. -/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need -/ to be added to the project to read current time form RTC. _NORTC_MON, +#define _NORTC_YEAR 2016 +/* The option _FS_NORTC switches timestamp functiton. If the system does not have +/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable +/ the timestamp function. All objects modified by FatFs will have a fixed timestamp +/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time. +/ To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be +/ added to the project to get current time form real-time clock. _NORTC_MON, / _NORTC_MDAY and _NORTC_YEAR have no effect. -/ These options have no effect at read-only configuration (_FS_READONLY == 1). */ +/ These options have no effect at read-only configuration (_FS_READONLY = 1). */ #define _FS_LOCK 0 -/* The _FS_LOCK option switches file lock feature to control duplicated file open +/* The option _FS_LOCK switches file lock function to control duplicated file open / and illegal operation to open objects. This option must be 0 when _FS_READONLY / is 1. / -/ 0: Disable file lock feature. To avoid volume corruption, application program +/ 0: Disable file lock function. To avoid volume corruption, application program / should avoid illegal open, remove and rename to the open objects. -/ >0: Enable file lock feature. The value defines how many files/sub-directories +/ >0: Enable file lock function. The value defines how many files/sub-directories / can be opened simultaneously under file lock control. Note that the file -/ lock feature is independent of re-entrancy. */ +/ lock control is independent of re-entrancy. */ #define _FS_REENTRANT 0 #define _FS_TIMEOUT 1000 #define _SYNC_t HANDLE -/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs +/* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs / module itself. Note that regardless of this option, file access to different / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() / and f_fdisk() function, are always not re-entrant. Only file/directory access -/ to the same volume is under control of this feature. +/ to the same volume is under control of this function. / / 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. / 1: Enable re-entrancy. Also user provided synchronization handlers, @@ -247,30 +262,4 @@ / included somewhere in the scope of ff.c. */ -#define _WORD_ACCESS 0 -/* The _WORD_ACCESS option is an only platform dependent option. It defines -/ which access method is used to the word data on the FAT volume. -/ -/ 0: Byte-by-byte access. Always compatible with all platforms. -/ 1: Word access. Do not choose this unless under both the following conditions. -/ -/ * Address misaligned memory access is always allowed to ALL instructions. -/ * Byte order on the memory is little-endian. -/ -/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size. -/ Following table shows allowable settings of some type of processors. -/ -/ ARM7TDMI 0 *2 ColdFire 0 *1 V850E 0 *2 -/ Cortex-M3 0 *3 Z80 0/1 V850ES 0/1 -/ Cortex-M0 0 *2 x86 0/1 TLCS-870 0/1 -/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1 -/ AVR32 0 *1 RL78 0 *2 R32C 0 *2 -/ PIC18 0/1 SH-2 0 *1 M16C 0/1 -/ PIC24 0 *2 H8S 0 *1 MSP430 0 *2 -/ PIC32 0 *1 H8/300H 0 *1 8051 0/1 -/ -/ *1:Big-endian. -/ *2:Unaligned memory access is not supported. -/ *3:Some compilers generate LDM/STM for mem_cpy function. -*/ - +/*--- End of configuration options ---*/ diff --git a/firmware/chibios-portapack/ext/fatfs/src/integer.h b/firmware/chibios-portapack/ext/fatfs/src/integer.h index 584bd1a9..4660ed62 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/integer.h +++ b/firmware/chibios-portapack/ext/fatfs/src/integer.h @@ -5,13 +5,19 @@ #ifndef _FF_INTEGER #define _FF_INTEGER -#ifdef _WIN32 /* Development platform */ +#ifdef _WIN32 /* FatFs development platform */ #include #include +typedef unsigned __int64 QWORD; + #else /* Embedded platform */ +/* These types MUST be 16-bit or 32-bit */ +typedef int INT; +typedef unsigned int UINT; + /* This type MUST be 8-bit */ typedef unsigned char BYTE; @@ -20,14 +26,13 @@ typedef short SHORT; typedef unsigned short WORD; typedef unsigned short WCHAR; -/* These types MUST be 16-bit or 32-bit */ -typedef int INT; -typedef unsigned int UINT; - /* These types MUST be 32-bit */ typedef long LONG; typedef unsigned long DWORD; +/* This type MUST be 64-bit (Remove this for C89 compatibility) */ +typedef unsigned long long QWORD; + #endif #endif diff --git a/firmware/chibios-portapack/ext/fatfs/src/option/cc932.c b/firmware/chibios-portapack/ext/fatfs/src/option/cc932.c index 19231a2b..1e244c37 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/option/cc932.c +++ b/firmware/chibios-portapack/ext/fatfs/src/option/cc932.c @@ -3782,46 +3782,87 @@ WCHAR ff_convert ( /* Converted code, 0 means conversion error */ -WCHAR ff_wtoupper ( - WCHAR chr /* Unicode character to be upper converted */ +WCHAR ff_wtoupper ( /* Returns upper converted character */ + WCHAR chr /* Unicode character to be upper converted (BMP only) */ ) { - static const WCHAR lower[] = { /* Lower case characters to be converted */ - /* Latin Supplement */ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, - /* Latin Extended-A */ 0x101,0x103,0x105,0x107,0x109,0x10B,0x10D,0x10F,0x111,0x113,0x115,0x117,0x119,0x11B,0x11D,0x11F,0x121,0x123,0x125,0x127,0x129,0x12B,0x12D,0x12F,0x131,0x133,0x135,0x137,0x13A,0x13C,0x13E,0x140,0x142,0x144,0x146,0x148,0x14B,0x14D,0x14F,0x151,0x153,0x155,0x157,0x159,0x15B,0x15D,0x15F,0x161,0x163,0x165,0x167,0x169,0x16B,0x16D,0x16F,0x171,0x173,0x175,0x177,0x17A,0x17C,0x17E, - /* Latin Extended-B */ 0x183,0x185,0x188,0x18C,0x192,0x199,0x1A1,0x1A3,0x1A8,0x1AD,0x1B0,0x1B4,0x1B6,0x1B9,0x1BD,0x1C6,0x1C9,0x1CC,0x1CE,0x1D0,0x1D2,0x1D4,0x1D6,0x1D8,0x1DA,0x1DC,0x1DD,0x1DF,0x1E1,0x1E3,0x1E5,0x1E7,0x1E9,0x1EB,0x1ED,0x1EF,0x1F3,0x1F5,0x1FB,0x1FD,0x1FF,0x201,0x203,0x205,0x207,0x209,0x20B,0x20D,0x20F,0x211,0x213,0x215,0x217, - /* Greek, Coptic */ 0x3B1,0x3B2,0x3B3,0x3B4,0x3B5,0x3B6,0x3B7,0x3B8,0x3B9,0x3BA,0x3BB,0x3BC,0x3BD,0x3BE,0x3BF,0x3C0,0x3C1,0x3C3,0x3C4,0x3C5,0x3C6,0x3C7,0x3C8,0x3C9,0x3CA,0x3CB,0x3CC,0x3CD,0x3CE,0x3E3,0x3E5,0x3E7,0x3E9,0x3EB, - /* Cyrillic */ 0x430,0x431,0x432,0x433,0x434,0x435,0x436,0x437,0x438,0x439,0x43A,0x43B,0x43C,0x43D,0x43E,0x43F,0x440,0x441,0x442,0x443,0x444,0x445,0x446,0x447,0x448,0x449,0x44A,0x44B,0x44C,0x44D,0x44E,0x44F,0x452,0x453,0x454,0x455,0x456,0x457,0x458,0x459,0x45A,0x45B,0x45C,0x45E,0x45F,0x461,0x463,0x465,0x467,0x469,0x46B,0x46D,0x46F,0x471,0x473,0x475,0x477,0x479,0x47B,0x47D,0x47F,0x481,0x491,0x493,0x495,0x497,0x499,0x49B,0x49D,0x49F,0x4A1,0x4A3,0x4A5,0x4A7,0x4A9,0x4AB,0x4AD,0x4AF,0x4B1,0x4B3,0x4B5,0x4B7,0x4B9,0x4BB,0x4BD,0x4BF,0x4C2,0x4C4,0x4C8,0x4D1,0x4D3,0x4D5,0x4D7,0x4D9,0x4DB,0x4DD,0x4DF,0x4E1,0x4E3,0x4E5,0x4E7,0x4E9,0x4EB,0x4ED,0x4EF,0x4F1,0x4F3,0x4F5,0x4F9, - /* Armenian */ 0x561,0x562,0x563,0x564,0x565,0x566,0x567,0x568,0x569,0x56A,0x56B,0x56C,0x56D,0x56E,0x56F,0x570,0x571,0x572,0x573,0x574,0x575,0x576,0x577,0x578,0x579,0x57A,0x57B,0x57C,0x57D,0x57E,0x57F,0x580,0x581,0x582,0x583,0x584,0x585,0x586, - /* Latin Extended Additional */ 0x1E01,0x1E03,0x1E05,0x1E07,0x1E09,0x1E0B,0x1E0D,0x1E0F,0x1E11,0x1E13,0x1E15,0x1E17,0x1E19,0x1E1B,0x1E1D,0x1E1F,0x1E21,0x1E23,0x1E25,0x1E27,0x1E29,0x1E2B,0x1E2D,0x1E2F,0x1E31,0x1E33,0x1E35,0x1E37,0x1E39,0x1E3B,0x1E3D,0x1E3F,0x1E41,0x1E43,0x1E45,0x1E47,0x1E49,0x1E4B,0x1E4D,0x1E4F,0x1E51,0x1E53,0x1E55,0x1E57,0x1E59,0x1E5B,0x1E5D,0x1E5F,0x1E61,0x1E63,0x1E65,0x1E67,0x1E69,0x1E6B,0x1E6D,0x1E6F,0x1E71,0x1E73,0x1E75,0x1E77,0x1E79,0x1E7B,0x1E7D,0x1E7F,0x1E81,0x1E83,0x1E85,0x1E87,0x1E89,0x1E8B,0x1E8D,0x1E8F,0x1E91,0x1E93,0x1E95,0x1E97,0x1E99,0x1E9B,0x1E9D,0x1E9F,0x1EA1,0x1EA3,0x1EA5,0x1EA7,0x1EA9,0x1EAB,0x1EAD,0x1EAF,0x1EB1,0x1EB3,0x1EB5,0x1EB7,0x1EB9,0x1EBB,0x1EBD,0x1EBF,0x1EC1,0x1EC3,0x1EC5,0x1EC7,0x1EC9,0x1ECB,0x1ECD,0x1ECF,0x1ED1,0x1ED3,0x1ED5,0x1ED7,0x1ED9,0x1EDB,0x1EDD,0x1EDF,0x1EE1,0x1EE3,0x1EE5,0x1EE7,0x1EE9,0x1EEB,0x1EED,0x1EEF,0x1EF1,0x1EF3,0x1EF5,0x1EF7,0x1EF9, - /* Number forms */ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F, - /* Full-width */ 0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A + /* Compressed upper conversion table */ + static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */ + /* Basic Latin */ + 0x0061,0x031A, + /* Latin-1 Supplement */ + 0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178, + /* Latin Extended-A */ + 0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106, + /* Latin Extended-B */ + 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA, + 0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128, + 0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A, + /* IPA Extensions */ + 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7, + /* Greek, Coptic */ + 0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311, + 0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118, + 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA, + /* Cyrillic */ + 0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144, + /* Armenian */ + 0x0561,0x0426, + + 0x0000 }; - static const WCHAR upper[] = { /* Upper case characters correspond to lower[] */ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x178, - 0x100,0x102,0x104,0x106,0x108,0x10A,0x10C,0x10E,0x110,0x112,0x114,0x116,0x118,0x11A,0x11C,0x11E,0x120,0x122,0x124,0x126,0x128,0x12A,0x12C,0x12E,0x130,0x132,0x134,0x136,0x139,0x13B,0x13D,0x13F,0x141,0x143,0x145,0x147,0x14A,0x14C,0x14E,0x150,0x152,0x154,0x156,0x158,0x15A,0x15C,0x15E,0x160,0x162,0x164,0x166,0x168,0x16A,0x16C,0x16E,0x170,0x172,0x174,0x176,0x179,0x17B,0x17D, - 0x182,0x184,0x187,0x18B,0x191,0x198,0x1A0,0x1A2,0x1A7,0x1AC,0x1AF,0x1B3,0x1B5,0x1B8,0x1BC,0x1C4,0x1C7,0x1CA,0x1CD,0x1CF,0x1D1,0x1D3,0x1D5,0x1D7,0x1D9,0x1DB,0x18E,0x1DE,0x1E0,0x1E2,0x1E4,0x1E6,0x1E8,0x1EA,0x1EC,0x1EE,0x1F1,0x1F4,0x1FA,0x1FC,0x1FE,0x200,0x202,0x204,0x206,0x208,0x20A,0x20C,0x20E,0x210,0x212,0x214,0x216, - 0x391,0x392,0x393,0x394,0x395,0x396,0x397,0x398,0x399,0x39A,0x39B,0x39C,0x39D,0x39E,0x39F,0x3A0,0x3A1,0x3A3,0x3A4,0x3A5,0x3A6,0x3A7,0x3A8,0x3A9,0x3AA,0x3AB,0x38C,0x38E,0x38F,0x3E2,0x3E4,0x3E6,0x3E8,0x3EA, - 0x410,0x411,0x412,0x413,0x414,0x415,0x416,0x417,0x418,0x419,0x41A,0x41B,0x41C,0x41D,0x41E,0x41F,0x420,0x421,0x422,0x423,0x424,0x425,0x426,0x427,0x428,0x429,0x42A,0x42B,0x42C,0x42D,0x42E,0x42F,0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40A,0x40B,0x40C,0x40E,0x40F,0x460,0x462,0x464,0x466,0x468,0x46A,0x46C,0x46E,0x470,0x472,0x474,0x476,0x478,0x47A,0x47C,0x47E,0x480,0x490,0x492,0x494,0x496,0x498,0x49A,0x49C,0x49E,0x4A0,0x4A2,0x4A4,0x4A6,0x4A8,0x4AA,0x4AC,0x4AE,0x4B0,0x4B2,0x4B4,0x4B6,0x4B8,0x4BA,0x4BC,0x4BE,0x4C1,0x4C3,0x5C7,0x4D0,0x4D2,0x4D4,0x4D6,0x4D8,0x4DA,0x4DC,0x4DE,0x4E0,0x4E2,0x4E4,0x4E6,0x4E8,0x4EA,0x4EC,0x4EE,0x4F0,0x4F2,0x4F4,0x4F8, - 0x531,0x532,0x533,0x534,0x535,0x536,0x537,0x538,0x539,0x53A,0x53B,0x53C,0x53D,0x53E,0x53F,0x540,0x541,0x542,0x543,0x544,0x545,0x546,0x547,0x548,0x549,0x54A,0x54B,0x54C,0x54D,0x54E,0x54F,0x550,0x551,0x552,0x553,0x554,0x555,0x556, - 0x1E00,0x1E02,0x1E04,0x1E06,0x1E08,0x1E0A,0x1E0C,0x1E0E,0x1E10,0x1E12,0x1E14,0x1E16,0x1E18,0x1E1A,0x1E1C,0x1E1E,0x1E20,0x1E22,0x1E24,0x1E26,0x1E28,0x1E2A,0x1E2C,0x1E2E,0x1E30,0x1E32,0x1E34,0x1E36,0x1E38,0x1E3A,0x1E3C,0x1E3E,0x1E40,0x1E42,0x1E44,0x1E46,0x1E48,0x1E4A,0x1E4C,0x1E4E,0x1E50,0x1E52,0x1E54,0x1E56,0x1E58,0x1E5A,0x1E5C,0x1E5E,0x1E60,0x1E62,0x1E64,0x1E66,0x1E68,0x1E6A,0x1E6C,0x1E6E,0x1E70,0x1E72,0x1E74,0x1E76,0x1E78,0x1E7A,0x1E7C,0x1E7E,0x1E80,0x1E82,0x1E84,0x1E86,0x1E88,0x1E8A,0x1E8C,0x1E8E,0x1E90,0x1E92,0x1E94,0x1E96,0x1E98,0x1E9A,0x1E9C,0x1E9E,0x1EA0,0x1EA2,0x1EA4,0x1EA6,0x1EA8,0x1EAA,0x1EAC,0x1EAE,0x1EB0,0x1EB2,0x1EB4,0x1EB6,0x1EB8,0x1EBA,0x1EBC,0x1EBE,0x1EC0,0x1EC2,0x1EC4,0x1EC6,0x1EC8,0x1ECA,0x1ECC,0x1ECE,0x1ED0,0x1ED2,0x1ED4,0x1ED6,0x1ED8,0x1EDA,0x1EDC,0x1EDE,0x1EE0,0x1EE2,0x1EE4,0x1EE6,0x1EE8,0x1EEA,0x1EEC,0x1EEE,0x1EF0,0x1EF2,0x1EF4,0x1EF6,0x1EF8, - 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216A,0x216B,0x216C,0x216D,0x216E,0x216F, - 0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A + static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */ + /* Phonetic Extensions */ + 0x1D7D,0x0001,0x2C63, + /* Latin Extended Additional */ + 0x1E00,0x0196, 0x1EA0,0x015A, + /* Greek Extended */ + 0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606, + 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608, + 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB, + 0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC, + 0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF2,0x0001,0x1FFC, + /* Letterlike Symbols */ + 0x214E,0x0001,0x2132, + /* Number forms */ + 0x2170,0x0210, 0x2184,0x0001,0x2183, + /* Enclosed Alphanumerics */ + 0x24D0,0x051A, 0x2C30,0x042F, + /* Latin Extended-C */ + 0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102, + /* Coptic */ + 0x2C80,0x0164, + /* Georgian Supplement */ + 0x2D00,0x0826, + /* Full-width */ + 0xFF41,0x031A, + + 0x0000 }; - UINT i, n, hi, li; + const WCHAR *p; + WCHAR bc, nc, cmd; - if (chr < 0x80) { /* ASCII characters (acceleration) */ - if (chr >= 0x61 && chr <= 0x7A) chr -= 0x20; - - } else { /* Extended characters */ - n = 12; li = 0; hi = sizeof lower / sizeof lower[0]; - do { - i = li + (hi - li) / 2; - if (chr == lower[i]) break; - if (chr > lower[i]) li = i; else hi = i; - } while (--n); - if (n) chr = upper[i]; + p = chr < 0x1000 ? cvt1 : cvt2; + for (;;) { + bc = *p++; /* Get block base */ + if (!bc || chr < bc) break; + nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ + if (chr < bc + nc) { /* In the block? */ + switch (cmd) { + case 0: chr = p[chr - bc]; break; /* Table conversion */ + case 1: chr -= (chr - bc) & 1; break; /* Case pairs */ + case 2: chr -= 16; break; /* Shift -16 */ + case 3: chr -= 32; break; /* Shift -32 */ + case 4: chr -= 48; break; /* Shift -48 */ + case 5: chr -= 26; break; /* Shift -26 */ + case 6: chr += 8; break; /* Shift +8 */ + case 7: chr -= 80; break; /* Shift -80 */ + case 8: chr -= 0x1C60; break; /* Shift -0x1C60 */ + } + break; + } + if (!cmd) p += nc; } return chr; diff --git a/firmware/chibios-portapack/ext/fatfs/src/option/cc936.c b/firmware/chibios-portapack/ext/fatfs/src/option/cc936.c index 4c20783e..3a5960f4 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/option/cc936.c +++ b/firmware/chibios-portapack/ext/fatfs/src/option/cc936.c @@ -10957,46 +10957,87 @@ WCHAR ff_convert ( /* Converted code, 0 means conversion error */ -WCHAR ff_wtoupper ( - WCHAR chr /* Unicode character to be upper converted */ +WCHAR ff_wtoupper ( /* Returns upper converted character */ + WCHAR chr /* Unicode character to be upper converted (BMP only) */ ) { - static const WCHAR lower[] = { /* Lower case characters to be converted */ - /* Latin Supplement */ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, - /* Latin Extended-A */ 0x101,0x103,0x105,0x107,0x109,0x10B,0x10D,0x10F,0x111,0x113,0x115,0x117,0x119,0x11B,0x11D,0x11F,0x121,0x123,0x125,0x127,0x129,0x12B,0x12D,0x12F,0x131,0x133,0x135,0x137,0x13A,0x13C,0x13E,0x140,0x142,0x144,0x146,0x148,0x14B,0x14D,0x14F,0x151,0x153,0x155,0x157,0x159,0x15B,0x15D,0x15F,0x161,0x163,0x165,0x167,0x169,0x16B,0x16D,0x16F,0x171,0x173,0x175,0x177,0x17A,0x17C,0x17E, - /* Latin Extended-B */ 0x183,0x185,0x188,0x18C,0x192,0x199,0x1A1,0x1A3,0x1A8,0x1AD,0x1B0,0x1B4,0x1B6,0x1B9,0x1BD,0x1C6,0x1C9,0x1CC,0x1CE,0x1D0,0x1D2,0x1D4,0x1D6,0x1D8,0x1DA,0x1DC,0x1DD,0x1DF,0x1E1,0x1E3,0x1E5,0x1E7,0x1E9,0x1EB,0x1ED,0x1EF,0x1F3,0x1F5,0x1FB,0x1FD,0x1FF,0x201,0x203,0x205,0x207,0x209,0x20B,0x20D,0x20F,0x211,0x213,0x215,0x217, - /* Greek, Coptic */ 0x3B1,0x3B2,0x3B3,0x3B4,0x3B5,0x3B6,0x3B7,0x3B8,0x3B9,0x3BA,0x3BB,0x3BC,0x3BD,0x3BE,0x3BF,0x3C0,0x3C1,0x3C3,0x3C4,0x3C5,0x3C6,0x3C7,0x3C8,0x3C9,0x3CA,0x3CB,0x3CC,0x3CD,0x3CE,0x3E3,0x3E5,0x3E7,0x3E9,0x3EB, - /* Cyrillic */ 0x430,0x431,0x432,0x433,0x434,0x435,0x436,0x437,0x438,0x439,0x43A,0x43B,0x43C,0x43D,0x43E,0x43F,0x440,0x441,0x442,0x443,0x444,0x445,0x446,0x447,0x448,0x449,0x44A,0x44B,0x44C,0x44D,0x44E,0x44F,0x452,0x453,0x454,0x455,0x456,0x457,0x458,0x459,0x45A,0x45B,0x45C,0x45E,0x45F,0x461,0x463,0x465,0x467,0x469,0x46B,0x46D,0x46F,0x471,0x473,0x475,0x477,0x479,0x47B,0x47D,0x47F,0x481,0x491,0x493,0x495,0x497,0x499,0x49B,0x49D,0x49F,0x4A1,0x4A3,0x4A5,0x4A7,0x4A9,0x4AB,0x4AD,0x4AF,0x4B1,0x4B3,0x4B5,0x4B7,0x4B9,0x4BB,0x4BD,0x4BF,0x4C2,0x4C4,0x4C8,0x4D1,0x4D3,0x4D5,0x4D7,0x4D9,0x4DB,0x4DD,0x4DF,0x4E1,0x4E3,0x4E5,0x4E7,0x4E9,0x4EB,0x4ED,0x4EF,0x4F1,0x4F3,0x4F5,0x4F9, - /* Armenian */ 0x561,0x562,0x563,0x564,0x565,0x566,0x567,0x568,0x569,0x56A,0x56B,0x56C,0x56D,0x56E,0x56F,0x570,0x571,0x572,0x573,0x574,0x575,0x576,0x577,0x578,0x579,0x57A,0x57B,0x57C,0x57D,0x57E,0x57F,0x580,0x581,0x582,0x583,0x584,0x585,0x586, - /* Latin Extended Additional */ 0x1E01,0x1E03,0x1E05,0x1E07,0x1E09,0x1E0B,0x1E0D,0x1E0F,0x1E11,0x1E13,0x1E15,0x1E17,0x1E19,0x1E1B,0x1E1D,0x1E1F,0x1E21,0x1E23,0x1E25,0x1E27,0x1E29,0x1E2B,0x1E2D,0x1E2F,0x1E31,0x1E33,0x1E35,0x1E37,0x1E39,0x1E3B,0x1E3D,0x1E3F,0x1E41,0x1E43,0x1E45,0x1E47,0x1E49,0x1E4B,0x1E4D,0x1E4F,0x1E51,0x1E53,0x1E55,0x1E57,0x1E59,0x1E5B,0x1E5D,0x1E5F,0x1E61,0x1E63,0x1E65,0x1E67,0x1E69,0x1E6B,0x1E6D,0x1E6F,0x1E71,0x1E73,0x1E75,0x1E77,0x1E79,0x1E7B,0x1E7D,0x1E7F,0x1E81,0x1E83,0x1E85,0x1E87,0x1E89,0x1E8B,0x1E8D,0x1E8F,0x1E91,0x1E93,0x1E95,0x1E97,0x1E99,0x1E9B,0x1E9D,0x1E9F,0x1EA1,0x1EA3,0x1EA5,0x1EA7,0x1EA9,0x1EAB,0x1EAD,0x1EAF,0x1EB1,0x1EB3,0x1EB5,0x1EB7,0x1EB9,0x1EBB,0x1EBD,0x1EBF,0x1EC1,0x1EC3,0x1EC5,0x1EC7,0x1EC9,0x1ECB,0x1ECD,0x1ECF,0x1ED1,0x1ED3,0x1ED5,0x1ED7,0x1ED9,0x1EDB,0x1EDD,0x1EDF,0x1EE1,0x1EE3,0x1EE5,0x1EE7,0x1EE9,0x1EEB,0x1EED,0x1EEF,0x1EF1,0x1EF3,0x1EF5,0x1EF7,0x1EF9, - /* Number forms */ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F, - /* Full-width */ 0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A + /* Compressed upper conversion table */ + static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */ + /* Basic Latin */ + 0x0061,0x031A, + /* Latin-1 Supplement */ + 0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178, + /* Latin Extended-A */ + 0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106, + /* Latin Extended-B */ + 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA, + 0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128, + 0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A, + /* IPA Extensions */ + 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7, + /* Greek, Coptic */ + 0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311, + 0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118, + 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA, + /* Cyrillic */ + 0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144, + /* Armenian */ + 0x0561,0x0426, + + 0x0000 }; - static const WCHAR upper[] = { /* Upper case characters correspond to lower[] */ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x178, - 0x100,0x102,0x104,0x106,0x108,0x10A,0x10C,0x10E,0x110,0x112,0x114,0x116,0x118,0x11A,0x11C,0x11E,0x120,0x122,0x124,0x126,0x128,0x12A,0x12C,0x12E,0x130,0x132,0x134,0x136,0x139,0x13B,0x13D,0x13F,0x141,0x143,0x145,0x147,0x14A,0x14C,0x14E,0x150,0x152,0x154,0x156,0x158,0x15A,0x15C,0x15E,0x160,0x162,0x164,0x166,0x168,0x16A,0x16C,0x16E,0x170,0x172,0x174,0x176,0x179,0x17B,0x17D, - 0x182,0x184,0x187,0x18B,0x191,0x198,0x1A0,0x1A2,0x1A7,0x1AC,0x1AF,0x1B3,0x1B5,0x1B8,0x1BC,0x1C4,0x1C7,0x1CA,0x1CD,0x1CF,0x1D1,0x1D3,0x1D5,0x1D7,0x1D9,0x1DB,0x18E,0x1DE,0x1E0,0x1E2,0x1E4,0x1E6,0x1E8,0x1EA,0x1EC,0x1EE,0x1F1,0x1F4,0x1FA,0x1FC,0x1FE,0x200,0x202,0x204,0x206,0x208,0x20A,0x20C,0x20E,0x210,0x212,0x214,0x216, - 0x391,0x392,0x393,0x394,0x395,0x396,0x397,0x398,0x399,0x39A,0x39B,0x39C,0x39D,0x39E,0x39F,0x3A0,0x3A1,0x3A3,0x3A4,0x3A5,0x3A6,0x3A7,0x3A8,0x3A9,0x3AA,0x3AB,0x38C,0x38E,0x38F,0x3E2,0x3E4,0x3E6,0x3E8,0x3EA, - 0x410,0x411,0x412,0x413,0x414,0x415,0x416,0x417,0x418,0x419,0x41A,0x41B,0x41C,0x41D,0x41E,0x41F,0x420,0x421,0x422,0x423,0x424,0x425,0x426,0x427,0x428,0x429,0x42A,0x42B,0x42C,0x42D,0x42E,0x42F,0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40A,0x40B,0x40C,0x40E,0x40F,0x460,0x462,0x464,0x466,0x468,0x46A,0x46C,0x46E,0x470,0x472,0x474,0x476,0x478,0x47A,0x47C,0x47E,0x480,0x490,0x492,0x494,0x496,0x498,0x49A,0x49C,0x49E,0x4A0,0x4A2,0x4A4,0x4A6,0x4A8,0x4AA,0x4AC,0x4AE,0x4B0,0x4B2,0x4B4,0x4B6,0x4B8,0x4BA,0x4BC,0x4BE,0x4C1,0x4C3,0x5C7,0x4D0,0x4D2,0x4D4,0x4D6,0x4D8,0x4DA,0x4DC,0x4DE,0x4E0,0x4E2,0x4E4,0x4E6,0x4E8,0x4EA,0x4EC,0x4EE,0x4F0,0x4F2,0x4F4,0x4F8, - 0x531,0x532,0x533,0x534,0x535,0x536,0x537,0x538,0x539,0x53A,0x53B,0x53C,0x53D,0x53E,0x53F,0x540,0x541,0x542,0x543,0x544,0x545,0x546,0x547,0x548,0x549,0x54A,0x54B,0x54C,0x54D,0x54E,0x54F,0x550,0x551,0x552,0x553,0x554,0x555,0x556, - 0x1E00,0x1E02,0x1E04,0x1E06,0x1E08,0x1E0A,0x1E0C,0x1E0E,0x1E10,0x1E12,0x1E14,0x1E16,0x1E18,0x1E1A,0x1E1C,0x1E1E,0x1E20,0x1E22,0x1E24,0x1E26,0x1E28,0x1E2A,0x1E2C,0x1E2E,0x1E30,0x1E32,0x1E34,0x1E36,0x1E38,0x1E3A,0x1E3C,0x1E3E,0x1E40,0x1E42,0x1E44,0x1E46,0x1E48,0x1E4A,0x1E4C,0x1E4E,0x1E50,0x1E52,0x1E54,0x1E56,0x1E58,0x1E5A,0x1E5C,0x1E5E,0x1E60,0x1E62,0x1E64,0x1E66,0x1E68,0x1E6A,0x1E6C,0x1E6E,0x1E70,0x1E72,0x1E74,0x1E76,0x1E78,0x1E7A,0x1E7C,0x1E7E,0x1E80,0x1E82,0x1E84,0x1E86,0x1E88,0x1E8A,0x1E8C,0x1E8E,0x1E90,0x1E92,0x1E94,0x1E96,0x1E98,0x1E9A,0x1E9C,0x1E9E,0x1EA0,0x1EA2,0x1EA4,0x1EA6,0x1EA8,0x1EAA,0x1EAC,0x1EAE,0x1EB0,0x1EB2,0x1EB4,0x1EB6,0x1EB8,0x1EBA,0x1EBC,0x1EBE,0x1EC0,0x1EC2,0x1EC4,0x1EC6,0x1EC8,0x1ECA,0x1ECC,0x1ECE,0x1ED0,0x1ED2,0x1ED4,0x1ED6,0x1ED8,0x1EDA,0x1EDC,0x1EDE,0x1EE0,0x1EE2,0x1EE4,0x1EE6,0x1EE8,0x1EEA,0x1EEC,0x1EEE,0x1EF0,0x1EF2,0x1EF4,0x1EF6,0x1EF8, - 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216A,0x216B,0x216C,0x216D,0x216E,0x216F, - 0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A + static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */ + /* Phonetic Extensions */ + 0x1D7D,0x0001,0x2C63, + /* Latin Extended Additional */ + 0x1E00,0x0196, 0x1EA0,0x015A, + /* Greek Extended */ + 0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606, + 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608, + 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB, + 0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC, + 0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF2,0x0001,0x1FFC, + /* Letterlike Symbols */ + 0x214E,0x0001,0x2132, + /* Number forms */ + 0x2170,0x0210, 0x2184,0x0001,0x2183, + /* Enclosed Alphanumerics */ + 0x24D0,0x051A, 0x2C30,0x042F, + /* Latin Extended-C */ + 0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102, + /* Coptic */ + 0x2C80,0x0164, + /* Georgian Supplement */ + 0x2D00,0x0826, + /* Full-width */ + 0xFF41,0x031A, + + 0x0000 }; - UINT i, n, hi, li; + const WCHAR *p; + WCHAR bc, nc, cmd; - if (chr < 0x80) { /* ASCII characters (acceleration) */ - if (chr >= 0x61 && chr <= 0x7A) chr -= 0x20; - - } else { /* Extended characters */ - n = 12; li = 0; hi = sizeof lower / sizeof lower[0]; - do { - i = li + (hi - li) / 2; - if (chr == lower[i]) break; - if (chr > lower[i]) li = i; else hi = i; - } while (--n); - if (n) chr = upper[i]; + p = chr < 0x1000 ? cvt1 : cvt2; + for (;;) { + bc = *p++; /* Get block base */ + if (!bc || chr < bc) break; + nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ + if (chr < bc + nc) { /* In the block? */ + switch (cmd) { + case 0: chr = p[chr - bc]; break; /* Table conversion */ + case 1: chr -= (chr - bc) & 1; break; /* Case pairs */ + case 2: chr -= 16; break; /* Shift -16 */ + case 3: chr -= 32; break; /* Shift -32 */ + case 4: chr -= 48; break; /* Shift -48 */ + case 5: chr -= 26; break; /* Shift -26 */ + case 6: chr += 8; break; /* Shift +8 */ + case 7: chr -= 80; break; /* Shift -80 */ + case 8: chr -= 0x1C60; break; /* Shift -0x1C60 */ + } + break; + } + if (!cmd) p += nc; } return chr; diff --git a/firmware/chibios-portapack/ext/fatfs/src/option/cc949.c b/firmware/chibios-portapack/ext/fatfs/src/option/cc949.c index f646e004..3bc617cc 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/option/cc949.c +++ b/firmware/chibios-portapack/ext/fatfs/src/option/cc949.c @@ -8586,47 +8586,87 @@ WCHAR ff_convert ( /* Converted code, 0 means conversion error */ - -WCHAR ff_wtoupper ( - WCHAR chr /* Unicode character to be upper converted */ +WCHAR ff_wtoupper ( /* Returns upper converted character */ + WCHAR chr /* Unicode character to be upper converted (BMP only) */ ) { - static const WCHAR lower[] = { /* Lower case characters to be converted */ - /* Latin Supplement */ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, - /* Latin Extended-A */ 0x101,0x103,0x105,0x107,0x109,0x10B,0x10D,0x10F,0x111,0x113,0x115,0x117,0x119,0x11B,0x11D,0x11F,0x121,0x123,0x125,0x127,0x129,0x12B,0x12D,0x12F,0x131,0x133,0x135,0x137,0x13A,0x13C,0x13E,0x140,0x142,0x144,0x146,0x148,0x14B,0x14D,0x14F,0x151,0x153,0x155,0x157,0x159,0x15B,0x15D,0x15F,0x161,0x163,0x165,0x167,0x169,0x16B,0x16D,0x16F,0x171,0x173,0x175,0x177,0x17A,0x17C,0x17E, - /* Latin Extended-B */ 0x183,0x185,0x188,0x18C,0x192,0x199,0x1A1,0x1A3,0x1A8,0x1AD,0x1B0,0x1B4,0x1B6,0x1B9,0x1BD,0x1C6,0x1C9,0x1CC,0x1CE,0x1D0,0x1D2,0x1D4,0x1D6,0x1D8,0x1DA,0x1DC,0x1DD,0x1DF,0x1E1,0x1E3,0x1E5,0x1E7,0x1E9,0x1EB,0x1ED,0x1EF,0x1F3,0x1F5,0x1FB,0x1FD,0x1FF,0x201,0x203,0x205,0x207,0x209,0x20B,0x20D,0x20F,0x211,0x213,0x215,0x217, - /* Greek, Coptic */ 0x3B1,0x3B2,0x3B3,0x3B4,0x3B5,0x3B6,0x3B7,0x3B8,0x3B9,0x3BA,0x3BB,0x3BC,0x3BD,0x3BE,0x3BF,0x3C0,0x3C1,0x3C3,0x3C4,0x3C5,0x3C6,0x3C7,0x3C8,0x3C9,0x3CA,0x3CB,0x3CC,0x3CD,0x3CE,0x3E3,0x3E5,0x3E7,0x3E9,0x3EB, - /* Cyrillic */ 0x430,0x431,0x432,0x433,0x434,0x435,0x436,0x437,0x438,0x439,0x43A,0x43B,0x43C,0x43D,0x43E,0x43F,0x440,0x441,0x442,0x443,0x444,0x445,0x446,0x447,0x448,0x449,0x44A,0x44B,0x44C,0x44D,0x44E,0x44F,0x452,0x453,0x454,0x455,0x456,0x457,0x458,0x459,0x45A,0x45B,0x45C,0x45E,0x45F,0x461,0x463,0x465,0x467,0x469,0x46B,0x46D,0x46F,0x471,0x473,0x475,0x477,0x479,0x47B,0x47D,0x47F,0x481,0x491,0x493,0x495,0x497,0x499,0x49B,0x49D,0x49F,0x4A1,0x4A3,0x4A5,0x4A7,0x4A9,0x4AB,0x4AD,0x4AF,0x4B1,0x4B3,0x4B5,0x4B7,0x4B9,0x4BB,0x4BD,0x4BF,0x4C2,0x4C4,0x4C8,0x4D1,0x4D3,0x4D5,0x4D7,0x4D9,0x4DB,0x4DD,0x4DF,0x4E1,0x4E3,0x4E5,0x4E7,0x4E9,0x4EB,0x4ED,0x4EF,0x4F1,0x4F3,0x4F5,0x4F9, - /* Armenian */ 0x561,0x562,0x563,0x564,0x565,0x566,0x567,0x568,0x569,0x56A,0x56B,0x56C,0x56D,0x56E,0x56F,0x570,0x571,0x572,0x573,0x574,0x575,0x576,0x577,0x578,0x579,0x57A,0x57B,0x57C,0x57D,0x57E,0x57F,0x580,0x581,0x582,0x583,0x584,0x585,0x586, - /* Latin Extended Additional */ 0x1E01,0x1E03,0x1E05,0x1E07,0x1E09,0x1E0B,0x1E0D,0x1E0F,0x1E11,0x1E13,0x1E15,0x1E17,0x1E19,0x1E1B,0x1E1D,0x1E1F,0x1E21,0x1E23,0x1E25,0x1E27,0x1E29,0x1E2B,0x1E2D,0x1E2F,0x1E31,0x1E33,0x1E35,0x1E37,0x1E39,0x1E3B,0x1E3D,0x1E3F,0x1E41,0x1E43,0x1E45,0x1E47,0x1E49,0x1E4B,0x1E4D,0x1E4F,0x1E51,0x1E53,0x1E55,0x1E57,0x1E59,0x1E5B,0x1E5D,0x1E5F,0x1E61,0x1E63,0x1E65,0x1E67,0x1E69,0x1E6B,0x1E6D,0x1E6F,0x1E71,0x1E73,0x1E75,0x1E77,0x1E79,0x1E7B,0x1E7D,0x1E7F,0x1E81,0x1E83,0x1E85,0x1E87,0x1E89,0x1E8B,0x1E8D,0x1E8F,0x1E91,0x1E93,0x1E95,0x1E97,0x1E99,0x1E9B,0x1E9D,0x1E9F,0x1EA1,0x1EA3,0x1EA5,0x1EA7,0x1EA9,0x1EAB,0x1EAD,0x1EAF,0x1EB1,0x1EB3,0x1EB5,0x1EB7,0x1EB9,0x1EBB,0x1EBD,0x1EBF,0x1EC1,0x1EC3,0x1EC5,0x1EC7,0x1EC9,0x1ECB,0x1ECD,0x1ECF,0x1ED1,0x1ED3,0x1ED5,0x1ED7,0x1ED9,0x1EDB,0x1EDD,0x1EDF,0x1EE1,0x1EE3,0x1EE5,0x1EE7,0x1EE9,0x1EEB,0x1EED,0x1EEF,0x1EF1,0x1EF3,0x1EF5,0x1EF7,0x1EF9, - /* Number forms */ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F, - /* Full-width */ 0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A + /* Compressed upper conversion table */ + static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */ + /* Basic Latin */ + 0x0061,0x031A, + /* Latin-1 Supplement */ + 0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178, + /* Latin Extended-A */ + 0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106, + /* Latin Extended-B */ + 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA, + 0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128, + 0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A, + /* IPA Extensions */ + 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7, + /* Greek, Coptic */ + 0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311, + 0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118, + 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA, + /* Cyrillic */ + 0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144, + /* Armenian */ + 0x0561,0x0426, + + 0x0000 }; - static const WCHAR upper[] = { /* Upper case characters correspond to lower[] */ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x178, - 0x100,0x102,0x104,0x106,0x108,0x10A,0x10C,0x10E,0x110,0x112,0x114,0x116,0x118,0x11A,0x11C,0x11E,0x120,0x122,0x124,0x126,0x128,0x12A,0x12C,0x12E,0x130,0x132,0x134,0x136,0x139,0x13B,0x13D,0x13F,0x141,0x143,0x145,0x147,0x14A,0x14C,0x14E,0x150,0x152,0x154,0x156,0x158,0x15A,0x15C,0x15E,0x160,0x162,0x164,0x166,0x168,0x16A,0x16C,0x16E,0x170,0x172,0x174,0x176,0x179,0x17B,0x17D, - 0x182,0x184,0x187,0x18B,0x191,0x198,0x1A0,0x1A2,0x1A7,0x1AC,0x1AF,0x1B3,0x1B5,0x1B8,0x1BC,0x1C4,0x1C7,0x1CA,0x1CD,0x1CF,0x1D1,0x1D3,0x1D5,0x1D7,0x1D9,0x1DB,0x18E,0x1DE,0x1E0,0x1E2,0x1E4,0x1E6,0x1E8,0x1EA,0x1EC,0x1EE,0x1F1,0x1F4,0x1FA,0x1FC,0x1FE,0x200,0x202,0x204,0x206,0x208,0x20A,0x20C,0x20E,0x210,0x212,0x214,0x216, - 0x391,0x392,0x393,0x394,0x395,0x396,0x397,0x398,0x399,0x39A,0x39B,0x39C,0x39D,0x39E,0x39F,0x3A0,0x3A1,0x3A3,0x3A4,0x3A5,0x3A6,0x3A7,0x3A8,0x3A9,0x3AA,0x3AB,0x38C,0x38E,0x38F,0x3E2,0x3E4,0x3E6,0x3E8,0x3EA, - 0x410,0x411,0x412,0x413,0x414,0x415,0x416,0x417,0x418,0x419,0x41A,0x41B,0x41C,0x41D,0x41E,0x41F,0x420,0x421,0x422,0x423,0x424,0x425,0x426,0x427,0x428,0x429,0x42A,0x42B,0x42C,0x42D,0x42E,0x42F,0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40A,0x40B,0x40C,0x40E,0x40F,0x460,0x462,0x464,0x466,0x468,0x46A,0x46C,0x46E,0x470,0x472,0x474,0x476,0x478,0x47A,0x47C,0x47E,0x480,0x490,0x492,0x494,0x496,0x498,0x49A,0x49C,0x49E,0x4A0,0x4A2,0x4A4,0x4A6,0x4A8,0x4AA,0x4AC,0x4AE,0x4B0,0x4B2,0x4B4,0x4B6,0x4B8,0x4BA,0x4BC,0x4BE,0x4C1,0x4C3,0x5C7,0x4D0,0x4D2,0x4D4,0x4D6,0x4D8,0x4DA,0x4DC,0x4DE,0x4E0,0x4E2,0x4E4,0x4E6,0x4E8,0x4EA,0x4EC,0x4EE,0x4F0,0x4F2,0x4F4,0x4F8, - 0x531,0x532,0x533,0x534,0x535,0x536,0x537,0x538,0x539,0x53A,0x53B,0x53C,0x53D,0x53E,0x53F,0x540,0x541,0x542,0x543,0x544,0x545,0x546,0x547,0x548,0x549,0x54A,0x54B,0x54C,0x54D,0x54E,0x54F,0x550,0x551,0x552,0x553,0x554,0x555,0x556, - 0x1E00,0x1E02,0x1E04,0x1E06,0x1E08,0x1E0A,0x1E0C,0x1E0E,0x1E10,0x1E12,0x1E14,0x1E16,0x1E18,0x1E1A,0x1E1C,0x1E1E,0x1E20,0x1E22,0x1E24,0x1E26,0x1E28,0x1E2A,0x1E2C,0x1E2E,0x1E30,0x1E32,0x1E34,0x1E36,0x1E38,0x1E3A,0x1E3C,0x1E3E,0x1E40,0x1E42,0x1E44,0x1E46,0x1E48,0x1E4A,0x1E4C,0x1E4E,0x1E50,0x1E52,0x1E54,0x1E56,0x1E58,0x1E5A,0x1E5C,0x1E5E,0x1E60,0x1E62,0x1E64,0x1E66,0x1E68,0x1E6A,0x1E6C,0x1E6E,0x1E70,0x1E72,0x1E74,0x1E76,0x1E78,0x1E7A,0x1E7C,0x1E7E,0x1E80,0x1E82,0x1E84,0x1E86,0x1E88,0x1E8A,0x1E8C,0x1E8E,0x1E90,0x1E92,0x1E94,0x1E96,0x1E98,0x1E9A,0x1E9C,0x1E9E,0x1EA0,0x1EA2,0x1EA4,0x1EA6,0x1EA8,0x1EAA,0x1EAC,0x1EAE,0x1EB0,0x1EB2,0x1EB4,0x1EB6,0x1EB8,0x1EBA,0x1EBC,0x1EBE,0x1EC0,0x1EC2,0x1EC4,0x1EC6,0x1EC8,0x1ECA,0x1ECC,0x1ECE,0x1ED0,0x1ED2,0x1ED4,0x1ED6,0x1ED8,0x1EDA,0x1EDC,0x1EDE,0x1EE0,0x1EE2,0x1EE4,0x1EE6,0x1EE8,0x1EEA,0x1EEC,0x1EEE,0x1EF0,0x1EF2,0x1EF4,0x1EF6,0x1EF8, - 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216A,0x216B,0x216C,0x216D,0x216E,0x216F, - 0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A + static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */ + /* Phonetic Extensions */ + 0x1D7D,0x0001,0x2C63, + /* Latin Extended Additional */ + 0x1E00,0x0196, 0x1EA0,0x015A, + /* Greek Extended */ + 0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606, + 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608, + 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB, + 0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC, + 0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF2,0x0001,0x1FFC, + /* Letterlike Symbols */ + 0x214E,0x0001,0x2132, + /* Number forms */ + 0x2170,0x0210, 0x2184,0x0001,0x2183, + /* Enclosed Alphanumerics */ + 0x24D0,0x051A, 0x2C30,0x042F, + /* Latin Extended-C */ + 0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102, + /* Coptic */ + 0x2C80,0x0164, + /* Georgian Supplement */ + 0x2D00,0x0826, + /* Full-width */ + 0xFF41,0x031A, + + 0x0000 }; - UINT i, n, hi, li; + const WCHAR *p; + WCHAR bc, nc, cmd; - if (chr < 0x80) { /* ASCII characters (acceleration) */ - if (chr >= 0x61 && chr <= 0x7A) chr -= 0x20; - - } else { /* Extended characters */ - n = 12; li = 0; hi = sizeof lower / sizeof lower[0]; - do { - i = li + (hi - li) / 2; - if (chr == lower[i]) break; - if (chr > lower[i]) li = i; else hi = i; - } while (--n); - if (n) chr = upper[i]; + p = chr < 0x1000 ? cvt1 : cvt2; + for (;;) { + bc = *p++; /* Get block base */ + if (!bc || chr < bc) break; + nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ + if (chr < bc + nc) { /* In the block? */ + switch (cmd) { + case 0: chr = p[chr - bc]; break; /* Table conversion */ + case 1: chr -= (chr - bc) & 1; break; /* Case pairs */ + case 2: chr -= 16; break; /* Shift -16 */ + case 3: chr -= 32; break; /* Shift -32 */ + case 4: chr -= 48; break; /* Shift -48 */ + case 5: chr -= 26; break; /* Shift -26 */ + case 6: chr += 8; break; /* Shift +8 */ + case 7: chr -= 80; break; /* Shift -80 */ + case 8: chr -= 0x1C60; break; /* Shift -0x1C60 */ + } + break; + } + if (!cmd) p += nc; } return chr; diff --git a/firmware/chibios-portapack/ext/fatfs/src/option/cc950.c b/firmware/chibios-portapack/ext/fatfs/src/option/cc950.c index 064f5ca5..bbcaa244 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/option/cc950.c +++ b/firmware/chibios-portapack/ext/fatfs/src/option/cc950.c @@ -6812,47 +6812,87 @@ WCHAR ff_convert ( /* Converted code, 0 means conversion error */ - -WCHAR ff_wtoupper ( - WCHAR chr /* Unicode character to be upper converted */ +WCHAR ff_wtoupper ( /* Returns upper converted character */ + WCHAR chr /* Unicode character to be upper converted (BMP only) */ ) { - static const WCHAR lower[] = { /* Lower case characters to be converted */ - /* Latin Supplement */ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, - /* Latin Extended-A */ 0x101,0x103,0x105,0x107,0x109,0x10B,0x10D,0x10F,0x111,0x113,0x115,0x117,0x119,0x11B,0x11D,0x11F,0x121,0x123,0x125,0x127,0x129,0x12B,0x12D,0x12F,0x131,0x133,0x135,0x137,0x13A,0x13C,0x13E,0x140,0x142,0x144,0x146,0x148,0x14B,0x14D,0x14F,0x151,0x153,0x155,0x157,0x159,0x15B,0x15D,0x15F,0x161,0x163,0x165,0x167,0x169,0x16B,0x16D,0x16F,0x171,0x173,0x175,0x177,0x17A,0x17C,0x17E, - /* Latin Extended-B */ 0x183,0x185,0x188,0x18C,0x192,0x199,0x1A1,0x1A3,0x1A8,0x1AD,0x1B0,0x1B4,0x1B6,0x1B9,0x1BD,0x1C6,0x1C9,0x1CC,0x1CE,0x1D0,0x1D2,0x1D4,0x1D6,0x1D8,0x1DA,0x1DC,0x1DD,0x1DF,0x1E1,0x1E3,0x1E5,0x1E7,0x1E9,0x1EB,0x1ED,0x1EF,0x1F3,0x1F5,0x1FB,0x1FD,0x1FF,0x201,0x203,0x205,0x207,0x209,0x20B,0x20D,0x20F,0x211,0x213,0x215,0x217, - /* Greek, Coptic */ 0x3B1,0x3B2,0x3B3,0x3B4,0x3B5,0x3B6,0x3B7,0x3B8,0x3B9,0x3BA,0x3BB,0x3BC,0x3BD,0x3BE,0x3BF,0x3C0,0x3C1,0x3C3,0x3C4,0x3C5,0x3C6,0x3C7,0x3C8,0x3C9,0x3CA,0x3CB,0x3CC,0x3CD,0x3CE,0x3E3,0x3E5,0x3E7,0x3E9,0x3EB, - /* Cyrillic */ 0x430,0x431,0x432,0x433,0x434,0x435,0x436,0x437,0x438,0x439,0x43A,0x43B,0x43C,0x43D,0x43E,0x43F,0x440,0x441,0x442,0x443,0x444,0x445,0x446,0x447,0x448,0x449,0x44A,0x44B,0x44C,0x44D,0x44E,0x44F,0x452,0x453,0x454,0x455,0x456,0x457,0x458,0x459,0x45A,0x45B,0x45C,0x45E,0x45F,0x461,0x463,0x465,0x467,0x469,0x46B,0x46D,0x46F,0x471,0x473,0x475,0x477,0x479,0x47B,0x47D,0x47F,0x481,0x491,0x493,0x495,0x497,0x499,0x49B,0x49D,0x49F,0x4A1,0x4A3,0x4A5,0x4A7,0x4A9,0x4AB,0x4AD,0x4AF,0x4B1,0x4B3,0x4B5,0x4B7,0x4B9,0x4BB,0x4BD,0x4BF,0x4C2,0x4C4,0x4C8,0x4D1,0x4D3,0x4D5,0x4D7,0x4D9,0x4DB,0x4DD,0x4DF,0x4E1,0x4E3,0x4E5,0x4E7,0x4E9,0x4EB,0x4ED,0x4EF,0x4F1,0x4F3,0x4F5,0x4F9, - /* Armenian */ 0x561,0x562,0x563,0x564,0x565,0x566,0x567,0x568,0x569,0x56A,0x56B,0x56C,0x56D,0x56E,0x56F,0x570,0x571,0x572,0x573,0x574,0x575,0x576,0x577,0x578,0x579,0x57A,0x57B,0x57C,0x57D,0x57E,0x57F,0x580,0x581,0x582,0x583,0x584,0x585,0x586, - /* Latin Extended Additional */ 0x1E01,0x1E03,0x1E05,0x1E07,0x1E09,0x1E0B,0x1E0D,0x1E0F,0x1E11,0x1E13,0x1E15,0x1E17,0x1E19,0x1E1B,0x1E1D,0x1E1F,0x1E21,0x1E23,0x1E25,0x1E27,0x1E29,0x1E2B,0x1E2D,0x1E2F,0x1E31,0x1E33,0x1E35,0x1E37,0x1E39,0x1E3B,0x1E3D,0x1E3F,0x1E41,0x1E43,0x1E45,0x1E47,0x1E49,0x1E4B,0x1E4D,0x1E4F,0x1E51,0x1E53,0x1E55,0x1E57,0x1E59,0x1E5B,0x1E5D,0x1E5F,0x1E61,0x1E63,0x1E65,0x1E67,0x1E69,0x1E6B,0x1E6D,0x1E6F,0x1E71,0x1E73,0x1E75,0x1E77,0x1E79,0x1E7B,0x1E7D,0x1E7F,0x1E81,0x1E83,0x1E85,0x1E87,0x1E89,0x1E8B,0x1E8D,0x1E8F,0x1E91,0x1E93,0x1E95,0x1E97,0x1E99,0x1E9B,0x1E9D,0x1E9F,0x1EA1,0x1EA3,0x1EA5,0x1EA7,0x1EA9,0x1EAB,0x1EAD,0x1EAF,0x1EB1,0x1EB3,0x1EB5,0x1EB7,0x1EB9,0x1EBB,0x1EBD,0x1EBF,0x1EC1,0x1EC3,0x1EC5,0x1EC7,0x1EC9,0x1ECB,0x1ECD,0x1ECF,0x1ED1,0x1ED3,0x1ED5,0x1ED7,0x1ED9,0x1EDB,0x1EDD,0x1EDF,0x1EE1,0x1EE3,0x1EE5,0x1EE7,0x1EE9,0x1EEB,0x1EED,0x1EEF,0x1EF1,0x1EF3,0x1EF5,0x1EF7,0x1EF9, - /* Number forms */ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F, - /* Full-width */ 0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A + /* Compressed upper conversion table */ + static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */ + /* Basic Latin */ + 0x0061,0x031A, + /* Latin-1 Supplement */ + 0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178, + /* Latin Extended-A */ + 0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106, + /* Latin Extended-B */ + 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA, + 0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128, + 0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A, + /* IPA Extensions */ + 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7, + /* Greek, Coptic */ + 0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311, + 0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118, + 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA, + /* Cyrillic */ + 0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144, + /* Armenian */ + 0x0561,0x0426, + + 0x0000 }; - static const WCHAR upper[] = { /* Upper case characters correspond to lower[] */ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x178, - 0x100,0x102,0x104,0x106,0x108,0x10A,0x10C,0x10E,0x110,0x112,0x114,0x116,0x118,0x11A,0x11C,0x11E,0x120,0x122,0x124,0x126,0x128,0x12A,0x12C,0x12E,0x130,0x132,0x134,0x136,0x139,0x13B,0x13D,0x13F,0x141,0x143,0x145,0x147,0x14A,0x14C,0x14E,0x150,0x152,0x154,0x156,0x158,0x15A,0x15C,0x15E,0x160,0x162,0x164,0x166,0x168,0x16A,0x16C,0x16E,0x170,0x172,0x174,0x176,0x179,0x17B,0x17D, - 0x182,0x184,0x187,0x18B,0x191,0x198,0x1A0,0x1A2,0x1A7,0x1AC,0x1AF,0x1B3,0x1B5,0x1B8,0x1BC,0x1C4,0x1C7,0x1CA,0x1CD,0x1CF,0x1D1,0x1D3,0x1D5,0x1D7,0x1D9,0x1DB,0x18E,0x1DE,0x1E0,0x1E2,0x1E4,0x1E6,0x1E8,0x1EA,0x1EC,0x1EE,0x1F1,0x1F4,0x1FA,0x1FC,0x1FE,0x200,0x202,0x204,0x206,0x208,0x20A,0x20C,0x20E,0x210,0x212,0x214,0x216, - 0x391,0x392,0x393,0x394,0x395,0x396,0x397,0x398,0x399,0x39A,0x39B,0x39C,0x39D,0x39E,0x39F,0x3A0,0x3A1,0x3A3,0x3A4,0x3A5,0x3A6,0x3A7,0x3A8,0x3A9,0x3AA,0x3AB,0x38C,0x38E,0x38F,0x3E2,0x3E4,0x3E6,0x3E8,0x3EA, - 0x410,0x411,0x412,0x413,0x414,0x415,0x416,0x417,0x418,0x419,0x41A,0x41B,0x41C,0x41D,0x41E,0x41F,0x420,0x421,0x422,0x423,0x424,0x425,0x426,0x427,0x428,0x429,0x42A,0x42B,0x42C,0x42D,0x42E,0x42F,0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40A,0x40B,0x40C,0x40E,0x40F,0x460,0x462,0x464,0x466,0x468,0x46A,0x46C,0x46E,0x470,0x472,0x474,0x476,0x478,0x47A,0x47C,0x47E,0x480,0x490,0x492,0x494,0x496,0x498,0x49A,0x49C,0x49E,0x4A0,0x4A2,0x4A4,0x4A6,0x4A8,0x4AA,0x4AC,0x4AE,0x4B0,0x4B2,0x4B4,0x4B6,0x4B8,0x4BA,0x4BC,0x4BE,0x4C1,0x4C3,0x5C7,0x4D0,0x4D2,0x4D4,0x4D6,0x4D8,0x4DA,0x4DC,0x4DE,0x4E0,0x4E2,0x4E4,0x4E6,0x4E8,0x4EA,0x4EC,0x4EE,0x4F0,0x4F2,0x4F4,0x4F8, - 0x531,0x532,0x533,0x534,0x535,0x536,0x537,0x538,0x539,0x53A,0x53B,0x53C,0x53D,0x53E,0x53F,0x540,0x541,0x542,0x543,0x544,0x545,0x546,0x547,0x548,0x549,0x54A,0x54B,0x54C,0x54D,0x54E,0x54F,0x550,0x551,0x552,0x553,0x554,0x555,0x556, - 0x1E00,0x1E02,0x1E04,0x1E06,0x1E08,0x1E0A,0x1E0C,0x1E0E,0x1E10,0x1E12,0x1E14,0x1E16,0x1E18,0x1E1A,0x1E1C,0x1E1E,0x1E20,0x1E22,0x1E24,0x1E26,0x1E28,0x1E2A,0x1E2C,0x1E2E,0x1E30,0x1E32,0x1E34,0x1E36,0x1E38,0x1E3A,0x1E3C,0x1E3E,0x1E40,0x1E42,0x1E44,0x1E46,0x1E48,0x1E4A,0x1E4C,0x1E4E,0x1E50,0x1E52,0x1E54,0x1E56,0x1E58,0x1E5A,0x1E5C,0x1E5E,0x1E60,0x1E62,0x1E64,0x1E66,0x1E68,0x1E6A,0x1E6C,0x1E6E,0x1E70,0x1E72,0x1E74,0x1E76,0x1E78,0x1E7A,0x1E7C,0x1E7E,0x1E80,0x1E82,0x1E84,0x1E86,0x1E88,0x1E8A,0x1E8C,0x1E8E,0x1E90,0x1E92,0x1E94,0x1E96,0x1E98,0x1E9A,0x1E9C,0x1E9E,0x1EA0,0x1EA2,0x1EA4,0x1EA6,0x1EA8,0x1EAA,0x1EAC,0x1EAE,0x1EB0,0x1EB2,0x1EB4,0x1EB6,0x1EB8,0x1EBA,0x1EBC,0x1EBE,0x1EC0,0x1EC2,0x1EC4,0x1EC6,0x1EC8,0x1ECA,0x1ECC,0x1ECE,0x1ED0,0x1ED2,0x1ED4,0x1ED6,0x1ED8,0x1EDA,0x1EDC,0x1EDE,0x1EE0,0x1EE2,0x1EE4,0x1EE6,0x1EE8,0x1EEA,0x1EEC,0x1EEE,0x1EF0,0x1EF2,0x1EF4,0x1EF6,0x1EF8, - 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216A,0x216B,0x216C,0x216D,0x216E,0x216F, - 0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A + static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */ + /* Phonetic Extensions */ + 0x1D7D,0x0001,0x2C63, + /* Latin Extended Additional */ + 0x1E00,0x0196, 0x1EA0,0x015A, + /* Greek Extended */ + 0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606, + 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608, + 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB, + 0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC, + 0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF2,0x0001,0x1FFC, + /* Letterlike Symbols */ + 0x214E,0x0001,0x2132, + /* Number forms */ + 0x2170,0x0210, 0x2184,0x0001,0x2183, + /* Enclosed Alphanumerics */ + 0x24D0,0x051A, 0x2C30,0x042F, + /* Latin Extended-C */ + 0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102, + /* Coptic */ + 0x2C80,0x0164, + /* Georgian Supplement */ + 0x2D00,0x0826, + /* Full-width */ + 0xFF41,0x031A, + + 0x0000 }; - UINT i, n, hi, li; + const WCHAR *p; + WCHAR bc, nc, cmd; - if (chr < 0x80) { /* ASCII characters (acceleration) */ - if (chr >= 0x61 && chr <= 0x7A) chr -= 0x20; - - } else { /* Extended characters */ - n = 12; li = 0; hi = sizeof lower / sizeof lower[0]; - do { - i = li + (hi - li) / 2; - if (chr == lower[i]) break; - if (chr > lower[i]) li = i; else hi = i; - } while (--n); - if (n) chr = upper[i]; + p = chr < 0x1000 ? cvt1 : cvt2; + for (;;) { + bc = *p++; /* Get block base */ + if (!bc || chr < bc) break; + nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ + if (chr < bc + nc) { /* In the block? */ + switch (cmd) { + case 0: chr = p[chr - bc]; break; /* Table conversion */ + case 1: chr -= (chr - bc) & 1; break; /* Case pairs */ + case 2: chr -= 16; break; /* Shift -16 */ + case 3: chr -= 32; break; /* Shift -32 */ + case 4: chr -= 48; break; /* Shift -48 */ + case 5: chr -= 26; break; /* Shift -26 */ + case 6: chr += 8; break; /* Shift +8 */ + case 7: chr -= 80; break; /* Shift -80 */ + case 8: chr -= 0x1C60; break; /* Shift -0x1C60 */ + } + break; + } + if (!cmd) p += nc; } return chr; diff --git a/firmware/chibios-portapack/ext/fatfs/src/option/ccsbcs.c b/firmware/chibios-portapack/ext/fatfs/src/option/ccsbcs.c index 37f763ef..164caa06 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/option/ccsbcs.c +++ b/firmware/chibios-portapack/ext/fatfs/src/option/ccsbcs.c @@ -300,47 +300,87 @@ WCHAR ff_convert ( /* Converted character, Returns zero on error */ - WCHAR ff_wtoupper ( /* Returns upper converted character */ - WCHAR chr /* Unicode character to be upper converted */ + WCHAR chr /* Unicode character to be upper converted (BMP only) */ ) { - static const WCHAR lower[] = { /* Lower case characters to be converted */ - /* Latin Supplement */ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, - /* Latin Extended-A */ 0x101,0x103,0x105,0x107,0x109,0x10B,0x10D,0x10F,0x111,0x113,0x115,0x117,0x119,0x11B,0x11D,0x11F,0x121,0x123,0x125,0x127,0x129,0x12B,0x12D,0x12F,0x131,0x133,0x135,0x137,0x13A,0x13C,0x13E,0x140,0x142,0x144,0x146,0x148,0x14B,0x14D,0x14F,0x151,0x153,0x155,0x157,0x159,0x15B,0x15D,0x15F,0x161,0x163,0x165,0x167,0x169,0x16B,0x16D,0x16F,0x171,0x173,0x175,0x177,0x17A,0x17C,0x17E, - /* Latin Extended-B */ 0x183,0x185,0x188,0x18C,0x192,0x199,0x1A1,0x1A3,0x1A8,0x1AD,0x1B0,0x1B4,0x1B6,0x1B9,0x1BD,0x1C6,0x1C9,0x1CC,0x1CE,0x1D0,0x1D2,0x1D4,0x1D6,0x1D8,0x1DA,0x1DC,0x1DD,0x1DF,0x1E1,0x1E3,0x1E5,0x1E7,0x1E9,0x1EB,0x1ED,0x1EF,0x1F3,0x1F5,0x1FB,0x1FD,0x1FF,0x201,0x203,0x205,0x207,0x209,0x20B,0x20D,0x20F,0x211,0x213,0x215,0x217, - /* Greek, Coptic */ 0x3B1,0x3B2,0x3B3,0x3B4,0x3B5,0x3B6,0x3B7,0x3B8,0x3B9,0x3BA,0x3BB,0x3BC,0x3BD,0x3BE,0x3BF,0x3C0,0x3C1,0x3C3,0x3C4,0x3C5,0x3C6,0x3C7,0x3C8,0x3C9,0x3CA,0x3CB,0x3CC,0x3CD,0x3CE,0x3E3,0x3E5,0x3E7,0x3E9,0x3EB, - /* Cyrillic */ 0x430,0x431,0x432,0x433,0x434,0x435,0x436,0x437,0x438,0x439,0x43A,0x43B,0x43C,0x43D,0x43E,0x43F,0x440,0x441,0x442,0x443,0x444,0x445,0x446,0x447,0x448,0x449,0x44A,0x44B,0x44C,0x44D,0x44E,0x44F,0x452,0x453,0x454,0x455,0x456,0x457,0x458,0x459,0x45A,0x45B,0x45C,0x45E,0x45F,0x461,0x463,0x465,0x467,0x469,0x46B,0x46D,0x46F,0x471,0x473,0x475,0x477,0x479,0x47B,0x47D,0x47F,0x481,0x491,0x493,0x495,0x497,0x499,0x49B,0x49D,0x49F,0x4A1,0x4A3,0x4A5,0x4A7,0x4A9,0x4AB,0x4AD,0x4AF,0x4B1,0x4B3,0x4B5,0x4B7,0x4B9,0x4BB,0x4BD,0x4BF,0x4C2,0x4C4,0x4C8,0x4D1,0x4D3,0x4D5,0x4D7,0x4D9,0x4DB,0x4DD,0x4DF,0x4E1,0x4E3,0x4E5,0x4E7,0x4E9,0x4EB,0x4ED,0x4EF,0x4F1,0x4F3,0x4F5,0x4F9, - /* Armenian */ 0x561,0x562,0x563,0x564,0x565,0x566,0x567,0x568,0x569,0x56A,0x56B,0x56C,0x56D,0x56E,0x56F,0x570,0x571,0x572,0x573,0x574,0x575,0x576,0x577,0x578,0x579,0x57A,0x57B,0x57C,0x57D,0x57E,0x57F,0x580,0x581,0x582,0x583,0x584,0x585,0x586, - /* Latin Extended Additional */ 0x1E01,0x1E03,0x1E05,0x1E07,0x1E09,0x1E0B,0x1E0D,0x1E0F,0x1E11,0x1E13,0x1E15,0x1E17,0x1E19,0x1E1B,0x1E1D,0x1E1F,0x1E21,0x1E23,0x1E25,0x1E27,0x1E29,0x1E2B,0x1E2D,0x1E2F,0x1E31,0x1E33,0x1E35,0x1E37,0x1E39,0x1E3B,0x1E3D,0x1E3F,0x1E41,0x1E43,0x1E45,0x1E47,0x1E49,0x1E4B,0x1E4D,0x1E4F,0x1E51,0x1E53,0x1E55,0x1E57,0x1E59,0x1E5B,0x1E5D,0x1E5F,0x1E61,0x1E63,0x1E65,0x1E67,0x1E69,0x1E6B,0x1E6D,0x1E6F,0x1E71,0x1E73,0x1E75,0x1E77,0x1E79,0x1E7B,0x1E7D,0x1E7F,0x1E81,0x1E83,0x1E85,0x1E87,0x1E89,0x1E8B,0x1E8D,0x1E8F,0x1E91,0x1E93,0x1E95,0x1E97,0x1E99,0x1E9B,0x1E9D,0x1E9F,0x1EA1,0x1EA3,0x1EA5,0x1EA7,0x1EA9,0x1EAB,0x1EAD,0x1EAF,0x1EB1,0x1EB3,0x1EB5,0x1EB7,0x1EB9,0x1EBB,0x1EBD,0x1EBF,0x1EC1,0x1EC3,0x1EC5,0x1EC7,0x1EC9,0x1ECB,0x1ECD,0x1ECF,0x1ED1,0x1ED3,0x1ED5,0x1ED7,0x1ED9,0x1EDB,0x1EDD,0x1EDF,0x1EE1,0x1EE3,0x1EE5,0x1EE7,0x1EE9,0x1EEB,0x1EED,0x1EEF,0x1EF1,0x1EF3,0x1EF5,0x1EF7,0x1EF9, - /* Number forms */ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F, - /* Full-width */ 0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A + /* Compressed upper conversion table */ + static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */ + /* Basic Latin */ + 0x0061,0x031A, + /* Latin-1 Supplement */ + 0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178, + /* Latin Extended-A */ + 0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106, + /* Latin Extended-B */ + 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA, + 0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128, + 0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A, + /* IPA Extensions */ + 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7, + /* Greek, Coptic */ + 0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311, + 0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118, + 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA, + /* Cyrillic */ + 0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144, + /* Armenian */ + 0x0561,0x0426, + + 0x0000 }; - static const WCHAR upper[] = { /* Upper case characters correspond to lower[] */ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x178, - 0x100,0x102,0x104,0x106,0x108,0x10A,0x10C,0x10E,0x110,0x112,0x114,0x116,0x118,0x11A,0x11C,0x11E,0x120,0x122,0x124,0x126,0x128,0x12A,0x12C,0x12E,0x130,0x132,0x134,0x136,0x139,0x13B,0x13D,0x13F,0x141,0x143,0x145,0x147,0x14A,0x14C,0x14E,0x150,0x152,0x154,0x156,0x158,0x15A,0x15C,0x15E,0x160,0x162,0x164,0x166,0x168,0x16A,0x16C,0x16E,0x170,0x172,0x174,0x176,0x179,0x17B,0x17D, - 0x182,0x184,0x187,0x18B,0x191,0x198,0x1A0,0x1A2,0x1A7,0x1AC,0x1AF,0x1B3,0x1B5,0x1B8,0x1BC,0x1C4,0x1C7,0x1CA,0x1CD,0x1CF,0x1D1,0x1D3,0x1D5,0x1D7,0x1D9,0x1DB,0x18E,0x1DE,0x1E0,0x1E2,0x1E4,0x1E6,0x1E8,0x1EA,0x1EC,0x1EE,0x1F1,0x1F4,0x1FA,0x1FC,0x1FE,0x200,0x202,0x204,0x206,0x208,0x20A,0x20C,0x20E,0x210,0x212,0x214,0x216, - 0x391,0x392,0x393,0x394,0x395,0x396,0x397,0x398,0x399,0x39A,0x39B,0x39C,0x39D,0x39E,0x39F,0x3A0,0x3A1,0x3A3,0x3A4,0x3A5,0x3A6,0x3A7,0x3A8,0x3A9,0x3AA,0x3AB,0x38C,0x38E,0x38F,0x3E2,0x3E4,0x3E6,0x3E8,0x3EA, - 0x410,0x411,0x412,0x413,0x414,0x415,0x416,0x417,0x418,0x419,0x41A,0x41B,0x41C,0x41D,0x41E,0x41F,0x420,0x421,0x422,0x423,0x424,0x425,0x426,0x427,0x428,0x429,0x42A,0x42B,0x42C,0x42D,0x42E,0x42F,0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40A,0x40B,0x40C,0x40E,0x40F,0x460,0x462,0x464,0x466,0x468,0x46A,0x46C,0x46E,0x470,0x472,0x474,0x476,0x478,0x47A,0x47C,0x47E,0x480,0x490,0x492,0x494,0x496,0x498,0x49A,0x49C,0x49E,0x4A0,0x4A2,0x4A4,0x4A6,0x4A8,0x4AA,0x4AC,0x4AE,0x4B0,0x4B2,0x4B4,0x4B6,0x4B8,0x4BA,0x4BC,0x4BE,0x4C1,0x4C3,0x5C7,0x4D0,0x4D2,0x4D4,0x4D6,0x4D8,0x4DA,0x4DC,0x4DE,0x4E0,0x4E2,0x4E4,0x4E6,0x4E8,0x4EA,0x4EC,0x4EE,0x4F0,0x4F2,0x4F4,0x4F8, - 0x531,0x532,0x533,0x534,0x535,0x536,0x537,0x538,0x539,0x53A,0x53B,0x53C,0x53D,0x53E,0x53F,0x540,0x541,0x542,0x543,0x544,0x545,0x546,0x547,0x548,0x549,0x54A,0x54B,0x54C,0x54D,0x54E,0x54F,0x550,0x551,0x552,0x553,0x554,0x555,0x556, - 0x1E00,0x1E02,0x1E04,0x1E06,0x1E08,0x1E0A,0x1E0C,0x1E0E,0x1E10,0x1E12,0x1E14,0x1E16,0x1E18,0x1E1A,0x1E1C,0x1E1E,0x1E20,0x1E22,0x1E24,0x1E26,0x1E28,0x1E2A,0x1E2C,0x1E2E,0x1E30,0x1E32,0x1E34,0x1E36,0x1E38,0x1E3A,0x1E3C,0x1E3E,0x1E40,0x1E42,0x1E44,0x1E46,0x1E48,0x1E4A,0x1E4C,0x1E4E,0x1E50,0x1E52,0x1E54,0x1E56,0x1E58,0x1E5A,0x1E5C,0x1E5E,0x1E60,0x1E62,0x1E64,0x1E66,0x1E68,0x1E6A,0x1E6C,0x1E6E,0x1E70,0x1E72,0x1E74,0x1E76,0x1E78,0x1E7A,0x1E7C,0x1E7E,0x1E80,0x1E82,0x1E84,0x1E86,0x1E88,0x1E8A,0x1E8C,0x1E8E,0x1E90,0x1E92,0x1E94,0x1E96,0x1E98,0x1E9A,0x1E9C,0x1E9E,0x1EA0,0x1EA2,0x1EA4,0x1EA6,0x1EA8,0x1EAA,0x1EAC,0x1EAE,0x1EB0,0x1EB2,0x1EB4,0x1EB6,0x1EB8,0x1EBA,0x1EBC,0x1EBE,0x1EC0,0x1EC2,0x1EC4,0x1EC6,0x1EC8,0x1ECA,0x1ECC,0x1ECE,0x1ED0,0x1ED2,0x1ED4,0x1ED6,0x1ED8,0x1EDA,0x1EDC,0x1EDE,0x1EE0,0x1EE2,0x1EE4,0x1EE6,0x1EE8,0x1EEA,0x1EEC,0x1EEE,0x1EF0,0x1EF2,0x1EF4,0x1EF6,0x1EF8, - 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216A,0x216B,0x216C,0x216D,0x216E,0x216F, - 0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A + static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */ + /* Phonetic Extensions */ + 0x1D7D,0x0001,0x2C63, + /* Latin Extended Additional */ + 0x1E00,0x0196, 0x1EA0,0x015A, + /* Greek Extended */ + 0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606, + 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608, + 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB, + 0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC, + 0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF2,0x0001,0x1FFC, + /* Letterlike Symbols */ + 0x214E,0x0001,0x2132, + /* Number forms */ + 0x2170,0x0210, 0x2184,0x0001,0x2183, + /* Enclosed Alphanumerics */ + 0x24D0,0x051A, 0x2C30,0x042F, + /* Latin Extended-C */ + 0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102, + /* Coptic */ + 0x2C80,0x0164, + /* Georgian Supplement */ + 0x2D00,0x0826, + /* Full-width */ + 0xFF41,0x031A, + + 0x0000 }; - UINT i, n, hi, li; + const WCHAR *p; + WCHAR bc, nc, cmd; - if (chr < 0x80) { /* ASCII characters (acceleration) */ - if (chr >= 0x61 && chr <= 0x7A) chr -= 0x20; - - } else { /* Non ASCII characters (table search) */ - n = 12; li = 0; hi = sizeof lower / sizeof lower[0]; - do { - i = li + (hi - li) / 2; - if (chr == lower[i]) break; - if (chr > lower[i]) li = i; else hi = i; - } while (--n); - if (n) chr = upper[i]; + p = chr < 0x1000 ? cvt1 : cvt2; + for (;;) { + bc = *p++; /* Get block base */ + if (!bc || chr < bc) break; + nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ + if (chr < bc + nc) { /* In the block? */ + switch (cmd) { + case 0: chr = p[chr - bc]; break; /* Table conversion */ + case 1: chr -= (chr - bc) & 1; break; /* Case pairs */ + case 2: chr -= 16; break; /* Shift -16 */ + case 3: chr -= 32; break; /* Shift -32 */ + case 4: chr -= 48; break; /* Shift -48 */ + case 5: chr -= 26; break; /* Shift -26 */ + case 6: chr += 8; break; /* Shift +8 */ + case 7: chr -= 80; break; /* Shift -80 */ + case 8: chr -= 0x1C60; break; /* Shift -0x1C60 */ + } + break; + } + if (!cmd) p += nc; } return chr; diff --git a/firmware/chibios-portapack/ext/fatfs/src/option/syscall.c b/firmware/chibios-portapack/ext/fatfs/src/option/syscall.c index 2036cb77..c9d219b7 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/option/syscall.c +++ b/firmware/chibios-portapack/ext/fatfs/src/option/syscall.c @@ -16,8 +16,8 @@ / the f_mount() function fails with FR_INT_ERR. */ -int ff_cre_syncobj ( /* !=0:Function succeeded, ==0:Could not create due to any error */ - BYTE vol, /* Corresponding logical drive being processed */ +int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */ + BYTE vol, /* Corresponding volume (logical drive number) */ _SYNC_t *sobj /* Pointer to return the created sync object */ ) { @@ -27,7 +27,7 @@ int ff_cre_syncobj ( /* !=0:Function succeeded, ==0:Could not create due to any *sobj = CreateMutex(NULL, FALSE, NULL); /* Win32 */ ret = (int)(*sobj != INVALID_HANDLE_VALUE); -// *sobj = SyncObjects[vol]; /* uITRON (give a static created sync object) */ +// *sobj = SyncObjects[vol]; /* uITRON (give a static sync object) */ // ret = 1; /* The initial value of the semaphore must be 1. */ // *sobj = OSMutexCreate(0, &err); /* uC/OS-II */ @@ -45,11 +45,11 @@ int ff_cre_syncobj ( /* !=0:Function succeeded, ==0:Could not create due to any /* Delete a Synchronization Object */ /*------------------------------------------------------------------------*/ /* This function is called in f_mount() function to delete a synchronization -/ object that created with ff_cre_syncobj function. When a 0 is returned, +/ object that created with ff_cre_syncobj() function. When a 0 is returned, / the f_mount() function fails with FR_INT_ERR. */ -int ff_del_syncobj ( /* !=0:Function succeeded, ==0:Could not delete due to any error */ +int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to any error */ _SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ ) { From 3a519338821acb56583773f8570405e1fb733e23 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Jul 2016 23:11:23 -0700 Subject: [PATCH 002/100] FatFs: Apply patch ff12a_p1.diff. --- firmware/chibios-portapack/ext/fatfs/src/ff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/chibios-portapack/ext/fatfs/src/ff.c b/firmware/chibios-portapack/ext/fatfs/src/ff.c index c6e91b37..98369088 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ff.c +++ b/firmware/chibios-portapack/ext/fatfs/src/ff.c @@ -298,7 +298,7 @@ typedef struct { 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 869 /* Greek 2 */ From 897110f715be65563e79d3151835e9ce5e84c83e Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Jul 2016 23:12:04 -0700 Subject: [PATCH 003/100] FatFs: Apply patch ff12a_p2.diff. --- firmware/chibios-portapack/ext/fatfs/src/ff.c | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/firmware/chibios-portapack/ext/fatfs/src/ff.c b/firmware/chibios-portapack/ext/fatfs/src/ff.c index 98369088..7fe9668f 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ff.c +++ b/firmware/chibios-portapack/ext/fatfs/src/ff.c @@ -1194,6 +1194,7 @@ FRESULT change_bitmap ( } while (bm <<= 1); /* Next bit */ bm = 1; } while (++i < SS(fs)); /* Next byte */ + i = 0; } } @@ -1263,7 +1264,7 @@ FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ if (res != FR_OK) return res; } - if (fs->free_clst != 0xFFFFFFFF) { /* Update FSINFO */ + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ fs->free_clst++; fs->fsi_flag |= 1; } @@ -3566,9 +3567,8 @@ FRESULT f_write ( if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ - /* Check fptr wrap-around (file size cannot exceed the limit on each FAT specs) */ - if ((_FS_EXFAT && fs->fs_type == FS_EXFAT && fp->fptr + btw < fp->fptr) - || (DWORD)fp->fptr + btw < (DWORD)fp->fptr) { + /* Check fptr wrap-around (file size cannot reach 4GiB on FATxx) */ + if ((!_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); } @@ -5101,8 +5101,8 @@ FRESULT f_expand ( } if (res == FR_OK) { if (opt) { - for (clst = scl; tcl; clst++, tcl--) { /* Create a cluster chain on the FAT */ - res = put_fat(fs, clst, (tcl == 1) ? 0xFFFFFFFF : clst + 1); + for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */ + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1); if (res != FR_OK) break; lclst = clst; } @@ -5112,12 +5112,18 @@ FRESULT f_expand ( } } - if (opt && res == FR_OK) { + if (res == FR_OK) { fs->last_clst = lclst; /* Set suggested start cluster to start next */ - fp->obj.sclust = scl; /* Update object allocation information */ - fp->obj.objsize = fsz; - if (_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ - fp->flag |= FA_MODIFIED; + if (opt) { + fp->obj.sclust = scl; /* Update object allocation information */ + fp->obj.objsize = fsz; + if (_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ + fp->flag |= FA_MODIFIED; + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst -= tcl; + fs->fsi_flag |= 1; + } + } } LEAVE_FF(fs, res); From aa8c8b29378f6bea955a66e4e520dc8f9da6110a Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Jul 2016 23:13:07 -0700 Subject: [PATCH 004/100] FatFs: Apply patch ff12a_p3.diff. --- firmware/chibios-portapack/ext/fatfs/src/ff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/chibios-portapack/ext/fatfs/src/ff.c b/firmware/chibios-portapack/ext/fatfs/src/ff.c index 7fe9668f..5bec9ccd 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ff.c +++ b/firmware/chibios-portapack/ext/fatfs/src/ff.c @@ -2266,7 +2266,7 @@ FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many S if (res != FR_OK) return res; dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set block position */ - if (dp->obj.stat & 4) { /* Has the sub-directory been stretched? */ + if (dp->obj.sclust != 0 && (dp->obj.stat & 4)) { /* Has the sub-directory been stretched? */ dp->obj.stat &= 3; dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */ res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */ From 4f6254cc9314ab25a6356741ba7dce82106188da Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Jul 2016 23:14:08 -0700 Subject: [PATCH 005/100] FatFs: Apply patch ff12a_p4.diff. --- firmware/chibios-portapack/ext/fatfs/src/ff.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/firmware/chibios-portapack/ext/fatfs/src/ff.c b/firmware/chibios-portapack/ext/fatfs/src/ff.c index 5bec9ccd..dd8ef500 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ff.c +++ b/firmware/chibios-portapack/ext/fatfs/src/ff.c @@ -2430,7 +2430,7 @@ void get_fileinfo ( /* No return code */ } #endif if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */ - fno->fname[i++] = (char)w; + fno->fname[i++] = (TCHAR)w; } fno->fname[i] = 0; /* Terminate the LFN */ } @@ -3680,8 +3680,9 @@ FRESULT f_sync ( FATFS *fs; DWORD tm; BYTE *dir; +#if _FS_EXFAT DEF_NAMBUF - +#endif res = validate(fp, &fs); /* Check validity of the object */ if (res == FR_OK) { @@ -4008,6 +4009,9 @@ FRESULT f_lseek ( /* Normal Seek */ { +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4GiB-1 if at FATxx */ +#endif if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ ofs = fp->obj.objsize; } From e5810ab7c77b0a478b64287bc5eba502229c1e82 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Jul 2016 23:14:43 -0700 Subject: [PATCH 006/100] FatFs: Remove (deprecated?) _USE_WRITE, _USE_IOCTL guards. --- .../os/various/fatfs_bindings/fatfs_diskio.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/firmware/chibios-portapack/os/various/fatfs_bindings/fatfs_diskio.c b/firmware/chibios-portapack/os/various/fatfs_bindings/fatfs_diskio.c index d6cd3b1f..862f27e6 100755 --- a/firmware/chibios-portapack/os/various/fatfs_bindings/fatfs_diskio.c +++ b/firmware/chibios-portapack/os/various/fatfs_bindings/fatfs_diskio.c @@ -153,7 +153,6 @@ DRESULT disk_read ( /* Write Sector(s) */ /*-----------------------------------------------------------------------*/ -#if _USE_WRITE DRESULT disk_write ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ const BYTE *buff, /* Data to be written */ @@ -190,7 +189,6 @@ DRESULT disk_write ( } return RES_PARERR; } -#endif /* _READONLY */ @@ -198,7 +196,6 @@ DRESULT disk_write ( /* Miscellaneous Functions */ /*-----------------------------------------------------------------------*/ -#if _USE_IOCTL DRESULT disk_ioctl ( BYTE pdrv, /* Physical drive nmuber (0..) */ BYTE cmd, /* Control code */ @@ -260,7 +257,6 @@ DRESULT disk_ioctl ( } return RES_PARERR; } -#endif /* _IOCTL */ DWORD get_fattime(void) { #if HAL_USE_RTC From e9d97dfd0f2c058d77100a9c9a32164770fc7c95 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Jul 2016 23:15:10 -0700 Subject: [PATCH 007/100] FatFs: Update application ffconf.h from template. --- firmware/application/ffconf.h | 143 ++++++++++++++++------------------ 1 file changed, 66 insertions(+), 77 deletions(-) diff --git a/firmware/application/ffconf.h b/firmware/application/ffconf.h index 2212ff1e..a02d51b0 100644 --- a/firmware/application/ffconf.h +++ b/firmware/application/ffconf.h @@ -2,10 +2,10 @@ #include "ch.h" /*---------------------------------------------------------------------------/ -/ FatFs - FAT file system module configuration file R0.11a (C)ChaN, 2015 +/ FatFs - FAT file system module configuration file /---------------------------------------------------------------------------*/ -#define _FFCONF 64180 /* Revision ID */ +#define _FFCONF 80186 /* Revision ID */ /*---------------------------------------------------------------------------/ / Function Configurations @@ -22,8 +22,8 @@ /* This option defines minimization level to remove some basic API functions. / / 0: All basic functions are enabled. -/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(), -/ f_truncate() and f_rename() function are removed. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() +/ are removed. / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. / 3: f_lseek() function is removed in addition to 2. */ @@ -38,8 +38,8 @@ #define _USE_FIND 1 -/* This option switches filtered directory read feature and related functions, -/ f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */ +/* This option switches filtered directory read functions, f_findfirst() and +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ #define _USE_MKFS 0 @@ -47,7 +47,16 @@ #define _USE_FASTSEEK 1 -/* This option switches fast seek feature. (0:Disable or 1:Enable) */ +/* This option switches fast seek function. (0:Disable or 1:Enable) */ + + +#define _USE_EXPAND 0 +/* This option switches f_expand function. (0:Disable or 1:Enable) */ + + +#define _USE_CHMOD 0 +/* This option switches attribute manipulation functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ #define _USE_LABEL 0 @@ -56,8 +65,7 @@ #define _USE_FORWARD 0 -/* This option switches f_forward() function. (0:Disable or 1:Enable) -/ To enable it, also _FS_TINY need to be set to 1. */ +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ /*---------------------------------------------------------------------------/ @@ -95,28 +103,30 @@ #define _USE_LFN 0 #define _MAX_LFN 255 -/* The _USE_LFN option switches the LFN feature. +/* The _USE_LFN switches the support of long file name (LFN). / -/ 0: Disable LFN feature. _MAX_LFN has no effect. +/ 0: Disable support of LFN. _MAX_LFN has no effect. / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. / 2: Enable LFN with dynamic working buffer on the STACK. / 3: Enable LFN with dynamic working buffer on the HEAP. / -/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must -/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. +/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added +/ to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and +/ additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255. +/ It should be set 255 to support full featured LFN operations. / When use stack for the working buffer, take care on stack overflow. When use heap / memory for the working buffer, memory management functions, ff_memalloc() and / ff_memfree(), must be added to the project. */ #define _LFN_UNICODE 0 -/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode) -/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE -/ to 1. This option also affects behavior of string I/O functions. */ +/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) +/ To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. +/ This option also affects behavior of string I/O functions. */ #define _STRF_ENCODE 3 -/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to +/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to / be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). / / 0: ANSI/OEM @@ -124,17 +134,16 @@ / 2: UTF-16BE / 3: UTF-8 / -/ When _LFN_UNICODE is 0, this option has no effect. */ +/ This option has no effect when _LFN_UNICODE == 0. */ #define _FS_RPATH 0 -/* This option configures relative path feature. +/* This option configures support of relative path. / -/ 0: Disable relative path feature and remove related functions. -/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available. +/ 0: Disable relative path and remove related functions. +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. / 2: f_getcwd() function is available in addition to 1. -/ -/ Note that directory items read via f_readdir() are affected by this option. */ +*/ /*---------------------------------------------------------------------------/ @@ -146,8 +155,8 @@ #define _STR_VOLUME_ID 0 -#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3" -/* _STR_VOLUME_ID option switches string volume ID feature. +#define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" +/* _STR_VOLUME_ID switches string support of volume ID. / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive / number in the path name. _VOLUME_STRS defines the drive ID strings for each / logical drives. Number of items must be equal to _VOLUMES. Valid characters for @@ -155,11 +164,12 @@ #define _MULTI_PARTITION 0 -/* This option switches multi-partition feature. By default (0), each logical drive -/ number is bound to the same physical drive number and only an FAT volume found on -/ the physical drive will be mounted. When multi-partition feature is enabled (1), -/ each logical drive number is bound to arbitrary physical drive and partition -/ listed in the VolToPart[]. Also f_fdisk() funciton will be available. */ +/* This option switches support of multi-partition on a physical drive. +/ By default (0), each logical drive number is bound to the same physical drive +/ number and only an FAT volume found on the physical drive will be mounted. +/ When multi-partition is enabled (1), each logical drive number can be bound to +/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() +/ funciton will be available. */ #define _MIN_SS 512 @@ -173,8 +183,8 @@ #define _USE_TRIM 0 -/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable) -/ To enable Trim feature, also CTRL_TRIM command should be implemented to the +/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable) +/ To enable Trim function, also CTRL_TRIM command should be implemented to the / disk_ioctl() function. */ @@ -197,46 +207,51 @@ #define _FS_TINY 0 /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) -/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS -/ bytes. Instead of private sector buffer eliminated from the file object, -/ common sector buffer in the file system object (FATFS) is used for the file -/ data transfer. */ +/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the file system object (FATFS) is used for the file data transfer. */ + + +#define _FS_EXFAT 0 +/* This option switches support of exFAT file system in addition to the traditional +/ FAT file system. (0:Disable or 1:Enable) To enable exFAT, also LFN must be enabled. +/ Note that enabling exFAT discards C89 compatibility. */ #define _FS_NORTC 0 #define _NORTC_MON 1 #define _NORTC_MDAY 1 -#define _NORTC_YEAR 2015 -/* The _FS_NORTC option switches timestamp feature. If the system does not have -/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable -/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp -/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR. -/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need -/ to be added to the project to read current time form RTC. _NORTC_MON, +#define _NORTC_YEAR 2016 +/* The option _FS_NORTC switches timestamp functiton. If the system does not have +/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable +/ the timestamp function. All objects modified by FatFs will have a fixed timestamp +/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time. +/ To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be +/ added to the project to get current time form real-time clock. _NORTC_MON, / _NORTC_MDAY and _NORTC_YEAR have no effect. -/ These options have no effect at read-only configuration (_FS_READONLY == 1). */ +/ These options have no effect at read-only configuration (_FS_READONLY = 1). */ #define _FS_LOCK 0 -/* The _FS_LOCK option switches file lock feature to control duplicated file open +/* The option _FS_LOCK switches file lock function to control duplicated file open / and illegal operation to open objects. This option must be 0 when _FS_READONLY / is 1. / -/ 0: Disable file lock feature. To avoid volume corruption, application program +/ 0: Disable file lock function. To avoid volume corruption, application program / should avoid illegal open, remove and rename to the open objects. -/ >0: Enable file lock feature. The value defines how many files/sub-directories +/ >0: Enable file lock function. The value defines how many files/sub-directories / can be opened simultaneously under file lock control. Note that the file -/ lock feature is independent of re-entrancy. */ +/ lock control is independent of re-entrancy. */ #define _FS_REENTRANT 1 #define _FS_TIMEOUT 1000 #define _SYNC_t Semaphore * -/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs +/* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs / module itself. Note that regardless of this option, file access to different / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() / and f_fdisk() function, are always not re-entrant. Only file/directory access -/ to the same volume is under control of this feature. +/ to the same volume is under control of this function. / / 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. / 1: Enable re-entrancy. Also user provided synchronization handlers, @@ -250,30 +265,4 @@ / included somewhere in the scope of ff.c. */ -#define _WORD_ACCESS 0 -/* The _WORD_ACCESS option is an only platform dependent option. It defines -/ which access method is used to the word data on the FAT volume. -/ -/ 0: Byte-by-byte access. Always compatible with all platforms. -/ 1: Word access. Do not choose this unless under both the following conditions. -/ -/ * Address misaligned memory access is always allowed to ALL instructions. -/ * Byte order on the memory is little-endian. -/ -/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size. -/ Following table shows allowable settings of some type of processors. -/ -/ ARM7TDMI 0 *2 ColdFire 0 *1 V850E 0 *2 -/ Cortex-M3 0 *3 Z80 0/1 V850ES 0/1 -/ Cortex-M0 0 *2 x86 0/1 TLCS-870 0/1 -/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1 -/ AVR32 0 *1 RL78 0 *2 R32C 0 *2 -/ PIC18 0/1 SH-2 0 *1 M16C 0/1 -/ PIC24 0 *2 H8S 0 *1 MSP430 0 *2 -/ PIC32 0 *1 H8/300H 0 *1 8051 0/1 -/ -/ *1:Big-endian. -/ *2:Unaligned memory access is not supported. -/ *3:Some compilers generate LDM/STM for mem_cpy function. -*/ - +/*--- End of configuration options ---*/ From 69173f5292f07d55dada5cfd3fcae3629f32c214 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Jul 2016 23:21:13 -0700 Subject: [PATCH 008/100] FatFs: My patch to fix wrong argument type warning. --- firmware/chibios-portapack/ext/fatfs/src/ff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/chibios-portapack/ext/fatfs/src/ff.c b/firmware/chibios-portapack/ext/fatfs/src/ff.c index dd8ef500..ca71f697 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ff.c +++ b/firmware/chibios-portapack/ext/fatfs/src/ff.c @@ -3992,7 +3992,7 @@ FRESULT f_lseek ( #if !_FS_TINY #if !_FS_READONLY if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ - if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fp, FR_DISK_ERR); + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); fp->flag &= ~FA_DIRTY; } #endif From 45a1ccbc53fa951331dd83acd19cca8b7d330b61 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 3 Aug 2016 16:12:01 -0700 Subject: [PATCH 009/100] Correctly map TX gain from dB to register value. --- firmware/application/max2837.cpp | 16 ++++++++++++++-- firmware/application/max2837.hpp | 10 +++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/firmware/application/max2837.cpp b/firmware/application/max2837.cpp index 33e4a8b6..4db7bf92 100644 --- a/firmware/application/max2837.cpp +++ b/firmware/application/max2837.cpp @@ -55,6 +55,18 @@ static uint_fast8_t gain_ordinal(const int8_t db) { } /* namespace vga */ +namespace tx { + +static uint_fast8_t gain_ordinal(const int8_t db) { + const auto db_sat = gain_db_range.clip(db); + uint8_t value = db_sat & 0x0f; + value = (db_sat >= 16) ? (value | 0x20) : value; + value = (db_sat >= 32) ? (value | 0x10) : value; + return (value & 0b111111) ^ 0b111111; +} + +} /* namespace tx */ + namespace filter { static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) { @@ -170,8 +182,8 @@ reg_t MAX2837::read(const Register reg) { return read(toUType(reg)); } -void MAX2837::set_tx_vga_gain(const int_fast8_t value) { - _map.r.tx_gain.TXVGA_GAIN_SPI = value; +void MAX2837::set_tx_vga_gain(const int_fast8_t db) { + _map.r.tx_gain.TXVGA_GAIN_SPI = tx::gain_ordinal(db); _dirty[Register::TX_GAIN] = 1; flush(); } diff --git a/firmware/application/max2837.hpp b/firmware/application/max2837.hpp index 6c91ae58..857bb4e9 100644 --- a/firmware/application/max2837.hpp +++ b/firmware/application/max2837.hpp @@ -83,6 +83,14 @@ constexpr int8_t gain_db_step = 2; /*************************************************************************/ +namespace tx { + +constexpr range_t gain_db_range { 0, 47 }; +constexpr int8_t gain_db_step = 1; +} + +/*************************************************************************/ + namespace filter { constexpr std::array bandwidths { @@ -829,7 +837,7 @@ public: void init(); void set_mode(const Mode mode); - void set_tx_vga_gain(const int_fast8_t value); + void set_tx_vga_gain(const int_fast8_t db); void set_lna_gain(const int_fast8_t db); void set_vga_gain(const int_fast8_t db); void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum); From 1e39b7ea45cee7c28b23883f5c1afcef6e7e46df Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 3 Aug 2016 16:12:22 -0700 Subject: [PATCH 010/100] Expose TX gain in radio API. --- firmware/application/radio.cpp | 4 ++++ firmware/application/radio.hpp | 1 + 2 files changed, 5 insertions(+) diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index 88ef4a21..b61ce022 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -146,6 +146,10 @@ void set_vga_gain(const int_fast8_t db) { second_if.set_vga_gain(db); } +void set_tx_gain(const int_fast8_t db) { + second_if.set_tx_vga_gain(db); +} + void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum) { second_if.set_lpf_rf_bandwidth(bandwidth_minimum); } diff --git a/firmware/application/radio.hpp b/firmware/application/radio.hpp index 4c1faa78..6d22acf3 100644 --- a/firmware/application/radio.hpp +++ b/firmware/application/radio.hpp @@ -47,6 +47,7 @@ bool set_tuning_frequency(const rf::Frequency frequency); void set_rf_amp(const bool rf_amp); void set_lna_gain(const int_fast8_t db); void set_vga_gain(const int_fast8_t db); +void set_tx_gain(const int_fast8_t db); void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum); void set_baseband_rate(const uint32_t rate); void set_baseband_decimation_by(const size_t n); From ef9b4051b78bbac5e28551132f0a24da188b2fa3 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 3 Aug 2016 16:13:54 -0700 Subject: [PATCH 011/100] Expose TX gain on ReceiverModel. Obviously, ReceiverModel is an even worse name/concept than it was before. --- firmware/application/receiver_model.cpp | 14 ++++++++++++++ firmware/application/receiver_model.hpp | 5 +++++ 2 files changed, 19 insertions(+) diff --git a/firmware/application/receiver_model.cpp b/firmware/application/receiver_model.cpp index 35059f5c..b5796c38 100644 --- a/firmware/application/receiver_model.cpp +++ b/firmware/application/receiver_model.cpp @@ -115,6 +115,15 @@ void ReceiverModel::set_vga(int32_t v_db) { update_vga(); } +int32_t ReceiverModel::tx_gain() const { + return tx_gain_db_; +} + +void ReceiverModel::set_tx_gain(int32_t v_db) { + tx_gain_db_ = v_db; + update_tx_gain(); +} + uint32_t ReceiverModel::sampling_rate() const { return sampling_rate_; } @@ -155,6 +164,7 @@ void ReceiverModel::enable() { update_rf_amp(); update_lna(); update_vga(); + update_tx_gain(); update_baseband_bandwidth(); update_sampling_rate(); update_modulation(); @@ -202,6 +212,10 @@ void ReceiverModel::update_vga() { radio::set_vga_gain(vga_gain_db_); } +void ReceiverModel::update_tx_gain() { + radio::set_tx_gain(tx_gain_db_); +} + void ReceiverModel::set_am_configuration(const size_t n) { if( n < am_configs.size() ) { am_config_index = n; diff --git a/firmware/application/receiver_model.hpp b/firmware/application/receiver_model.hpp index d103c4c8..4499e9fe 100644 --- a/firmware/application/receiver_model.hpp +++ b/firmware/application/receiver_model.hpp @@ -60,6 +60,9 @@ public: int32_t vga() const; void set_vga(int32_t v_db); + int32_t tx_gain() const; + void set_tx_gain(int32_t v_db); + uint32_t sampling_rate() const; void set_sampling_rate(uint32_t v); @@ -91,6 +94,7 @@ private: int32_t lna_gain_db_ { 32 }; uint32_t baseband_bandwidth_ { max2837::filter::bandwidth_minimum }; int32_t vga_gain_db_ { 32 }; + int32_t tx_gain_db_ { 47 }; Mode mode_ { Mode::NarrowbandFMAudio }; uint32_t sampling_rate_ { 3072000 }; size_t decimation_factor_ { 1 }; @@ -107,6 +111,7 @@ private: void update_lna(); void update_baseband_bandwidth(); void update_vga(); + void update_tx_gain(); void update_sampling_rate(); void update_headphone_volume(); From 447a7a5661fc04870a329ea73a414b3dfb002f70 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 3 Aug 2016 16:14:34 -0700 Subject: [PATCH 012/100] Add TXGainField user interface element. --- firmware/application/ui_receiver.cpp | 25 +++++++++++++++++++++++++ firmware/application/ui_receiver.hpp | 9 +++++++++ 2 files changed, 34 insertions(+) diff --git a/firmware/application/ui_receiver.cpp b/firmware/application/ui_receiver.cpp index 18a5c613..d48917a4 100644 --- a/firmware/application/ui_receiver.cpp +++ b/firmware/application/ui_receiver.cpp @@ -353,4 +353,29 @@ void VGAGainField::on_focus() { } } +/* TXGainField **********************************************************/ + +TXGainField::TXGainField( + Point parent_pos +) : NumberField { + parent_pos, 2, + { max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum }, + max2837::tx::gain_db_step, + ' ', + } +{ + set_value(receiver_model.tx_gain()); + + on_change = [](int32_t v) { + receiver_model.set_tx_gain(v); + }; +} + +void TXGainField::on_focus() { + //Widget::on_focus(); + if( on_show_options ) { + on_show_options(); + } +} + } /* namespace ui */ diff --git a/firmware/application/ui_receiver.hpp b/firmware/application/ui_receiver.hpp index 1d4cb6d3..eb012f4d 100644 --- a/firmware/application/ui_receiver.hpp +++ b/firmware/application/ui_receiver.hpp @@ -330,6 +330,15 @@ public: void on_focus() override; }; +class TXGainField : public NumberField { +public: + std::function on_show_options; + + TXGainField(Point parent_pos); + + void on_focus() override; +}; + } /* namespace ui */ #endif/*__UI_RECEIVER_H__*/ From c8af6dcd70535e60ba8957a6b4b1a12812c6ee95 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 10 Aug 2016 09:53:35 -0700 Subject: [PATCH 013/100] Add SMULL instruction inline function. --- .../os/hal/platforms/LPC43xx_M4/lpc43xx_m4.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/firmware/chibios-portapack/os/hal/platforms/LPC43xx_M4/lpc43xx_m4.h b/firmware/chibios-portapack/os/hal/platforms/LPC43xx_M4/lpc43xx_m4.h index 25f56907..d56457ae 100644 --- a/firmware/chibios-portapack/os/hal/platforms/LPC43xx_M4/lpc43xx_m4.h +++ b/firmware/chibios-portapack/os/hal/platforms/LPC43xx_M4/lpc43xx_m4.h @@ -196,6 +196,20 @@ __attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __SMULTT(uint32_t op1 return result; } +#undef __SMULL + +__attribute__( ( always_inline ) ) static inline int64_t __SMULL (int32_t op1, int32_t op2) +{ + union llreg_u{ + uint32_t w32[2]; + int64_t w64; + } llr; + + __asm volatile ("smull %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2)); + + return(llr.w64); +} + #undef __SMLALD __attribute__( ( always_inline ) ) static inline int64_t __SMLALD (uint32_t op1, uint32_t op2, int64_t acc) From 96da55d83aeadd92ac9e57dec9a38057b1b35f67 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 10 Aug 2016 10:34:14 -0700 Subject: [PATCH 014/100] wait_for_buffer() now handles TX buffers. Feels a bit awkward to read LLI src/dest to determine if RX or TX. But it works. --- firmware/baseband/baseband_dma.cpp | 7 +++++-- firmware/baseband/baseband_dma.hpp | 2 +- firmware/baseband/baseband_thread.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/firmware/baseband/baseband_dma.cpp b/firmware/baseband/baseband_dma.cpp index d86bef72..4fc1fdd8 100644 --- a/firmware/baseband/baseband_dma.cpp +++ b/firmware/baseband/baseband_dma.cpp @@ -156,12 +156,15 @@ void disable() { gpdma_channel_sgpio.disable(); } -baseband::buffer_t wait_for_rx_buffer() { +baseband::buffer_t wait_for_buffer() { const auto next_index = thread_wait.sleep(); if( next_index >= 0 ) { const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; - return { reinterpret_cast(lli_loop[free_index].destaddr), transfer_samples }; + const auto src = lli_loop[free_index].srcaddr; + const auto dst = lli_loop[free_index].destaddr; + const auto p = (src == reinterpret_cast(&LPC_SGPIO->REG_SS[0])) ? dst : src; + return { reinterpret_cast(p), transfer_samples }; } else { return { }; } diff --git a/firmware/baseband/baseband_dma.hpp b/firmware/baseband/baseband_dma.hpp index 94827d4b..56752ddc 100644 --- a/firmware/baseband/baseband_dma.hpp +++ b/firmware/baseband/baseband_dma.hpp @@ -42,7 +42,7 @@ bool is_enabled(); void disable(); -baseband::buffer_t wait_for_rx_buffer(); +baseband::buffer_t wait_for_buffer(); } /* namespace dma */ } /* namespace baseband */ diff --git a/firmware/baseband/baseband_thread.cpp b/firmware/baseband/baseband_thread.cpp index 0784d9bd..926e4fb0 100644 --- a/firmware/baseband/baseband_thread.cpp +++ b/firmware/baseband/baseband_thread.cpp @@ -77,7 +77,7 @@ void BasebandThread::run() { while( !chThdShouldTerminate() ) { // TODO: Place correct sampling rate into buffer returned here: - const auto buffer_tmp = baseband::dma::wait_for_rx_buffer(); + const auto buffer_tmp = baseband::dma::wait_for_buffer(); if( buffer_tmp ) { buffer_c8_t buffer { buffer_tmp.p, buffer_tmp.count, sampling_rate From 2ec1bab5d5f153172163ac6a2e585eb9a4a66f36 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 10 Aug 2016 10:36:03 -0700 Subject: [PATCH 015/100] Plumb BasebandThread to accept direction argument. Default is to receive, for compatibility with existing users. --- firmware/baseband/baseband_thread.cpp | 6 ++++-- firmware/baseband/baseband_thread.hpp | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/firmware/baseband/baseband_thread.cpp b/firmware/baseband/baseband_thread.cpp index 926e4fb0..3f683a59 100644 --- a/firmware/baseband/baseband_thread.cpp +++ b/firmware/baseband/baseband_thread.cpp @@ -44,9 +44,11 @@ Thread* BasebandThread::thread = nullptr; BasebandThread::BasebandThread( uint32_t sampling_rate, BasebandProcessor* const baseband_processor, - const tprio_t priority + const tprio_t priority, + baseband::Direction direction ) : baseband_processor { baseband_processor }, - sampling_rate { sampling_rate } + sampling_rate { sampling_rate }, + _direction { direction } { thread = chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa), priority, ThreadBase::fn, diff --git a/firmware/baseband/baseband_thread.hpp b/firmware/baseband/baseband_thread.hpp index 3263b4f1..4d06e326 100644 --- a/firmware/baseband/baseband_thread.hpp +++ b/firmware/baseband/baseband_thread.hpp @@ -33,20 +33,22 @@ public: BasebandThread( uint32_t sampling_rate, BasebandProcessor* const baseband_processor, - const tprio_t priority + const tprio_t priority, + const baseband::Direction direction = baseband::Direction::Receive ); ~BasebandThread(); // This getter should die, it's just here to leak information to code that // isn't in the right place to begin with. baseband::Direction direction() const { - return baseband::Direction::Receive; + return _direction; } private: static Thread* thread; BasebandProcessor* baseband_processor { nullptr }; + baseband::Direction _direction; uint32_t sampling_rate; void run() override; From 62d2ae2336bd847412dd80562e900adb81af951f Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Aug 2016 16:42:39 -0700 Subject: [PATCH 016/100] SGPIO: Change bus direction more deliberately. There may have been an instant where the CPLD and SGPIO were driving the bus simultaneously, when switching from TX to RX. --- firmware/common/baseband_sgpio.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/firmware/common/baseband_sgpio.cpp b/firmware/common/baseband_sgpio.cpp index e4554e96..a718c6fc 100644 --- a/firmware/common/baseband_sgpio.cpp +++ b/firmware/common/baseband_sgpio.cpp @@ -299,7 +299,9 @@ void SGPIO::configure(const Direction direction) { disable_all_slice_counters(); LPC_SGPIO->GPIO_OUTREG = gpio_outreg(direction); - LPC_SGPIO->GPIO_OENREG = gpio_oenreg(direction); + + // Set data pins as input, temporarily. + LPC_SGPIO->GPIO_OENREG = gpio_oenreg(Direction::Receive); LPC_SGPIO->OUT_MUX_CFG[ 8] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE); LPC_SGPIO->OUT_MUX_CFG[ 9] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE); LPC_SGPIO->OUT_MUX_CFG[10] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); @@ -314,6 +316,9 @@ void SGPIO::configure(const Direction direction) { LPC_SGPIO->OUT_MUX_CFG[i] = data_out_mux_cfg; } + // Now that output enable sources are set, enable data bus in correct direction. + LPC_SGPIO->GPIO_OENREG = gpio_oenreg(direction); + const auto slice_gpdma = Slice::H; const size_t slice_count = slice_mode_multislice ? 8 : 1; From 52c089c4dfb4298f2f0d06eea26e85a4d69251cb Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Aug 2016 16:46:02 -0700 Subject: [PATCH 017/100] SGPIO: Hi-Z data bus before setting direction pin. Another tactic to avoid bus contention, however brief. --- firmware/common/baseband_sgpio.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/firmware/common/baseband_sgpio.cpp b/firmware/common/baseband_sgpio.cpp index a718c6fc..a574e81d 100644 --- a/firmware/common/baseband_sgpio.cpp +++ b/firmware/common/baseband_sgpio.cpp @@ -298,10 +298,12 @@ constexpr P_OUT_CFG data_p_out_cfg( void SGPIO::configure(const Direction direction) { disable_all_slice_counters(); - LPC_SGPIO->GPIO_OUTREG = gpio_outreg(direction); - // Set data pins as input, temporarily. LPC_SGPIO->GPIO_OENREG = gpio_oenreg(Direction::Receive); + + // Now that data pins are inputs, safe to change CPLD direction. + LPC_SGPIO->GPIO_OUTREG = gpio_outreg(direction); + LPC_SGPIO->OUT_MUX_CFG[ 8] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE); LPC_SGPIO->OUT_MUX_CFG[ 9] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE); LPC_SGPIO->OUT_MUX_CFG[10] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE); From b0a3f680e52944c6b24928827eccfc723cd42459 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 21 Aug 2016 11:31:37 -0700 Subject: [PATCH 018/100] CPLD: Remove decimation feature. --- firmware/application/ais_app.cpp | 1 - firmware/application/baseband_cpld.cpp | 13 ------------- firmware/application/baseband_cpld.hpp | 1 - firmware/application/capture_app.cpp | 1 - firmware/application/ert_app.cpp | 1 - firmware/application/radio.cpp | 5 ----- firmware/application/radio.hpp | 2 -- firmware/application/receiver_model.cpp | 8 +------- firmware/application/receiver_model.hpp | 3 --- firmware/application/tpms_app.cpp | 1 - firmware/common/hackrf_gpio.hpp | 5 ----- 11 files changed, 1 insertion(+), 40 deletions(-) diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index 814262e6..189ecacc 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -315,7 +315,6 @@ AISAppView::AISAppView(NavigationView&) { receiver_model.rf_amp(), static_cast(receiver_model.lna()), static_cast(receiver_model.vga()), - 1, }); options_channel.on_change = [this](size_t, OptionsField::value_t v) { diff --git a/firmware/application/baseband_cpld.cpp b/firmware/application/baseband_cpld.cpp index 50949cc7..1c42daa0 100644 --- a/firmware/application/baseband_cpld.cpp +++ b/firmware/application/baseband_cpld.cpp @@ -27,23 +27,10 @@ using namespace hackrf::one; namespace baseband { void CPLD::init() { - set_decimation_by(1); - gpios_baseband_decimation[0].output(); - gpios_baseband_decimation[1].output(); - gpios_baseband_decimation[2].output(); - set_q_invert(false); gpio_baseband_q_invert.output(); } -void CPLD::set_decimation_by(const uint8_t n) { - const uint8_t skip_n = n - 1; - const uint8_t value = skip_n ^ 7; - gpios_baseband_decimation[0].write(value & 1); - gpios_baseband_decimation[1].write(value & 2); - gpios_baseband_decimation[2].write(value & 4); -} - void CPLD::set_q_invert(const bool invert) { gpio_baseband_q_invert.write(invert); } diff --git a/firmware/application/baseband_cpld.hpp b/firmware/application/baseband_cpld.hpp index 2482306a..5124ba7f 100644 --- a/firmware/application/baseband_cpld.hpp +++ b/firmware/application/baseband_cpld.hpp @@ -30,7 +30,6 @@ class CPLD { public: void init(); - void set_decimation_by(const uint8_t n); void set_q_invert(const bool invert); private: diff --git a/firmware/application/capture_app.cpp b/firmware/application/capture_app.cpp index 87d40114..6ebcd02f 100644 --- a/firmware/application/capture_app.cpp +++ b/firmware/application/capture_app.cpp @@ -74,7 +74,6 @@ CaptureAppView::CaptureAppView(NavigationView& nav) { receiver_model.rf_amp(), static_cast(receiver_model.lna()), static_cast(receiver_model.vga()), - 1, }); record_view.set_sampling_rate(sampling_rate / 8); diff --git a/firmware/application/ert_app.cpp b/firmware/application/ert_app.cpp index e5597cd8..4ec70f93 100644 --- a/firmware/application/ert_app.cpp +++ b/firmware/application/ert_app.cpp @@ -142,7 +142,6 @@ ERTAppView::ERTAppView(NavigationView&) { receiver_model.rf_amp(), static_cast(receiver_model.lna()), static_cast(receiver_model.vga()), - 1, }); logger = std::make_unique(); diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index b61ce022..27cd253c 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -158,10 +158,6 @@ void set_baseband_rate(const uint32_t rate) { portapack::clock_manager.set_sampling_frequency(rate); } -void set_baseband_decimation_by(const size_t n) { - baseband_cpld.set_decimation_by(n); -} - void set_antenna_bias(const bool on) { /* Pull MOSFET gate low to turn on antenna bias. */ first_if.set_gpo1(on ? 0 : 1); @@ -185,7 +181,6 @@ void configure(Configuration configuration) { set_lna_gain(configuration.lna_gain); set_vga_gain(configuration.vga_gain); set_baseband_rate(configuration.baseband_rate); - set_baseband_decimation_by(configuration.baseband_decimation); set_baseband_filter_bandwidth(configuration.baseband_filter_bandwidth); set_direction(configuration.direction); } diff --git a/firmware/application/radio.hpp b/firmware/application/radio.hpp index 6d22acf3..cb1929c8 100644 --- a/firmware/application/radio.hpp +++ b/firmware/application/radio.hpp @@ -37,7 +37,6 @@ struct Configuration { bool rf_amp; int8_t lna_gain; int8_t vga_gain; - uint8_t baseband_decimation; }; void init(); @@ -50,7 +49,6 @@ void set_vga_gain(const int_fast8_t db); void set_tx_gain(const int_fast8_t db); void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum); void set_baseband_rate(const uint32_t rate); -void set_baseband_decimation_by(const size_t n); void set_antenna_bias(const bool on); void enable(Configuration configuration); diff --git a/firmware/application/receiver_model.cpp b/firmware/application/receiver_model.cpp index b5796c38..48d8447b 100644 --- a/firmware/application/receiver_model.cpp +++ b/firmware/application/receiver_model.cpp @@ -151,11 +151,6 @@ void ReceiverModel::set_headphone_volume(volume_t v) { update_headphone_volume(); } -uint32_t ReceiverModel::baseband_oversampling() const { - // TODO: Rename decimation_factor. - return decimation_factor_; -} - void ReceiverModel::enable() { enabled_ = true; radio::set_direction(rf::Direction::Receive); @@ -243,9 +238,8 @@ void ReceiverModel::update_sampling_rate() { // protocols that need quick RX/TX turn-around. // Disabling baseband while changing sampling rates seems like a good idea... - radio::set_baseband_rate(sampling_rate() * baseband_oversampling()); + radio::set_baseband_rate(sampling_rate()); update_tuning_frequency(); - radio::set_baseband_decimation_by(baseband_oversampling()); } void ReceiverModel::update_headphone_volume() { diff --git a/firmware/application/receiver_model.hpp b/firmware/application/receiver_model.hpp index 4499e9fe..1d6d4783 100644 --- a/firmware/application/receiver_model.hpp +++ b/firmware/application/receiver_model.hpp @@ -72,8 +72,6 @@ public: volume_t headphone_volume() const; void set_headphone_volume(volume_t v); - uint32_t baseband_oversampling() const; - void enable(); void disable(); @@ -97,7 +95,6 @@ private: int32_t tx_gain_db_ { 47 }; Mode mode_ { Mode::NarrowbandFMAudio }; uint32_t sampling_rate_ { 3072000 }; - size_t decimation_factor_ { 1 }; size_t am_config_index = 0; size_t nbfm_config_index = 0; size_t wfm_config_index = 0; diff --git a/firmware/application/tpms_app.cpp b/firmware/application/tpms_app.cpp index 008a920a..cc55f276 100644 --- a/firmware/application/tpms_app.cpp +++ b/firmware/application/tpms_app.cpp @@ -184,7 +184,6 @@ TPMSAppView::TPMSAppView(NavigationView&) { receiver_model.rf_amp(), static_cast(receiver_model.lna()), static_cast(receiver_model.vga()), - 1, }); options_band.on_change = [this](size_t, OptionsField::value_t v) { diff --git a/firmware/common/hackrf_gpio.hpp b/firmware/common/hackrf_gpio.hpp index 57257f06..62d32446 100644 --- a/firmware/common/hackrf_gpio.hpp +++ b/firmware/common/hackrf_gpio.hpp @@ -71,11 +71,6 @@ constexpr GPIO gpio_max2837_txenable = gpio[GPIO2_4]; constexpr GPIO gpio_max5864_select = gpio[GPIO2_7]; -constexpr std::array gpios_baseband_decimation { - gpio[GPIO5_12], - gpio[GPIO5_13], - gpio[GPIO5_14] -}; constexpr GPIO gpio_baseband_q_invert = gpio[GPIO0_13]; constexpr GPIO gpio_cpld_tdo = gpio[GPIO5_18]; From 77016b9a40178ab99df3ada3a137311d0dce5550 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 21 Aug 2016 11:34:46 -0700 Subject: [PATCH 019/100] Rename CPLD "Q_INVERT" to signal to "INVERT". Don't expose detail in name about how the task is accomplished. --- firmware/application/baseband_cpld.cpp | 8 ++++---- firmware/application/baseband_cpld.hpp | 2 +- firmware/application/radio.cpp | 2 +- firmware/application/tuning.cpp | 8 ++++---- firmware/application/tuning.hpp | 8 ++++---- firmware/common/hackrf_gpio.hpp | 2 +- firmware/common/pins.hpp | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/firmware/application/baseband_cpld.cpp b/firmware/application/baseband_cpld.cpp index 1c42daa0..ded20b48 100644 --- a/firmware/application/baseband_cpld.cpp +++ b/firmware/application/baseband_cpld.cpp @@ -27,12 +27,12 @@ using namespace hackrf::one; namespace baseband { void CPLD::init() { - set_q_invert(false); - gpio_baseband_q_invert.output(); + set_invert(false); + gpio_baseband_invert.output(); } -void CPLD::set_q_invert(const bool invert) { - gpio_baseband_q_invert.write(invert); +void CPLD::set_invert(const bool invert) { + gpio_baseband_invert.write(invert); } } diff --git a/firmware/application/baseband_cpld.hpp b/firmware/application/baseband_cpld.hpp index 5124ba7f..6bcb63aa 100644 --- a/firmware/application/baseband_cpld.hpp +++ b/firmware/application/baseband_cpld.hpp @@ -30,7 +30,7 @@ class CPLD { public: void init(); - void set_q_invert(const bool invert); + void set_invert(const bool invert); private: }; diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index 27cd253c..681f5560 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -126,7 +126,7 @@ bool set_tuning_frequency(const rf::Frequency frequency) { const auto result_second_if = second_if.set_frequency(tuning_config.second_lo_frequency); rf_path.set_band(tuning_config.rf_path_band); - baseband_cpld.set_q_invert(tuning_config.baseband_q_invert); + baseband_cpld.set_invert(tuning_config.baseband_invert); return result_second_if; } else { diff --git a/firmware/application/tuning.cpp b/firmware/application/tuning.cpp index 0ca8711b..310b3213 100644 --- a/firmware/application/tuning.cpp +++ b/firmware/application/tuning.cpp @@ -49,8 +49,8 @@ constexpr rf::Frequency high_band_second_lo_frequency(const rf::Frequency target Config low_band(const rf::Frequency target_frequency) { const rf::Frequency first_lo_frequency = target_frequency + low_band_second_lo_frequency(target_frequency); const rf::Frequency second_lo_frequency = first_lo_frequency - target_frequency; - const bool baseband_q_invert = true; - return { first_lo_frequency, second_lo_frequency, rf::path::Band::Low, baseband_q_invert }; + const bool baseband_invert = true; + return { first_lo_frequency, second_lo_frequency, rf::path::Band::Low, baseband_invert }; } Config mid_band(const rf::Frequency target_frequency) { @@ -60,8 +60,8 @@ Config mid_band(const rf::Frequency target_frequency) { Config high_band(const rf::Frequency target_frequency) { const rf::Frequency first_lo_frequency = target_frequency - high_band_second_lo_frequency(target_frequency); const rf::Frequency second_lo_frequency = target_frequency - first_lo_frequency; - const bool baseband_q_invert = false; - return { first_lo_frequency, second_lo_frequency, rf::path::Band::High, baseband_q_invert }; + const bool baseband_invert = false; + return { first_lo_frequency, second_lo_frequency, rf::path::Band::High, baseband_invert }; } } /* namespace */ diff --git a/firmware/application/tuning.hpp b/firmware/application/tuning.hpp index 16b19fd9..16b4509a 100644 --- a/firmware/application/tuning.hpp +++ b/firmware/application/tuning.hpp @@ -33,7 +33,7 @@ struct Config { ) : first_lo_frequency(0), second_lo_frequency(0), rf_path_band(rf::path::Band::Mid), - baseband_q_invert(false) + baseband_invert(false) { } @@ -41,11 +41,11 @@ struct Config { rf::Frequency first_lo_frequency, rf::Frequency second_lo_frequency, rf::path::Band rf_path_band, - bool baseband_q_invert + bool baseband_invert ) : first_lo_frequency(first_lo_frequency), second_lo_frequency(second_lo_frequency), rf_path_band(rf_path_band), - baseband_q_invert(baseband_q_invert) + baseband_invert(baseband_invert) { } @@ -56,7 +56,7 @@ struct Config { const rf::Frequency first_lo_frequency; const rf::Frequency second_lo_frequency; const rf::path::Band rf_path_band; - const bool baseband_q_invert; + const bool baseband_invert; }; Config create(const rf::Frequency target_frequency); diff --git a/firmware/common/hackrf_gpio.hpp b/firmware/common/hackrf_gpio.hpp index 62d32446..41f58fc9 100644 --- a/firmware/common/hackrf_gpio.hpp +++ b/firmware/common/hackrf_gpio.hpp @@ -71,7 +71,7 @@ constexpr GPIO gpio_max2837_txenable = gpio[GPIO2_4]; constexpr GPIO gpio_max5864_select = gpio[GPIO2_7]; -constexpr GPIO gpio_baseband_q_invert = gpio[GPIO0_13]; +constexpr GPIO gpio_baseband_invert = gpio[GPIO0_13]; constexpr GPIO gpio_cpld_tdo = gpio[GPIO5_18]; constexpr GPIO gpio_cpld_tck = gpio[GPIO3_0]; diff --git a/firmware/common/pins.hpp b/firmware/common/pins.hpp index 527e4485..76f1761d 100644 --- a/firmware/common/pins.hpp +++ b/firmware/common/pins.hpp @@ -61,7 +61,7 @@ constexpr Pin pins[] = { [P1_15] = { 1, 15, PinConfig::sgpio_inout_fast(2) }, /* SGPIO2/BANK2F3M9: CPLD.74/HOST_DATA2(IO) */ [P1_16] = { 1, 16, PinConfig::sgpio_inout_fast(2) }, /* SGPIO3/BANK2F3M10: CPLD.72/HOST_DATA3(IO) */ [P1_17] = { 1, 17, PinConfig::sgpio_out_fast_with_pullup(6) }, /* SGPIO11/P79/BANK2F3M11: CPLD.71/HOST_DIRECTION(I) */ - [P1_18] = { 1, 18, PinConfig::gpio_out_with_pulldown(0) }, /* SGPIO12/BANK2F3M12: CPLD.70/HOST_Q_INVERT(I) */ + [P1_18] = { 1, 18, PinConfig::gpio_out_with_pulldown(0) }, /* SGPIO12/BANK2F3M12: CPLD.70/HOST_INVERT(I) */ [P1_19] = { 1, 19, { .mode=1, .pd=1, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* SSP1_SCK/P39: MAX2837.SCLK(I), MAX5864.SCLK(I) */ [P1_20] = { 1, 20, { .mode=0, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* CS_XCVR/P53: MAX2837.CS(I) */ [P2_0] = { 2, 0, { .mode=4, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* U0_TXD: PortaPack P2_0/IO_STBX */ From f20647feb4e74529452199b174255143333c17b8 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 21 Aug 2016 11:42:05 -0700 Subject: [PATCH 020/100] MAX2837: Expose trim/bias/calibration adjustments. --- firmware/application/max2837.cpp | 28 ++++++++++++++++++++++++++++ firmware/application/max2837.hpp | 5 +++++ 2 files changed, 33 insertions(+) diff --git a/firmware/application/max2837.cpp b/firmware/application/max2837.cpp index 4db7bf92..4825da10 100644 --- a/firmware/application/max2837.cpp +++ b/firmware/application/max2837.cpp @@ -242,6 +242,34 @@ bool MAX2837::set_frequency(const rf::Frequency lo_frequency) { return true; } +void MAX2837::set_rx_lo_iq_calibration(const size_t v) { + _map.r.rx_top_rx_bias.RX_IQERR_SPI_EN = 1; + _dirty[Register::RX_TOP_RX_BIAS] = 1; + _map.r.rxrf_2.iqerr_trim = v; + _dirty[Register::RXRF_2] = 1; + flush(); +} + +void MAX2837::set_rx_bias_trim(const size_t v) { + _map.r.rx_top_rx_bias.EN_Bias_Trim = 1; + _map.r.rx_top_rx_bias.BIAS_TRIM_SPI = v; + _dirty[Register::RX_TOP_RX_BIAS] = 1; + flush(); +} + +void MAX2837::set_vco_bias(const size_t v) { + _map.r.vco_cfg.VCO_BIAS_SPI_EN = 1; + _map.r.vco_cfg.VCO_BIAS_SPI = v; + _dirty[Register::VCO_CFG] = 1; + flush(); +} + +void MAX2837::set_rx_buff_vcm(const size_t v) { + _map.r.lpf_3_vga_1.BUFF_VCM = v; + _dirty[Register::LPF_3_VGA_1] = 1; + flush(); +} + reg_t MAX2837::temp_sense() { if( !_map.r.rx_top.ts_en ) { _map.r.rx_top.ts_en = 1; diff --git a/firmware/application/max2837.hpp b/firmware/application/max2837.hpp index 857bb4e9..3f1beb70 100644 --- a/firmware/application/max2837.hpp +++ b/firmware/application/max2837.hpp @@ -884,6 +884,11 @@ public: bool set_frequency(const rf::Frequency lo_frequency); + void set_rx_lo_iq_calibration(const size_t v); + void set_rx_bias_trim(const size_t v); + void set_vco_bias(const size_t v); + void set_rx_buff_vcm(const size_t v); + reg_t temp_sense(); reg_t read(const address_t reg_num); From 43a11ba04801dc34f7ea65aaec64b087ab1f8330 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 21 Aug 2016 17:49:06 -0700 Subject: [PATCH 021/100] Rename time files/namespace to not conflict with existing defs. --- firmware/application/CMakeLists.txt | 2 +- firmware/application/event_m0.cpp | 4 ++-- firmware/application/{time.cpp => rtc_time.cpp} | 6 +++--- firmware/application/{time.hpp => rtc_time.hpp} | 10 +++++----- firmware/application/ui_record_view.cpp | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) rename firmware/application/{time.cpp => rtc_time.cpp} (92%) rename firmware/application/{time.hpp => rtc_time.hpp} (88%) diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 50dc53bb..23d61736 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -170,7 +170,7 @@ set(CPPSRC ${COMMON}/ert_packet.cpp capture_app.cpp sd_card.cpp - time.cpp + rtc_time.cpp file.cpp log_file.cpp ${COMMON}/png_writer.cpp diff --git a/firmware/application/event_m0.cpp b/firmware/application/event_m0.cpp index d40d60a9..a7fae4a3 100644 --- a/firmware/application/event_m0.cpp +++ b/firmware/application/event_m0.cpp @@ -24,7 +24,7 @@ #include "portapack.hpp" #include "sd_card.hpp" -#include "time.hpp" +#include "rtc_time.hpp" #include "message.hpp" #include "message_queue.hpp" @@ -219,7 +219,7 @@ void EventDispatcher::handle_rtc_tick() { portapack::temperature_logger.second_tick(); - time::on_tick_second(); + rtc_time::on_tick_second(); } ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent event) { diff --git a/firmware/application/time.cpp b/firmware/application/rtc_time.cpp similarity index 92% rename from firmware/application/time.cpp rename to firmware/application/rtc_time.cpp index 10ba7f3b..d4a9f506 100644 --- a/firmware/application/time.cpp +++ b/firmware/application/rtc_time.cpp @@ -19,9 +19,9 @@ * Boston, MA 02110-1301, USA. */ -#include "time.hpp" +#include "rtc_time.hpp" -namespace time { +namespace rtc_time { Signal<> signal_tick_second; @@ -29,4 +29,4 @@ void on_tick_second() { signal_tick_second.emit(); } -} /* namespace time */ +} /* namespace rtc_time */ diff --git a/firmware/application/time.hpp b/firmware/application/rtc_time.hpp similarity index 88% rename from firmware/application/time.hpp rename to firmware/application/rtc_time.hpp index 470c894d..e43793be 100644 --- a/firmware/application/time.hpp +++ b/firmware/application/rtc_time.hpp @@ -19,17 +19,17 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __TIME_H__ -#define __TIME_H__ +#ifndef __RTC_TIME_H__ +#define __RTC_TIME_H__ #include "signal.hpp" -namespace time { +namespace rtc_time { extern Signal<> signal_tick_second; void on_tick_second(); -} /* namespace time */ +} /* namespace rtc_time */ -#endif/*__TIME_H__*/ +#endif/*__RTC_TIME_H__*/ diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index 695a454d..bf7ec391 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -25,7 +25,7 @@ using namespace portapack; #include "file.hpp" -#include "time.hpp" +#include "rtc_time.hpp" #include "string_format.hpp" #include "utility.hpp" @@ -188,13 +188,13 @@ RecordView::RecordView( this->toggle(); }; - signal_token_tick_second = time::signal_tick_second += [this]() { + signal_token_tick_second = rtc_time::signal_tick_second += [this]() { this->on_tick_second(); }; } RecordView::~RecordView() { - time::signal_tick_second -= signal_token_tick_second; + rtc_time::signal_tick_second -= signal_token_tick_second; } void RecordView::focus() { From f7bfde73b63c0bb17d7f64e0468bf279bbfd0776 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 21 Aug 2016 18:06:39 -0700 Subject: [PATCH 022/100] FatFs: Enable long file name support. Lots of re-plumbing to make this work, including a bunch of Unicode stuff now in the binary. Bloat City, I'm sure. TODO: FatFs using unsigned (uint16_t) for UTF16 representation is kinda inconvenient. Lots of reinterpret_cast<>(). --- firmware/application/ais_app.cpp | 2 +- firmware/application/ais_app.hpp | 2 +- firmware/application/analog_audio_app.hpp | 2 +- firmware/application/capture_app.hpp | 2 +- firmware/application/ert_app.cpp | 2 +- firmware/application/ert_app.hpp | 2 +- firmware/application/ffconf.h | 4 +-- firmware/application/file.cpp | 34 +++++++++---------- firmware/application/file.hpp | 21 +++++++----- firmware/application/log_file.hpp | 2 +- firmware/application/sd_card.cpp | 2 +- firmware/application/tpms_app.cpp | 2 +- firmware/application/tpms_app.hpp | 2 +- firmware/application/ui_navigation.cpp | 4 +-- firmware/application/ui_record_view.cpp | 24 ++++++++----- firmware/application/ui_record_view.hpp | 6 ++-- firmware/application/ui_sd_card_debug.cpp | 8 ++--- .../os/various/fatfs_bindings/fatfs.cmake | 1 + firmware/common/png_writer.cpp | 2 +- firmware/common/png_writer.hpp | 2 +- 20 files changed, 68 insertions(+), 58 deletions(-) diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index 189ecacc..1b04bc18 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -331,7 +331,7 @@ AISAppView::AISAppView(NavigationView&) { logger = std::make_unique(); if( logger ) { - logger->append("ais.txt"); + logger->append(u"ais.txt"); } } diff --git a/firmware/application/ais_app.hpp b/firmware/application/ais_app.hpp index b38a2146..10da06a2 100644 --- a/firmware/application/ais_app.hpp +++ b/firmware/application/ais_app.hpp @@ -95,7 +95,7 @@ using AISRecentEntries = RecentEntries; class AISLogger { public: - Optional append(const std::string& filename) { + Optional append(const std::filesystem::path& filename) { return log_file.append(filename); } diff --git a/firmware/application/analog_audio_app.hpp b/firmware/application/analog_audio_app.hpp index a91e8059..697a199a 100644 --- a/firmware/application/analog_audio_app.hpp +++ b/firmware/application/analog_audio_app.hpp @@ -143,7 +143,7 @@ private: RecordView record_view { { 0 * 8, 2 * 16, 30 * 8, 1 * 16 }, - "AUD_????", RecordView::FileType::WAV, 4096, 4 + u"AUD_????", RecordView::FileType::WAV, 4096, 4 }; spectrum::WaterfallWidget waterfall; diff --git a/firmware/application/capture_app.hpp b/firmware/application/capture_app.hpp index a7d04859..76d287f0 100644 --- a/firmware/application/capture_app.hpp +++ b/firmware/application/capture_app.hpp @@ -89,7 +89,7 @@ private: RecordView record_view { { 0 * 8, 1 * 16, 30 * 8, 1 * 16 }, - "BBD_????", RecordView::FileType::RawS16, 16384, 3 + u"BBD_????", RecordView::FileType::RawS16, 16384, 3 }; spectrum::WaterfallWidget waterfall; diff --git a/firmware/application/ert_app.cpp b/firmware/application/ert_app.cpp index 4ec70f93..5bed3782 100644 --- a/firmware/application/ert_app.cpp +++ b/firmware/application/ert_app.cpp @@ -146,7 +146,7 @@ ERTAppView::ERTAppView(NavigationView&) { logger = std::make_unique(); if( logger ) { - logger->append("ert.txt"); + logger->append(u"ert.txt"); } } diff --git a/firmware/application/ert_app.hpp b/firmware/application/ert_app.hpp index 1d901f45..cba9e74a 100644 --- a/firmware/application/ert_app.hpp +++ b/firmware/application/ert_app.hpp @@ -90,7 +90,7 @@ struct ERTRecentEntry { class ERTLogger { public: - Optional append(const std::string& filename) { + Optional append(const std::filesystem::path& filename) { return log_file.append(filename); } diff --git a/firmware/application/ffconf.h b/firmware/application/ffconf.h index a02d51b0..cfe0e890 100644 --- a/firmware/application/ffconf.h +++ b/firmware/application/ffconf.h @@ -101,7 +101,7 @@ */ -#define _USE_LFN 0 +#define _USE_LFN 2 #define _MAX_LFN 255 /* The _USE_LFN switches the support of long file name (LFN). / @@ -119,7 +119,7 @@ / ff_memfree(), must be added to the project. */ -#define _LFN_UNICODE 0 +#define _LFN_UNICODE 1 /* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) / To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. / This option also affects behavior of string I/O functions. */ diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index f4415452..4ceb51aa 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -30,8 +30,8 @@ static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected."); #define FR_BAD_SEEK (0x102) #define FR_UNEXPECTED (0x103) -Optional File::open_fatfs(const std::string& filename, BYTE mode) { - auto result = f_open(&f, filename.c_str(), mode); +Optional File::open_fatfs(const std::filesystem::path& filename, BYTE mode) { + auto result = f_open(&f, reinterpret_cast(filename.c_str()), mode); if( result == FR_OK ) { if( mode & FA_OPEN_ALWAYS ) { const auto result = f_lseek(&f, f_size(&f)); @@ -48,15 +48,15 @@ Optional File::open_fatfs(const std::string& filename, BYTE mode) { } } -Optional File::open(const std::string& filename) { +Optional File::open(const std::filesystem::path& filename) { return open_fatfs(filename, FA_READ); } -Optional File::append(const std::string& filename) { +Optional File::append(const std::filesystem::path& filename) { return open_fatfs(filename, FA_WRITE | FA_OPEN_ALWAYS); } -Optional File::create(const std::string& filename) { +Optional File::create(const std::filesystem::path& filename) { return open_fatfs(filename, FA_WRITE | FA_CREATE_ALWAYS); } @@ -124,9 +124,9 @@ Optional File::sync() { } } -static std::string find_last_file_matching_pattern(const std::string& pattern) { - std::string last_match; - for(const auto& entry : std::filesystem::directory_iterator("", pattern.c_str())) { +static std::filesystem::path find_last_file_matching_pattern(const std::filesystem::path& pattern) { + std::filesystem::path last_match; + for(const auto& entry : std::filesystem::directory_iterator(u"", pattern.c_str())) { if( std::filesystem::is_regular_file(entry.status()) ) { const auto match = entry.path(); if( match > last_match ) { @@ -137,13 +137,13 @@ static std::string find_last_file_matching_pattern(const std::string& pattern) { return last_match; } -static std::string remove_filename_extension(const std::string& filename) { +static std::filesystem::path remove_filename_extension(const std::filesystem::path& filename) { const auto extension_index = filename.find_last_of('.'); return filename.substr(0, extension_index); } -static std::string increment_filename_stem_ordinal(const std::string& filename_stem) { - std::string result { filename_stem }; +static std::filesystem::path increment_filename_stem_ordinal(const std::filesystem::path& filename_stem) { + std::filesystem::path result { filename_stem }; auto it = result.rbegin(); @@ -165,8 +165,8 @@ static std::string increment_filename_stem_ordinal(const std::string& filename_s return result; } -std::string next_filename_stem_matching_pattern(const std::string& filename_stem_pattern) { - const auto filename = find_last_file_matching_pattern(filename_stem_pattern + ".*"); +std::filesystem::path next_filename_stem_matching_pattern(const std::filesystem::path& filename_stem_pattern) { + const auto filename = find_last_file_matching_pattern(filename_stem_pattern + u".*"); auto filename_stem = remove_filename_extension(filename); if( filename_stem.empty() ) { filename_stem = filename_stem_pattern; @@ -211,11 +211,11 @@ std::string filesystem_error::what() const { } directory_iterator::directory_iterator( - const char* path, - const char* wild + const std::filesystem::path::value_type* path, + const std::filesystem::path::value_type* wild ) { impl = std::make_shared(); - const auto result = f_findfirst(&impl->dir, &impl->filinfo, path, wild); + const auto result = f_findfirst(&impl->dir, &impl->filinfo, reinterpret_cast(path), reinterpret_cast(wild)); if( result != FR_OK ) { impl.reset(); // TODO: Throw exception if/when I enable exceptions... @@ -237,7 +237,7 @@ bool is_regular_file(const file_status s) { space_info space(const path& p) { DWORD free_clusters { 0 }; FATFS* fs; - if( f_getfree(p.c_str(), &free_clusters, &fs) == FR_OK ) { + if( f_getfree(reinterpret_cast(p.c_str()), &free_clusters, &fs) == FR_OK ) { #if _MAX_SS != _MIN_SS static_assert(false, "FatFs not configured for fixed sector size"); #else diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index f3739317..62813e21 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -33,8 +33,6 @@ #include #include -std::string next_filename_stem_matching_pattern(const std::string& filename_stem_pattern); - namespace std { namespace filesystem { @@ -66,9 +64,12 @@ private: uint32_t err; }; -using path = std::string; +using path = std::u16string; using file_status = BYTE; +static_assert(sizeof(path::value_type) == 2, "sizeof(std::filesystem::path::value_type) != 2"); +static_assert(sizeof(path::value_type) == sizeof(TCHAR), "FatFs TCHAR size != std::filesystem::path::value_type"); + struct space_info { static_assert(sizeof(std::uintmax_t) >= 8, "std::uintmax_t too small ((fname); }; }; class directory_iterator { @@ -107,7 +108,7 @@ public: using iterator_category = std::input_iterator_tag; directory_iterator() noexcept { }; - directory_iterator(const char* path, const char* wild); + directory_iterator(const std::filesystem::path::value_type* path, const std::filesystem::path::value_type* wild); ~directory_iterator() { } @@ -131,6 +132,8 @@ space_info space(const path& p); } /* namespace filesystem */ } /* namespace std */ +std::filesystem::path next_filename_stem_matching_pattern(const std::filesystem::path& filename_stem_pattern); + class File { public: using Error = std::filesystem::filesystem_error; @@ -193,9 +196,9 @@ public: File& operator=(const File&) = delete; // TODO: Return Result<>. - Optional open(const std::string& filename); - Optional append(const std::string& filename); - Optional create(const std::string& filename); + Optional open(const std::filesystem::path& filename); + Optional append(const std::filesystem::path& filename); + Optional create(const std::filesystem::path& filename); Result read(void* const data, const size_t bytes_to_read); Result write(const void* const data, const size_t bytes_to_write); @@ -215,7 +218,7 @@ public: private: FIL f; - Optional open_fatfs(const std::string& filename, BYTE mode); + Optional open_fatfs(const std::filesystem::path& filename, BYTE mode); }; #endif/*__FILE_H__*/ diff --git a/firmware/application/log_file.hpp b/firmware/application/log_file.hpp index 1e1f667a..5f222a79 100644 --- a/firmware/application/log_file.hpp +++ b/firmware/application/log_file.hpp @@ -31,7 +31,7 @@ using namespace lpc43xx; class LogFile { public: - Optional append(const std::string& filename) { + Optional append(const std::filesystem::path& filename) { return file.append(filename); } diff --git a/firmware/application/sd_card.cpp b/firmware/application/sd_card.cpp index ed2a90b2..b7846152 100644 --- a/firmware/application/sd_card.cpp +++ b/firmware/application/sd_card.cpp @@ -36,7 +36,7 @@ Status status_ { Status::NotPresent }; FATFS fs; FRESULT mount() { - return f_mount(&fs, "", 0); + return f_mount(&fs, reinterpret_cast(_T("")), 0); } } /* namespace */ diff --git a/firmware/application/tpms_app.cpp b/firmware/application/tpms_app.cpp index cc55f276..2a13e2f2 100644 --- a/firmware/application/tpms_app.cpp +++ b/firmware/application/tpms_app.cpp @@ -193,7 +193,7 @@ TPMSAppView::TPMSAppView(NavigationView&) { logger = std::make_unique(); if( logger ) { - logger->append("tpms.txt"); + logger->append(u"tpms.txt"); } } diff --git a/firmware/application/tpms_app.hpp b/firmware/application/tpms_app.hpp index fc9e0c35..98715219 100644 --- a/firmware/application/tpms_app.hpp +++ b/firmware/application/tpms_app.hpp @@ -76,7 +76,7 @@ using TPMSRecentEntries = RecentEntries; class TPMSLogger { public: - Optional append(const std::string& filename) { + Optional append(const std::filesystem::path& filename) { return log_file.append(filename); } diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 4238af12..ba8e783c 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -81,13 +81,13 @@ void SystemStatusView::set_title(const std::string new_value) { } void SystemStatusView::on_camera() { - const auto filename_stem = next_filename_stem_matching_pattern("SCR_????"); + const auto filename_stem = next_filename_stem_matching_pattern(u"SCR_????"); if( filename_stem.empty() ) { return; } PNGWriter png; - auto create_error = png.create(filename_stem + ".PNG"); + auto create_error = png.create(filename_stem + u".PNG"); if( create_error.is_valid() ) { return; } diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index bf7ec391..42e0d374 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -32,6 +32,9 @@ using namespace portapack; #include +#include +#include + class FileWriter : public Writer { public: FileWriter() = default; @@ -41,7 +44,7 @@ public: FileWriter(FileWriter&& file) = delete; FileWriter& operator=(FileWriter&&) = delete; - Optional create(const std::string& filename) { + Optional create(const std::filesystem::path& filename) { return file.create(filename); } @@ -79,7 +82,7 @@ public: } Optional create( - const std::string& filename + const std::filesystem::path& filename ) { const auto create_error = FileWriter::create(filename); if( create_error.is_valid() ) { @@ -164,7 +167,7 @@ namespace ui { RecordView::RecordView( const Rect parent_rect, - std::string filename_stem_pattern, + std::filesystem::path filename_stem_pattern, const FileType file_type, const size_t write_size, const size_t buffer_count @@ -251,7 +254,7 @@ void RecordView::start() { sampling_rate ); auto create_error = p->create( - filename_stem + ".WAV" + filename_stem + u".WAV" ); if( create_error.is_valid() ) { handle_error(create_error.value()); @@ -263,7 +266,7 @@ void RecordView::start() { case FileType::RawS16: { - const auto metadata_file_error = write_metadata_file(filename_stem + ".TXT"); + const auto metadata_file_error = write_metadata_file(filename_stem + u".TXT"); if( metadata_file_error.is_valid() ) { handle_error(metadata_file_error.value()); return; @@ -271,7 +274,7 @@ void RecordView::start() { auto p = std::make_unique(); auto create_error = p->create( - filename_stem + ".C16" + filename_stem + u".C16" ); if( create_error.is_valid() ) { handle_error(create_error.value()); @@ -286,7 +289,10 @@ void RecordView::start() { }; if( writer ) { - text_record_filename.set(filename_stem); + std::wstring_convert, std::filesystem::path::value_type> conv; + const auto filename_stem_s = conv.to_bytes(filename_stem); + + text_record_filename.set(filename_stem_s); button_record.set_bitmap(&bitmap_stop); capture_thread = std::make_unique( std::move(writer), @@ -314,7 +320,7 @@ void RecordView::stop() { update_status_display(); } -Optional RecordView::write_metadata_file(const std::string& filename) { +Optional RecordView::write_metadata_file(const std::filesystem::path& filename) { File file; const auto create_error = file.create(filename); if( create_error.is_valid() ) { @@ -344,7 +350,7 @@ void RecordView::update_status_display() { } if( sampling_rate ) { - const auto space_info = std::filesystem::space(""); + const auto space_info = std::filesystem::space(u""); const uint32_t bytes_per_second = file_type == FileType::WAV ? (sampling_rate * 2) : (sampling_rate * 4); const uint32_t available_seconds = space_info.free / bytes_per_second; const uint32_t seconds = available_seconds % 60; diff --git a/firmware/application/ui_record_view.hpp b/firmware/application/ui_record_view.hpp index 023fb0d5..203d334c 100644 --- a/firmware/application/ui_record_view.hpp +++ b/firmware/application/ui_record_view.hpp @@ -46,7 +46,7 @@ public: RecordView( const Rect parent_rect, - std::string filename_stem_pattern, + std::filesystem::path filename_stem_pattern, FileType file_type, const size_t write_size, const size_t buffer_count @@ -64,7 +64,7 @@ public: private: void toggle(); - Optional write_metadata_file(const std::string& filename); + Optional write_metadata_file(const std::filesystem::path& filename); void on_tick_second(); void update_status_display(); @@ -72,7 +72,7 @@ private: void handle_capture_thread_done(const File::Error error); void handle_error(const File::Error error); - const std::string filename_stem_pattern; + const std::filesystem::path filename_stem_pattern; const FileType file_type; const size_t write_size; const size_t buffer_count; diff --git a/firmware/application/ui_sd_card_debug.cpp b/firmware/application/ui_sd_card_debug.cpp index eac201ea..dd92f5c6 100644 --- a/firmware/application/ui_sd_card_debug.cpp +++ b/firmware/application/ui_sd_card_debug.cpp @@ -95,7 +95,7 @@ private: } Result run() { - const std::string filename { "_PPTEST_.DAT" }; + const std::filesystem::path filename { u"_PPTEST_.DAT" }; const auto write_result = write(filename); if( write_result != Result::OK ) { @@ -115,7 +115,7 @@ private: return read_result; } - f_unlink(filename.c_str()); + f_unlink(reinterpret_cast(filename.c_str())); if( _stats.read_bytes < bytes_to_read ) { return Result::FailReadIncomplete; @@ -128,7 +128,7 @@ private: return Result::OK; } - Result write(const std::string& filename) { + Result write(const std::filesystem::path& filename) { const auto buffer = std::make_unique>(); if( !buffer ) { return Result::FailHeap; @@ -175,7 +175,7 @@ private: return Result::OK; } - Result read(const std::string& filename) { + Result read(const std::filesystem::path& filename) { const auto buffer = std::make_unique>(); if( !buffer ) { return Result::FailHeap; diff --git a/firmware/chibios-portapack/os/various/fatfs_bindings/fatfs.cmake b/firmware/chibios-portapack/os/various/fatfs_bindings/fatfs.cmake index 389f5d14..1eff8f31 100644 --- a/firmware/chibios-portapack/os/various/fatfs_bindings/fatfs.cmake +++ b/firmware/chibios-portapack/os/various/fatfs_bindings/fatfs.cmake @@ -3,6 +3,7 @@ set(FATFSSRC ${CHIBIOS_PORTAPACK}/os/various/fatfs_bindings/fatfs_diskio.c ${CHIBIOS_PORTAPACK}/os/various/fatfs_bindings/fatfs_syscall.c ${CHIBIOS_PORTAPACK}/ext/fatfs/src/ff.c + ${CHIBIOS_PORTAPACK}/ext/fatfs/src/option/unicode.c ) set(FATFSINC diff --git a/firmware/common/png_writer.cpp b/firmware/common/png_writer.cpp index e1667f14..b714e322 100644 --- a/firmware/common/png_writer.cpp +++ b/firmware/common/png_writer.cpp @@ -50,7 +50,7 @@ static constexpr std::array png_iend { { } }; Optional PNGWriter::create( - const std::string& filename + const std::filesystem::path& filename ) { const auto create_error = file.create(filename); if( create_error.is_valid() ) { diff --git a/firmware/common/png_writer.hpp b/firmware/common/png_writer.hpp index 37bede47..020a118a 100644 --- a/firmware/common/png_writer.hpp +++ b/firmware/common/png_writer.hpp @@ -35,7 +35,7 @@ class PNGWriter { public: ~PNGWriter(); - Optional create(const std::string& filename); + Optional create(const std::filesystem::path& filename); void write_scanline(const std::array& scanline); From ed791ac5bdc9ae54676cf8197ef56e8dcc1692d8 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 21 Aug 2016 22:15:19 -0700 Subject: [PATCH 023/100] File: Widen size/offset types for 64-bit filesystems. --- firmware/application/capture_thread.hpp | 2 +- firmware/application/file.cpp | 10 +++++----- firmware/application/file.hpp | 10 ++++++---- firmware/application/ui_record_view.cpp | 2 +- firmware/application/ui_sd_card_debug.cpp | 12 ++++++------ 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/firmware/application/capture_thread.hpp b/firmware/application/capture_thread.hpp index 849a3131..6e2bbaa8 100644 --- a/firmware/application/capture_thread.hpp +++ b/firmware/application/capture_thread.hpp @@ -35,7 +35,7 @@ class Writer { public: - virtual File::Result write(const void* const buffer, const size_t bytes) = 0; + virtual File::Result write(const void* const buffer, const File::Size bytes) = 0; virtual ~Writer() = default; }; diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index 4ceb51aa..8c45c8b3 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -64,7 +64,7 @@ File::~File() { f_close(&f); } -File::Result File::read(void* const data, const size_t bytes_to_read) { +File::Result File::read(void* const data, const Size bytes_to_read) { UINT bytes_read = 0; const auto result = f_read(&f, data, bytes_to_read, &bytes_read); if( result == FR_OK ) { @@ -74,12 +74,12 @@ File::Result File::read(void* const data, const size_t bytes_to_read) { } } -File::Result File::write(const void* const data, const size_t bytes_to_write) { +File::Result File::write(const void* const data, const Size bytes_to_write) { UINT bytes_written = 0; const auto result = f_write(&f, data, bytes_to_write, &bytes_written); if( result == FR_OK ) { if( bytes_to_write == bytes_written ) { - return { static_cast(bytes_written) }; + return { static_cast(bytes_written) }; } else { return Error { FR_DISK_FULL }; } @@ -88,7 +88,7 @@ File::Result File::write(const void* const data, const size_t bytes_to_w } } -File::Result File::seek(const uint64_t new_position) { +File::Result File::seek(const Offset new_position) { /* NOTE: Returns *old* position, not new position */ const auto old_position = f_tell(&f); const auto result = f_lseek(&f, new_position); @@ -98,7 +98,7 @@ File::Result File::seek(const uint64_t new_position) { if( f_tell(&f) != new_position ) { return { static_cast(FR_BAD_SEEK) }; } - return { static_cast(old_position) }; + return { static_cast(old_position) }; } Optional File::write_line(const std::string& s) { diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index 62813e21..64691703 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -136,6 +136,8 @@ std::filesystem::path next_filename_stem_matching_pattern(const std::filesystem: class File { public: + using Size = uint64_t; + using Offset = uint64_t; using Error = std::filesystem::filesystem_error; template @@ -200,13 +202,13 @@ public: Optional append(const std::filesystem::path& filename); Optional create(const std::filesystem::path& filename); - Result read(void* const data, const size_t bytes_to_read); - Result write(const void* const data, const size_t bytes_to_write); + Result read(void* const data, const Size bytes_to_read); + Result write(const void* const data, const Size bytes_to_write); - Result seek(const uint64_t new_position); + Result seek(const uint64_t Offset); template - Result write(const std::array& data) { + Result write(const std::array& data) { return write(data.data(), N); } diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index 42e0d374..71211dfc 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -48,7 +48,7 @@ public: return file.create(filename); } - File::Result write(const void* const buffer, const size_t bytes) override { + File::Result write(const void* const buffer, const File::Size bytes) override { auto write_result = file.write(buffer, bytes) ; if( write_result.is_ok() ) { bytes_written += write_result.value(); diff --git a/firmware/application/ui_sd_card_debug.cpp b/firmware/application/ui_sd_card_debug.cpp index dd92f5c6..856f3e63 100644 --- a/firmware/application/ui_sd_card_debug.cpp +++ b/firmware/application/ui_sd_card_debug.cpp @@ -51,13 +51,13 @@ public: halrtcnt_t write_duration_min { 0 }; halrtcnt_t write_duration_max { 0 }; halrtcnt_t write_test_duration { 0 }; - size_t write_bytes { 0 }; + File::Size write_bytes { 0 }; size_t write_count { 0 }; halrtcnt_t read_duration_min { 0 }; halrtcnt_t read_duration_max { 0 }; halrtcnt_t read_test_duration { 0 }; - size_t read_bytes { 0 }; + File::Size read_bytes { 0 }; size_t read_count { 0 }; }; @@ -80,9 +80,9 @@ public: } private: - static constexpr size_t write_size = 16384; - static constexpr size_t bytes_to_write = 16 * 1024 * 1024; - static constexpr size_t bytes_to_read = bytes_to_write; + static constexpr File::Size write_size = 16384; + static constexpr File::Size bytes_to_write = 16 * 1024 * 1024; + static constexpr File::Size bytes_to_read = bytes_to_write; static Thread* thread; volatile Result _result { Result::Incomplete }; @@ -365,7 +365,7 @@ static std::string format_ticks_as_ms(const halrtcnt_t value) { return format_3dot3_string(us); } -static std::string format_bytes_per_ticks_as_mib(const size_t bytes, const halrtcnt_t ticks) { +static std::string format_bytes_per_ticks_as_mib(const File::Size bytes, const halrtcnt_t ticks) { const uint32_t bps = uint64_t(bytes) * halGetCounterFrequency() / ticks; const uint32_t kbps = bps / 1000U; return format_3dot3_string(kbps); From 81517b3f4d095516e564debc85ab8935d348af8e Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 21 Aug 2016 22:16:08 -0700 Subject: [PATCH 024/100] SD debug: Enlarge stack for long filenames, etc. --- firmware/application/ui_sd_card_debug.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/application/ui_sd_card_debug.cpp b/firmware/application/ui_sd_card_debug.cpp index 856f3e63..22db218c 100644 --- a/firmware/application/ui_sd_card_debug.cpp +++ b/firmware/application/ui_sd_card_debug.cpp @@ -63,7 +63,7 @@ public: SDCardTestThread( ) { - thread = chThdCreateFromHeap(NULL, 2048, NORMALPRIO + 10, SDCardTestThread::static_fn, this); + thread = chThdCreateFromHeap(NULL, 3072, NORMALPRIO + 10, SDCardTestThread::static_fn, this); } Result result() const { From 60cc9b7faa4b435c78b2dc6f4abddc7c81c034e8 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 21 Aug 2016 23:02:00 -0700 Subject: [PATCH 025/100] FatFs: Apply patch ff12a_p5.diff. Fixed one chunk of this patch in an earlier commit. --- firmware/chibios-portapack/ext/fatfs/src/ff.c | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/firmware/chibios-portapack/ext/fatfs/src/ff.c b/firmware/chibios-portapack/ext/fatfs/src/ff.c index ca71f697..f0b927d5 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ff.c +++ b/firmware/chibios-portapack/ext/fatfs/src/ff.c @@ -5303,7 +5303,7 @@ FRESULT f_mkfs ( #if _FS_EXFAT if (fmt == FS_EXFAT) { /* Create an exFAT volume */ - DWORD sum, szb_bit, szb_case; + DWORD szb_bit, szb_case, sum, nb, cl; WCHAR ch, si; UINT j, st; BYTE b; @@ -5368,32 +5368,37 @@ FRESULT f_mkfs ( tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case clusters */ /* Initialize the allocation bitmap */ - mem_set(buf, 0, szb_buf); /* Set in-use flags of bitmap, up-case and root dir */ - for (i = 0, n = tbl[0] + tbl[1] + tbl[2]; n >= 8; buf[i++] = 0xFF, n -= 8) ; - for (b = 1; n; buf[i] |= b, b <<= 1, n--) ; sect = b_data; n = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of the sectors */ - do { /* Fill allocation bitmap sectors */ - ns = (n > sz_buf) ? sz_buf : n; + nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */ + do { + mem_set(buf, 0, szb_buf); + for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ; + for (b = 1; nb && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ; + ns = (n > sz_buf) ? sz_buf : n; /* Write the buffered data */ if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR; - sect += ns; - mem_set(buf, 0, ss); - } while (n -= ns); + sect += ns; n -= ns; + } while (n); /* Initialize the FAT */ - st_qword(buf, 0xFFFFFFFFFFFFFFF8); /* Entry 0 and 1 */ - for (j = 0, i = 2; j < 3; j++) { /* Set entries of bitmap, up-case and root dir */ - for (n = tbl[j]; n; n--) { - st_dword(buf + i * 4, (n >= 2) ? i + 1 : 0xFFFFFFFF); - i++; - } - } sect = b_fat; n = sz_fat; /* Start of FAT and number of the sectors */ - do { /* Fill FAT sectors */ - ns = (n > sz_buf) ? sz_buf : n; + j = nb = cl = 0; + do { + mem_set(buf, 0, szb_buf); i = 0; + if (cl == 0) { /* Set entry 0 and 1 */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; + } + do { /* Create chains of bitmap, up-case and root dir */ + while (nb && i < szb_buf) { /* Create a chain */ + st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); + i += 4; cl++; nb--; + } + if (!nb && j < 3) nb = tbl[j++]; /* Next chain */ + } while (nb && i < szb_buf); + ns = (n > sz_buf) ? sz_buf : n; /* Write the buffered data */ if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR; - sect += ns; - mem_set(buf, 0, ss); - } while (n -= ns); + sect += ns; n -= ns; + } while (n); /* Initialize the root directory */ mem_set(buf, 0, ss); From 11a5aa9766f2b6c592eefa7a09475bd598fbb0b1 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 21 Aug 2016 23:03:08 -0700 Subject: [PATCH 026/100] FatFs: Apply patch ff12a_p6.diff. --- firmware/chibios-portapack/ext/fatfs/src/ff.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/firmware/chibios-portapack/ext/fatfs/src/ff.c b/firmware/chibios-portapack/ext/fatfs/src/ff.c index f0b927d5..04e197cf 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ff.c +++ b/firmware/chibios-portapack/ext/fatfs/src/ff.c @@ -4038,11 +4038,16 @@ FRESULT f_lseek ( } if (clst != 0) { while (ofs > bcs) { /* Cluster following loop */ + ofs -= bcs; fp->fptr += bcs; #if !_FS_READONLY if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + if (_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } clst = create_chain(&fp->obj, clst); /* Force stretch if in write mode */ if (clst == 0) { /* When disk gets full, clip file size */ - ofs = bcs; break; + ofs = 0; break; } } else #endif @@ -4050,8 +4055,6 @@ FRESULT f_lseek ( if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); fp->clust = clst; - fp->fptr += bcs; - ofs -= bcs; } fp->fptr += ofs; if (ofs % SS(fs)) { From e2fe4b65d9c74c5daa5de807f781b31c331a5af9 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 23 Aug 2016 10:30:05 -0700 Subject: [PATCH 027/100] CPLD: Set DECIM1 as input to CPLD. How did DECIM work before?! Now, decimate is no longer a feature, so this doesn't really matter. But tidying it up anyway. --- firmware/common/baseband_sgpio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/common/baseband_sgpio.cpp b/firmware/common/baseband_sgpio.cpp index a574e81d..1cc0d143 100644 --- a/firmware/common/baseband_sgpio.cpp +++ b/firmware/common/baseband_sgpio.cpp @@ -190,7 +190,7 @@ constexpr uint32_t gpio_outreg(const Direction direction) { constexpr uint32_t gpio_oenreg(const Direction direction) { return (0U << PIN_DECIM2) - | (1U << PIN_DECIM1) + | (0U << PIN_DECIM1) | (0U << PIN_DECIM0) | (0U << PIN_INVERT) | (1U << PIN_DIRECTION) From f0c4b0fc9879c893c2ef2ed3a641b24b5d81e687 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 29 Aug 2016 20:26:39 -0700 Subject: [PATCH 028/100] AIS: Doesn't use RRC filter -- use rect instead. --- firmware/baseband/proc_ais.hpp | 2 +- firmware/common/ais_baseband.hpp | 22 +++++++--------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/firmware/baseband/proc_ais.hpp b/firmware/baseband/proc_ais.hpp index eff71b4a..97e813ff 100644 --- a/firmware/baseband/proc_ais.hpp +++ b/firmware/baseband/proc_ais.hpp @@ -62,7 +62,7 @@ private: dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0; dsp::decimate::FIRC16xR16x32Decim8 decim_1; - dsp::matched_filter::MatchedFilter mf { baseband::ais::rrc_taps_38k4_4t_p, 2 }; + dsp::matched_filter::MatchedFilter mf { baseband::ais::square_taps_38k4_1t_p, 2 }; clock_recovery::ClockRecovery clock_recovery { 19200, 9600, { 0.0555f }, diff --git a/firmware/common/ais_baseband.hpp b/firmware/common/ais_baseband.hpp index 1d7a2aa6..f8bbc0d8 100644 --- a/firmware/common/ais_baseband.hpp +++ b/firmware/common/ais_baseband.hpp @@ -32,21 +32,13 @@ namespace baseband { namespace ais { -// RRC length should be about 4 x the symbol length (4T) to do a good job of -// cleaning up ISI. - -// Translate+RRC filter -// sample=38.4k, deviation=2400, b=0.5, symbol=9600 -// Length: 16 taps, 4 symbols, 1 cycles of sinusoid -constexpr std::array, 16> rrc_taps_38k4_4t_p { { - { 1.0619794019e-02f, 0.0000000000e+00f }, { 3.5758705854e-03f, 1.4811740938e-03f }, - { -1.3274742629e-02f, -1.3274742629e-02f }, { -1.5018657262e-02f, -3.6258246051e-02f }, - { 0.0000000000e+00f, -2.6549484581e-02f }, { -1.5018657262e-02f, 3.6258246051e-02f }, - { -1.0237997393e-01f, 1.0237997393e-01f }, { -2.2527985355e-01f, 9.3313970669e-02f }, - { -2.8440842032e-01f, 0.0000000000e+00f }, { -2.2527985355e-01f, -9.3313970669e-02f }, - { -1.0237997393e-01f, -1.0237997393e-01f }, { -1.5018657262e-02f, -3.6258246051e-02f }, - { 0.0000000000e+00f, 2.6549484581e-02f }, { -1.5018657262e-02f, 3.6258246051e-02f }, - { -1.3274742629e-02f, 1.3274742629e-02f }, { 3.5758705854e-03f, -1.4811740938e-03f }, +// Translate+Rectangular window filter +// sample=38.4k, deviation=2400, symbol=9600 +// Length: 4 taps, 1 symbol, 1/4 cycle of sinusoid +// Gain: 1.0 (sinusoid / len(taps)) +constexpr std::array, 4> square_taps_38k4_1t_p { { + { 0.25000000f, 0.00000000f }, { 0.23096988f, 0.09567086f }, + { 0.17677670f, 0.17677670f }, { 0.09567086f, 0.23096988f }, } }; } /* namespace ais */ From 42a07bb10ce360b57ff3737fb6602fef77abaf94 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 30 Aug 2016 21:26:55 -0700 Subject: [PATCH 029/100] Remove repeated code in RF path Config. --- firmware/application/rf_path.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/application/rf_path.cpp b/firmware/application/rf_path.cpp index 4680ae4c..3f646954 100644 --- a/firmware/application/rf_path.cpp +++ b/firmware/application/rf_path.cpp @@ -92,9 +92,9 @@ struct Config { lp(band == Band::Low), amp_bypass(!amplify), tx_amp((direction == Direction::Transmit) && amplify), - not_tx_amp(!((direction == Direction::Transmit) && amplify)), + not_tx_amp(!tx_amp), rx_amp((direction == Direction::Receive) && amplify), - not_rx_amp(!((direction == Direction::Receive) && amplify)) + not_rx_amp(!rx_amp) { } From 337d5ebaea653579e7b7ea3f09f78ab295c08b99 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 30 Aug 2016 21:29:23 -0700 Subject: [PATCH 030/100] Add PortaPack-customized HackRF CPLD bitstream. --- firmware/CMakeLists.txt | 2 +- firmware/hackrf_cpld_portapack.svf | 1122 ++++++++++++++++++++++++++++ 2 files changed, 1123 insertions(+), 1 deletion(-) create mode 100755 firmware/hackrf_cpld_portapack.svf diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 9a7bb2d1..124c420e 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -27,7 +27,7 @@ set(CHIBIOS_PORTAPACK ${PROJECT_SOURCE_DIR}/chibios-portapack) set(HACKRF_FIRMWARE_FILENAME hackrf_one_usb_ram.dfu) set(HACKRF_FIRMWARE_IMAGE ${PROJECT_SOURCE_DIR}/${HACKRF_FIRMWARE_FILENAME}) -set(HACKRF_CPLD_SVF_FILENAME hackrf_cpld_default.svf) +set(HACKRF_CPLD_SVF_FILENAME hackrf_cpld_portapack.svf) set(HACKRF_CPLD_SVF_PATH ${PROJECT_SOURCE_DIR}/${HACKRF_CPLD_SVF_FILENAME}) set(EXTRACT_CPLD_DATA ${PROJECT_SOURCE_DIR}/tools/extract_cpld_data.py) diff --git a/firmware/hackrf_cpld_portapack.svf b/firmware/hackrf_cpld_portapack.svf new file mode 100755 index 00000000..424fb659 --- /dev/null +++ b/firmware/hackrf_cpld_portapack.svf @@ -0,0 +1,1122 @@ +// Created using Xilinx Cse Software [ISE - 14.7] +// Date: Tue Aug 23 23:01:43 2016 + +TRST OFF; +ENDIR IDLE; +ENDDR IDLE; +STATE RESET; +STATE IDLE; +FREQUENCY 1E6 HZ; +TIR 0 ; +HIR 0 ; +TDR 0 ; +HDR 0 ; +TIR 0 ; +HIR 0 ; +HDR 0 ; +TDR 0 ; +//Loading device with 'idcode' instruction. +SIR 8 TDI (01) SMASK (ff) ; +SDR 32 TDI (00000000) SMASK (ffffffff) TDO (f6e5f093) MASK (0fff8fff) ; +//Check for Read/Write Protect. +SIR 8 TDI (ff) TDO (01) MASK (03) ; +//Boundary Scan Chain Contents +//Position 1: xc2c64a +TIR 0 ; +HIR 0 ; +TDR 0 ; +HDR 0 ; +TIR 0 ; +HIR 0 ; +TDR 0 ; +HDR 0 ; +TIR 0 ; +HIR 0 ; +HDR 0 ; +TDR 0 ; +//Loading device with 'idcode' instruction. +SIR 8 TDI (01) ; +SDR 32 TDI (00000000) TDO (f6e5f093) ; +//Check for Read/Write Protect. +SIR 8 TDI (ff) TDO (01) MASK (03) ; +//Loading device with 'bypass' instruction. +SIR 8 TDI (ff) ; +//Loading device with 'enable' instruction. +SIR 8 TDI (e8) ; +//Loading device with 'enable' instruction. +SIR 8 TDI (e8) ; +// Loading device with a 'erase' instruction. +ENDIR IRPAUSE; +SIR 8 TDI (ed) SMASK (ff) ; +ENDIR IDLE; +STATE IREXIT2 IRUPDATE DRSELECT DRCAPTURE DREXIT1 DRPAUSE; +RUNTEST DRPAUSE 20 TCK; +STATE IDLE; +RUNTEST IDLE 100000 TCK; +STATE DRPAUSE; +RUNTEST DRPAUSE 5000 TCK; +ENDIR IRPAUSE; +SIR 8 TDI (f0) SMASK (ff) ; +STATE IDLE; +RUNTEST IDLE 20 TCK; +ENDIR IRPAUSE; +SIR 8 TDI (f0) SMASK (ff) ; +STATE IREXIT2 IRUPDATE DRSELECT DRCAPTURE DREXIT1 DRUPDATE IDLE; +RUNTEST 800 TCK; +ENDIR IDLE; +//Loading device with 'conld' instruction. +SIR 8 TDI (c0) ; +RUNTEST IDLE 100 TCK; +//Loading device with 'enable' instruction. +SIR 8 TDI (e8) ; +// Programming. +// Loading device with a 'program' instruction. +ENDIR IRPAUSE; +SIR 8 TDI (ea) ; +SDR 281 TDI (0003c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) SMASK (01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +ENDIR IDLE; +RUNTEST 10000 TCK; +SDR 281 TDI (0103f9ffffffffffffffffffffffffffffffff777fffffffffffffffffffffffffdffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0180f97fffffffffffffffffffffffbfffe9de7fffffffffffffffffeffffffffffbfe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0083c1fffffffffffffffffffffffffffbf99e7ffffffffffbffffffffffffffffffde0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00c3f9fffffffffffffffffffffffffffbf99e7fffffffffffffbfffffffffffffff7e7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01c0f93ffffffffffffffffffffffffffff99e7fffffefbefbffffffefbeffffeaab2a7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0143c1bffffffffffffffffffffdfffffffffeeefffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0043f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0060f9fffffffffffffffffffffbfffffff99e7fffffffffffffffffffffffffeaabfc7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0163c1fffffffffffffffffffffffffffbf99e7ffffffffeffffffffffffffffefffde4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01e3f9fffffffffffffffffffffffffffbf87e7fffffffbffffffffffffffffffeffde7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00e0f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00a3c1fffffffffffffffffffffffffefffbbefffffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01a3f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0120f9fffffffffffffffffffffffffffffd7f7ffffffffffffffffffffefffffffffe83) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0023c1fffffffffffffffffffffffffffff5ff7fffffffffffffffffffffffffeaabf64f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0033f9fffffffffffffffffffffffffffbf87e7fffffeffffffffffffffffffffbffde7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0130f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01b3c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00b3f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00f0f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01f3c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffabfe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0173f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9dfe1d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0070f9ffffffffffffffffffffffffffffffe607fffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0053c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff544f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0153f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff07ffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01d0f8bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdffff03) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00d3c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0093f9fffffffffffffffffffffffffffffddf7fffffffffffffffffffbfffffffeffe7d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0190f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0113c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0013f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0018f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83) ; +RUNTEST 10000 TCK; +SDR 281 TDI (011bc1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (019b99fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (009afdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00dbc1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01db99fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (015af9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83) ; +RUNTEST 10000 TCK; +SDR 281 TDI (005bc9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (007bd9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (017afdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01fbc9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00fbd9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00bafdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01bbc1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (013b99fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (003afdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03) ; +RUNTEST 10000 TCK; +SDR 281 TDI (002bc8eabfffffffffffffeffffffffffffffefefbfffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0128feeabf7fffffeffffffffffffffffffffefefbfffffffffffffffffffffffffffe1d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01aa00eab7ffff7ffffffffffffffffffffffefefbfffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00abc1bff77ffffffffffffffffffffffffffeeefffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00ebf8ebffffffffffffffffffffffbffffffefeeffffffffffffffffffffffffffffe69) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01e8f9fffbfffffffffffffffffffffffffbbefffffffffffffffffffffffffffffffe7d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (016bc9ffffbffffffffffffffffffffffffafefffffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0068e1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe69) ; +RUNTEST 10000 TCK; +SDR 281 TDI (004ac5fffffffffffffefffffffffffffffffeeefffffffffffffffffffffffffffffe7d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (014bc0fffffffffffffffffffffffffffbfffefeeffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01cbf9fffff7fffffffffffffffffffffffffeeefffffffffffffffffffffffffffffe69) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00c8f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (008bc8eaffffffffffffffffbffffffffffffefeeffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0188e0effffffffffffffffffffffffefffffefeeffffffffffffffffffffffffffffe69) ; +RUNTEST 10000 TCK; +SDR 281 TDI (010ac3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (000bc1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (000ff9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe69) ; +RUNTEST 10000 TCK; +SDR 281 TDI (010cf9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (018fc9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (008ce1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe69) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00cec5ffffbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01cfc1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (014ee1ffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe69) ; +RUNTEST 10000 TCK; +SDR 281 TDI (004cf8bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) ; +RUNTEST 10000 TCK; +SDR 281 TDI (006fc9dfcefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (016ce1fffffffffffffffffffffffffffffff9fffffffffffffffffffffffffffffffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01eec3df7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00efc993fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00ace1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01aec5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (012fc1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (002ee1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (003cf9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (013fc1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01bee1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00bcf9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00ffc1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01fff9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (017cf9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (007fc9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (005ce1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (015ec5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (01dfc9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (00dce1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (009ec5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (019fc9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (011ce1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) ; +RUNTEST 10000 TCK; +SDR 281 TDI (001ec5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 10000 TCK; +SDR 281 TDI (0117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 10000 TCK; +SIR 8 TDI (f0) SMASK (ff) ; +STATE IDLE; +RUNTEST IDLE 20 TCK; +ENDIR IRPAUSE; +SIR 8 TDI (f0) SMASK (ff) ; +STATE IREXIT2 IRUPDATE DRSELECT DRCAPTURE DREXIT1 DRUPDATE IDLE; +RUNTEST 800 TCK; +ENDIR IDLE; +//Loading device with 'enable' instruction. +SIR 8 TDI (e8) ; +//Loading device with 'enable' instruction. +SIR 8 TDI (e8) ; +// Verification. +// Loading device with a 'verify' instruction. +ENDIR IRPAUSE; +SIR 8 TDI (ee) ; +ENDDR DRPAUSE; +SDR 7 TDI (00) SMASK (7f) ; +ENDIR IDLE; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (40) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9ffffffffffffffffffffffffffffffff777fffffffffffffffffffffffffdffe7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (60) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f97fffffffffffffffffffffffbfffe9de7fffffffffffffffffeffffffffffbfe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (20) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffbf99e7ffffffffffbffffffffffffffffffde0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (30) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffbf99e7fffffffffffffbfffffffffffffff7e7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (70) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f93ffffffffffffffffffffffffffff99e7fffffefbefbffffffefbeffffeaab2a7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (50) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1bffffffffffffffffffffdfffffffffeeefffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (10) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (18) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffbfffffff99e7fffffffffffffffffffffffffeaabfc7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (58) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffbf99e7ffffffffeffffffffffffffffefffde4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (78) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffbf87e7fffffffbffffffffffffffffffeffde7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (38) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (28) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffefffbbefffffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (68) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (48) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffffffffffd7f7ffffffffffffffffffffefffffffffe83) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (08) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffff5ff7fffffffffffffffffffffffffeaabf64f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (0c) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffbf87e7fffffeffffffffffffffffffffbffde7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (4c) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (6c) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (2c) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (3c) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (7c) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffabfe0f) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (5c) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9dfe1d) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (1c) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9ffffffffffffffffffffffffffffffe607fffffffffffffffffffffffffffffe7c) MASK ( +03fffffffffffffffffffffffffffffe001ff801ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (14) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff544f) MASK ( +03fffffffffffffffffffffffffffffe001f8001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (54) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff07ffe7c) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (74) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f8bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdffff03) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (34) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (24) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffffddf7fffffffffffffffffffbfffffffeffe7d) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (64) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (44) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (04) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (06) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (46) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (66) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (0399fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (26) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02fdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (36) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (76) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (0399fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (56) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (16) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (1e) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03d9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (5e) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02fdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (7e) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (3e) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03d9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (2e) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02fdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (6e) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (4e) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (0399fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (0e) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02fdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (0a) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c8eabfffffffffffffeffffffffffffffefefbfffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (4a) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00feeabf7fffffeffffffffffffffffffffefefbfffffffffffffffffffffffffffe1d) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (6a) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (0200eab7ffff7ffffffffffffffffffffffefefbfffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (2a) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1bff77ffffffffffffffffffffffffffeeefffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (3a) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f8ebffffffffffffffffffffffbffffffefeeffffffffffffffffffffffffffffe69) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (7a) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffbfffffffffffffffffffffffffbbefffffffffffffffffffffffffffffffe7d) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (5a) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c9ffffbffffffffffffffffffffffffafefffffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (1a) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00e1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe69) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (12) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02c5fffffffffffffefffffffffffffffffeeefffffffffffffffffffffffffffffe7d) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (52) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c0fffffffffffffffffffffffffffbfffefeeffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (72) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffff7fffffffffffffffffffffffffeeefffffffffffffffffffffffffffffe69) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (32) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (22) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c8eaffffffffffffffffbffffffffffffefeeffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (62) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00e0effffffffffffffffffffffffefffffefeeffffffffffffffffffffffffffffe69) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (42) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02c3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (02) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (03) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe69) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (43) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (63) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (23) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00e1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe69) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (33) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02c5ffffbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (73) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4f) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (53) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02e1ffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe69) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (13) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f8bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7d) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (1b) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c9dfcefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (5b) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00e1fffffffffffffffffffffffffffffff9fffffffffffffffffffffffffffffffe7f) MASK ( +03fffffffffffffffffffffffffffffe00078001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (7b) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02c3df7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (3b) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c993fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03fffffffffffffffffffffffffffffe00000001ffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (2b) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00e1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (6b) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02c5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (4b) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (0b) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02e1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (0f) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (4f) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (6f) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02e1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (2f) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (3f) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (7f) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (5f) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (1f) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (17) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00e1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (57) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02c5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (77) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (37) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00e1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (27) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02c5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (67) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03c9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (47) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (00e1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (07) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02c5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c) MASK ( +03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (05) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) MASK ( +0000000000000000000000000000000000000000000000000000000000000000000000) ; +RUNTEST 100 TCK; +ENDDR DRPAUSE; +SDR 7 TDI (45) SMASK (7f) ; +RUNTEST DRPAUSE 20 TCK; +ENDDR IDLE; +RUNTEST IDLE 100 TCK; +// masking lower UES bits. +SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) MASK ( +0000000000000000000000000000000000000000000000000000000000000000000000) ; +RUNTEST 100 TCK; +SIR 8 TDI (f0) SMASK (ff) ; +STATE IDLE; +RUNTEST IDLE 20 TCK; +ENDIR IRPAUSE; +SIR 8 TDI (f0) SMASK (ff) ; +STATE IREXIT2 IRUPDATE DRSELECT DRCAPTURE DREXIT1 DRUPDATE IDLE; +RUNTEST 800 TCK; +ENDIR IDLE; +//Loading device with 'conld' instruction. +SIR 8 TDI (c0) ; +RUNTEST IDLE 100 TCK; +//Loading device with 'enable' instruction. +SIR 8 TDI (e8) ; +// Setting Done bit ... +// Loading device with a 'program' instruction. +ENDIR IRPAUSE; +SIR 8 TDI (ea) ; +SDR 281 TDI (0017fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; +ENDIR IDLE; +RUNTEST 10000 TCK; +SIR 8 TDI (f0) SMASK (ff) ; +STATE IDLE; +RUNTEST IDLE 20 TCK; +ENDIR IRPAUSE; +SIR 8 TDI (f0) SMASK (ff) ; +STATE IREXIT2 IRUPDATE DRSELECT DRCAPTURE DREXIT1 DRUPDATE IDLE; +RUNTEST 800 TCK; +ENDIR IDLE; +//Loading device with 'conld' instruction. +SIR 8 TDI (c0) ; +RUNTEST IDLE 100 TCK; +//Loading device with 'idcode' instruction. +SIR 8 TDI (01) ; +SDR 32 TDI (00000000) SMASK (ffffffff) TDO (f6e5f093) MASK (0fff8fff) ; +//Check for Done bit. +SIR 8 TDI (ff) TDO (05) MASK (07) ; +//Loading device with 'bypass' instruction. +SIR 8 TDI (ff) ; +TIR 0 ; +HIR 0 ; +HDR 0 ; +TDR 0 ; +TIR 0 ; +HIR 0 ; +TDR 0 ; +HDR 0 ; +SIR 8 TDI (ff) ; +SDR 1 TDI (00) SMASK (01) ; From 00c7cdf027a0ec034a989bfe566102826f6bd428 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 30 Aug 2016 21:30:03 -0700 Subject: [PATCH 031/100] CPLD: Always clock SGPIO data on external clock rising edge. --- firmware/common/baseband_sgpio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/common/baseband_sgpio.cpp b/firmware/common/baseband_sgpio.cpp index 1cc0d143..8e9e972d 100644 --- a/firmware/common/baseband_sgpio.cpp +++ b/firmware/common/baseband_sgpio.cpp @@ -282,7 +282,7 @@ constexpr CLK_CAPTURE_MODE data_clk_capture_mode( ) { return (direction == Direction::Transmit) ? CLK_CAPTURE_MODE::RISING_CLOCK_EDGE - : CLK_CAPTURE_MODE::FALLING_CLOCK_EDGE + : CLK_CAPTURE_MODE::RISING_CLOCK_EDGE ; } From 5d2ad9c1aaf00019b8db3d64aaece7bbc0d354b1 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 30 Aug 2016 21:33:44 -0700 Subject: [PATCH 032/100] SGPIO: Use pin constants when changing output enables. --- firmware/common/baseband_sgpio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/common/baseband_sgpio.cpp b/firmware/common/baseband_sgpio.cpp index 8e9e972d..2dc34cbe 100644 --- a/firmware/common/baseband_sgpio.cpp +++ b/firmware/common/baseband_sgpio.cpp @@ -184,7 +184,7 @@ constexpr Slice slice_order[] { }; constexpr uint32_t gpio_outreg(const Direction direction) { - return ((direction == Direction::Transmit) ? (1U << 11) : 0U) | (1U << 10); + return ((direction == Direction::Transmit) ? (1U << PIN_DIRECTION) : 0U) | (1U << PIN_DISABLE); } constexpr uint32_t gpio_oenreg(const Direction direction) { From 2396d2d97aa8a1c30c5408e0fe8178218588fa6e Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 31 Aug 2016 11:23:42 -0700 Subject: [PATCH 033/100] CMake: Remove DFU --reset, which fails build if reset fails. dfu-util will often complain of reset not succeeding (which is apparently fine), then halting the programming phase (which is not fine). --- firmware/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 124c420e..2c338ce3 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -59,7 +59,7 @@ add_custom_target( add_custom_target( program - COMMAND dfu-util --device 1fc9:000c --download ${HACKRF_FIRMWARE_IMAGE} --reset + COMMAND dfu-util --device 1fc9:000c --download ${HACKRF_FIRMWARE_IMAGE} COMMAND sleep 1s COMMAND hackrf_spiflash -w ${FIRMWARE_FILENAME} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FIRMWARE_FILENAME} From 1e0d452f57c3761983665d1af292e09bb0109f81 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 2 Sep 2016 22:22:30 -0700 Subject: [PATCH 034/100] RecentEntriesView: Generalize draw_header() implementations. --- firmware/application/ais_app.cpp | 30 ++++--------------- firmware/application/ert_app.cpp | 34 ++++++---------------- firmware/application/recent_entries.hpp | 27 +++++++++++++++++- firmware/application/tpms_app.cpp | 38 +++++++------------------ 4 files changed, 50 insertions(+), 79 deletions(-) diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index 1b04bc18..3d7ad098 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -185,30 +185,6 @@ void AISRecentEntry::update(const ais::Packet& packet) { namespace ui { -static const std::array, 2> ais_columns { { - { "MMSI", 9 }, - { "Name/Call", 20 }, -} }; - -template<> -void RecentEntriesView::draw_header( - const Rect& target_rect, - Painter& painter, - const Style& style -) { - auto x = 0; - for(const auto& column : ais_columns) { - const auto width = column.second; - auto text = column.first; - if( width > text.length() ) { - text.append(width - text.length(), ' '); - } - - painter.draw_string({ x, target_rect.pos.y }, style, text); - x += (width * 8) + 8; - } -} - template<> void RecentEntriesView::draw( const Entry& entry, @@ -303,6 +279,12 @@ AISAppView::AISAppView(NavigationView&) { &recent_entry_detail_view, } }); + const std::array columns { { + { "MMSI", 9 }, + { "Name/Call", 20 }, + } }; + recent_entries_view.set_columns(columns); + recent_entry_detail_view.hidden(true); target_frequency_ = initial_target_frequency; diff --git a/firmware/application/ert_app.cpp b/firmware/application/ert_app.cpp index 5bed3782..fac65320 100644 --- a/firmware/application/ert_app.cpp +++ b/firmware/application/ert_app.cpp @@ -75,32 +75,6 @@ void ERTRecentEntry::update(const ert::Packet& packet) { namespace ui { -static const std::array, 4> ert_columns { { - { "ID", 10 }, - { "Tp", 2 }, - { "Consumpt", 10 }, - { "Cnt", 3 }, -} }; - -template<> -void RecentEntriesView::draw_header( - const Rect& target_rect, - Painter& painter, - const Style& style -) { - auto x = 0; - for(const auto& column : ert_columns) { - const auto width = column.second; - auto text = column.first; - if( width > text.length() ) { - text.append(width - text.length(), ' '); - } - - painter.draw_string({ x, target_rect.pos.y }, style, text); - x += (width * 8) + 8; - } -} - template<> void RecentEntriesView::draw( const Entry& entry, @@ -134,6 +108,14 @@ ERTAppView::ERTAppView(NavigationView&) { &recent_entries_view, } }); + const std::array columns { { + { "ID", 10 }, + { "Tp", 2 }, + { "Consumpt", 10 }, + { "Cnt", 3 }, + } }; + recent_entries_view.set_columns(columns); + radio::enable({ initial_target_frequency, sampling_rate, diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 84afb868..30bf4a8d 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -118,6 +118,8 @@ private: namespace ui { +using RecentEntriesColumn = std::pair; + template class RecentEntriesView : public View { public: @@ -132,6 +134,16 @@ public: set_focusable(true); } + template + void set_columns( + const std::array& columns + ) { + _columns.clear(); + for(const auto& column : columns) { + _columns.emplace_back(column); + } + } + void paint(Painter& painter) override { const auto r = screen_rect(); const auto& s = style(); @@ -192,6 +204,7 @@ public: private: Entries& recent; + std::vector> _columns; using EntryKey = typename Entry::Key; EntryKey selected_key = Entry::invalid_key; @@ -226,7 +239,19 @@ private: const Rect& target_rect, Painter& painter, const Style& style - ); + ) { + auto x = 0; + for(const auto& column : _columns) { + const auto width = column.second; + auto text = column.first; + if( width > text.length() ) { + text.append(width - text.length(), ' '); + } + + painter.draw_string({ x, target_rect.pos.y }, style, text); + x += (width * 8) + 8; + } + } void draw( const Entry& entry, diff --git a/firmware/application/tpms_app.cpp b/firmware/application/tpms_app.cpp index 2a13e2f2..25ad2420 100644 --- a/firmware/application/tpms_app.cpp +++ b/firmware/application/tpms_app.cpp @@ -95,34 +95,6 @@ void TPMSRecentEntry::update(const tpms::Reading& reading) { namespace ui { -static const std::array, 6> tpms_columns { { - { "Tp", 2 }, - { "ID", 8 }, - { "kPa", 3 }, - { "C", 3 }, - { "Cnt", 3 }, - { "Fl", 2 }, -} }; - -template<> -void RecentEntriesView::draw_header( - const Rect& target_rect, - Painter& painter, - const Style& style -) { - auto x = 0; - for(const auto& column : tpms_columns) { - const auto width = column.second; - auto text = column.first; - if( width > text.length() ) { - text.append(width - text.length(), ' '); - } - - painter.draw_string({ x, target_rect.pos.y }, style, text); - x += (width * 8) + 8; - } -} - template<> void RecentEntriesView::draw( const Entry& entry, @@ -176,6 +148,16 @@ TPMSAppView::TPMSAppView(NavigationView&) { &recent_entries_view, } }); + const std::array columns { { + { "Tp", 2 }, + { "ID", 8 }, + { "kPa", 3 }, + { "C", 3 }, + { "Cnt", 3 }, + { "Fl", 2 }, + } }; + recent_entries_view.set_columns(columns); + radio::enable({ tuning_frequency(), sampling_rate, From c6f7d7f844f63fce33bd419d292d45ab8d803c86 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 2 Sep 2016 22:44:40 -0700 Subject: [PATCH 035/100] RecentEntriesView: Extract duplicate focus+selection style code. --- firmware/application/ais_app.cpp | 7 ++----- firmware/application/ert_app.cpp | 7 ++----- firmware/application/recent_entries.hpp | 6 +++--- firmware/application/tpms_app.cpp | 7 ++----- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index 3d7ad098..cb9e1ef8 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -190,11 +190,8 @@ void RecentEntriesView::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style, - const bool is_selected + const Style& style ) { - const auto& draw_style = is_selected ? style.invert() : style; - std::string line = ais::format::mmsi(entry.mmsi) + " "; if( !entry.name.empty() ) { line += entry.name; @@ -203,7 +200,7 @@ void RecentEntriesView::draw( } line.resize(target_rect.width() / 8, ' '); - painter.draw_string(target_rect.pos, draw_style, line); + painter.draw_string(target_rect.pos, style, line); } AISRecentEntryDetailView::AISRecentEntryDetailView() { diff --git a/firmware/application/ert_app.cpp b/firmware/application/ert_app.cpp index fac65320..d02133c8 100644 --- a/firmware/application/ert_app.cpp +++ b/firmware/application/ert_app.cpp @@ -80,11 +80,8 @@ void RecentEntriesView::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style, - const bool is_selected + const Style& style ) { - const auto& draw_style = is_selected ? style.invert() : style; - std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption); if( entry.received_count > 999 ) { @@ -94,7 +91,7 @@ void RecentEntriesView::draw( } line.resize(target_rect.width() / 8, ' '); - painter.draw_string(target_rect.pos, draw_style, line); + painter.draw_string(target_rect.pos, style, line); } ERTAppView::ERTAppView(NavigationView&) { diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 30bf4a8d..c0118f77 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -170,7 +170,8 @@ public: for(auto p = range.first; p != range.second; p++) { const auto& entry = *p; const auto is_selected_key = (selected_key == entry.key()); - draw(entry, target_rect, painter, s, (has_focus() && is_selected_key)); + const auto item_style = (has_focus() && is_selected_key) ? s.invert() : s; + draw(entry, target_rect, painter, item_style); target_rect.pos.y += target_rect.height(); } @@ -257,8 +258,7 @@ private: const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style, - const bool is_selected + const Style& style ); }; diff --git a/firmware/application/tpms_app.cpp b/firmware/application/tpms_app.cpp index 25ad2420..9da4fce6 100644 --- a/firmware/application/tpms_app.cpp +++ b/firmware/application/tpms_app.cpp @@ -100,11 +100,8 @@ void RecentEntriesView::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style, - const bool is_selected + const Style& style ) { - const auto& draw_style = is_selected ? style.invert() : style; - std::string line = tpms::format::type(entry.type) + " " + tpms::format::id(entry.id); if( entry.last_pressure.is_valid() ) { @@ -132,7 +129,7 @@ void RecentEntriesView::draw( } line.resize(target_rect.width() / 8, ' '); - painter.draw_string(target_rect.pos, draw_style, line); + painter.draw_string(target_rect.pos, style, line); } TPMSAppView::TPMSAppView(NavigationView&) { From 4d781df76c2c3f95de093d405b658ff2cbcda667 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 3 Sep 2016 12:58:11 -0700 Subject: [PATCH 036/100] RecentEntries: Don't reference Entry template arg directly. --- firmware/application/recent_entries.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index c0118f77..0f899ed0 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -37,13 +37,13 @@ template class RecentEntries { public: using EntryType = Entry; - using Key = typename Entry::Key; - using ContainerType = std::list; + using Key = typename EntryType::Key; + using ContainerType = std::list; using const_reference = typename ContainerType::const_reference; using const_iterator = typename ContainerType::const_iterator; using RangeType = std::pair; - const Entry& on_packet(const Key key, const Packet& packet) { + const EntryType& on_packet(const Key key, const Packet& packet) { auto matching_recent = find(key); if( matching_recent != std::end(entries) ) { // Found within. Move to front of list, increment counter. @@ -67,7 +67,7 @@ public: const_iterator find(const Key key) const { return std::find_if( std::begin(entries), std::end(entries), - [key](const Entry& e) { return e.key() == key; } + [key](const EntryType& e) { return e.key() == key; } ); } From 42d98c3b4595e17032c9a19656a639a60e6fa8ac Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 3 Sep 2016 16:38:44 -0700 Subject: [PATCH 037/100] RecentEntries: Remove Packet template arg. --- firmware/application/ais_app.cpp | 7 ++++--- firmware/application/ais_app.hpp | 2 +- firmware/application/ert_app.cpp | 3 ++- firmware/application/ert_app.hpp | 2 +- firmware/application/recent_entries.hpp | 9 +++------ firmware/application/tpms_app.cpp | 3 ++- firmware/application/tpms_app.hpp | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index cb9e1ef8..049acf71 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -336,12 +336,13 @@ void AISAppView::on_packet(const ais::Packet& packet) { logger->on_packet(packet); } - const auto updated_entry = recent.on_packet(packet.source_id(), packet); + auto& entry = recent.on_packet(packet.source_id()); + entry.update(packet); recent_entries_view.set_dirty(); // TODO: Crude hack, should be a more formal listener arrangement... - if( updated_entry.key() == recent_entry_detail_view.entry().key() ) { - recent_entry_detail_view.set_entry(updated_entry); + if( entry.key() == recent_entry_detail_view.entry().key() ) { + recent_entry_detail_view.set_entry(entry); } } diff --git a/firmware/application/ais_app.hpp b/firmware/application/ais_app.hpp index 10da06a2..556002e1 100644 --- a/firmware/application/ais_app.hpp +++ b/firmware/application/ais_app.hpp @@ -91,7 +91,7 @@ struct AISRecentEntry { void update(const ais::Packet& packet); }; -using AISRecentEntries = RecentEntries; +using AISRecentEntries = RecentEntries; class AISLogger { public: diff --git a/firmware/application/ert_app.cpp b/firmware/application/ert_app.cpp index d02133c8..fcfc9b1e 100644 --- a/firmware/application/ert_app.cpp +++ b/firmware/application/ert_app.cpp @@ -150,7 +150,8 @@ void ERTAppView::on_packet(const ert::Packet& packet) { } if( packet.crc_ok() ) { - recent.on_packet({ packet.id(), packet.commodity_type() }, packet); + auto& entry = recent.on_packet({ packet.id(), packet.commodity_type() }); + entry.update(packet); recent_entries_view.set_dirty(); } } diff --git a/firmware/application/ert_app.hpp b/firmware/application/ert_app.hpp index cba9e74a..49c7701e 100644 --- a/firmware/application/ert_app.hpp +++ b/firmware/application/ert_app.hpp @@ -100,7 +100,7 @@ private: LogFile log_file; }; -using ERTRecentEntries = RecentEntries; +using ERTRecentEntries = RecentEntries; namespace ui { diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 0f899ed0..9c23bde6 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -33,7 +33,7 @@ #include #include -template +template class RecentEntries { public: using EntryType = Entry; @@ -43,7 +43,7 @@ public: using const_iterator = typename ContainerType::const_iterator; using RangeType = std::pair; - const EntryType& on_packet(const Key key, const Packet& packet) { + EntryType& on_packet(const Key key) { auto matching_recent = find(key); if( matching_recent != std::end(entries) ) { // Found within. Move to front of list, increment counter. @@ -54,10 +54,7 @@ public: truncate_entries(); } - auto& entry = entries.front(); - entry.update(packet); - - return entry; + return entries.front(); } const_reference front() const { diff --git a/firmware/application/tpms_app.cpp b/firmware/application/tpms_app.cpp index 9da4fce6..a00ff16f 100644 --- a/firmware/application/tpms_app.cpp +++ b/firmware/application/tpms_app.cpp @@ -199,7 +199,8 @@ void TPMSAppView::on_packet(const tpms::Packet& packet) { const auto reading_opt = packet.reading(); if( reading_opt.is_valid() ) { const auto reading = reading_opt.value(); - recent.on_packet({ reading.type(), reading.id() }, reading); + auto& entry = recent.on_packet({ reading.type(), reading.id() }); + entry.update(reading); recent_entries_view.set_dirty(); } } diff --git a/firmware/application/tpms_app.hpp b/firmware/application/tpms_app.hpp index 98715219..c294862e 100644 --- a/firmware/application/tpms_app.hpp +++ b/firmware/application/tpms_app.hpp @@ -72,7 +72,7 @@ struct TPMSRecentEntry { void update(const tpms::Reading& reading); }; -using TPMSRecentEntries = RecentEntries; +using TPMSRecentEntries = RecentEntries; class TPMSLogger { public: From b596d0697c1bc188f50162ad5206427cc302c5f7 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 3 Sep 2016 17:09:03 -0700 Subject: [PATCH 038/100] RecentEntries: Extract range_around(). --- firmware/application/recent_entries.hpp | 50 +++++++++++++------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 9c23bde6..d276dc84 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -41,7 +41,6 @@ public: using ContainerType = std::list; using const_reference = typename ContainerType::const_reference; using const_iterator = typename ContainerType::const_iterator; - using RangeType = std::pair; EntryType& on_packet(const Key key) { auto matching_recent = find(key); @@ -80,28 +79,6 @@ public: return entries.empty(); } - RangeType range_around( - const_iterator item, const size_t count - ) const { - auto start = item; - auto end = item; - size_t i = 0; - - // Move start iterator toward first entry. - while( (start != std::begin(entries)) && (i < count / 2) ) { - std::advance(start, -1); - i++; - } - - // Move end iterator toward last entry. - while( (end != std::end(entries)) && (i < count) ) { - std::advance(end, 1); - i++; - } - - return { start, end }; - } - private: ContainerType entries; const size_t entries_max = 64; @@ -113,6 +90,31 @@ private: } }; +template +static std::pair range_around( + const ContainerType& entries, + typename ContainerType::const_iterator item, + const size_t count +) { + auto start = item; + auto end = item; + size_t i = 0; + + // Move start iterator toward first entry. + while( (start != std::begin(entries)) && (i < count / 2) ) { + std::advance(start, -1); + i++; + } + + // Move end iterator toward last entry. + while( (end != std::end(entries)) && (i < count) ) { + std::advance(end, 1); + i++; + } + + return { start, end }; +} + namespace ui { using RecentEntriesColumn = std::pair; @@ -162,7 +164,7 @@ public: selected = std::begin(recent); } - auto range = recent.range_around(selected, visible_item_count); + auto range = range_around(recent, selected, visible_item_count); for(auto p = range.first; p != range.second; p++) { const auto& entry = *p; From c8f7863c8394c39c9322cc9b25370aaf007d4cf1 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 3 Sep 2016 18:12:07 -0700 Subject: [PATCH 039/100] RecentEntries: Expose container as base class. Trying to refactor until there's nothing but the base class left. --- firmware/application/recent_entries.hpp | 35 +++++++------------------ 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index d276dc84..a06038e0 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -34,7 +34,7 @@ #include template -class RecentEntries { +class RecentEntries : public std::list { public: using EntryType = Entry; using Key = typename EntryType::Key; @@ -44,48 +44,31 @@ public: EntryType& on_packet(const Key key) { auto matching_recent = find(key); - if( matching_recent != std::end(entries) ) { + if( matching_recent != std::end(*this) ) { // Found within. Move to front of list, increment counter. - entries.push_front(*matching_recent); - entries.erase(matching_recent); + this->push_front(*matching_recent); + this->erase(matching_recent); } else { - entries.emplace_front(key); + this->emplace_front(key); truncate_entries(); } - return entries.front(); - } - - const_reference front() const { - return entries.front(); + return this->front(); } const_iterator find(const Key key) const { return std::find_if( - std::begin(entries), std::end(entries), + std::begin(*this), std::end(*this), [key](const EntryType& e) { return e.key() == key; } ); } - const_iterator begin() const { - return entries.begin(); - } - - const_iterator end() const { - return entries.end(); - } - - bool empty() const { - return entries.empty(); - } - private: - ContainerType entries; const size_t entries_max = 64; void truncate_entries() { - while(entries.size() > entries_max) { - entries.pop_back(); + while(this->size() > entries_max) { + this->pop_back(); } } }; From bd785d8bf4dbf2099029fa72cbaa04aa6e19b67b Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 3 Sep 2016 18:26:48 -0700 Subject: [PATCH 040/100] RecentEntries: Extract more algorithms. --- firmware/application/ais_app.cpp | 2 +- firmware/application/ert_app.cpp | 2 +- firmware/application/recent_entries.hpp | 66 ++++++++++++------------- firmware/application/tpms_app.cpp | 2 +- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index 049acf71..5c50dbac 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -336,7 +336,7 @@ void AISAppView::on_packet(const ais::Packet& packet) { logger->on_packet(packet); } - auto& entry = recent.on_packet(packet.source_id()); + auto& entry = ::on_packet(recent, packet.source_id()); entry.update(packet); recent_entries_view.set_dirty(); diff --git a/firmware/application/ert_app.cpp b/firmware/application/ert_app.cpp index fcfc9b1e..8c1b305f 100644 --- a/firmware/application/ert_app.cpp +++ b/firmware/application/ert_app.cpp @@ -150,7 +150,7 @@ void ERTAppView::on_packet(const ert::Packet& packet) { } if( packet.crc_ok() ) { - auto& entry = recent.on_packet({ packet.id(), packet.commodity_type() }); + auto& entry = ::on_packet(recent, ERTRecentEntry::Key { packet.id(), packet.commodity_type() }); entry.update(packet); recent_entries_view.set_dirty(); } diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index a06038e0..22576588 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -41,38 +41,38 @@ public: using ContainerType = std::list; using const_reference = typename ContainerType::const_reference; using const_iterator = typename ContainerType::const_iterator; - - EntryType& on_packet(const Key key) { - auto matching_recent = find(key); - if( matching_recent != std::end(*this) ) { - // Found within. Move to front of list, increment counter. - this->push_front(*matching_recent); - this->erase(matching_recent); - } else { - this->emplace_front(key); - truncate_entries(); - } - - return this->front(); - } - - const_iterator find(const Key key) const { - return std::find_if( - std::begin(*this), std::end(*this), - [key](const EntryType& e) { return e.key() == key; } - ); - } - -private: - const size_t entries_max = 64; - - void truncate_entries() { - while(this->size() > entries_max) { - this->pop_back(); - } - } }; +template +typename ContainerType::const_iterator find(const ContainerType& entries, const Key key) { + return std::find_if( + std::begin(entries), std::end(entries), + [key](typename ContainerType::const_reference e) { return e.key() == key; } + ); +} + +template +static void truncate_entries(ContainerType& entries, const size_t entries_max = 64) { + while(entries.size() > entries_max) { + entries.pop_back(); + } +} + +template +typename ContainerType::reference on_packet(ContainerType& entries, const Key key) { + auto matching_recent = find(entries, key); + if( matching_recent != std::end(entries) ) { + // Found within. Move to front of list, increment counter. + entries.push_front(*matching_recent); + entries.erase(matching_recent); + } else { + entries.emplace_front(key); + truncate_entries(entries); + } + + return entries.front(); +} + template static std::pair range_around( const ContainerType& entries, @@ -142,7 +142,7 @@ public: draw_header(target_rect, painter, style_header); target_rect.pos.y += target_rect.height(); - auto selected = recent.find(selected_key); + auto selected = find(recent, selected_key); if( selected == std::end(recent) ) { selected = std::begin(recent); } @@ -171,7 +171,7 @@ public: bool on_key(const ui::KeyEvent event) override { if( event == ui::KeyEvent::Select ) { if( on_select ) { - const auto selected = recent.find(selected_key); + const auto selected = find(recent, selected_key); if( selected != std::end(recent) ) { on_select(*selected); return true; @@ -193,7 +193,7 @@ private: EntryKey selected_key = Entry::invalid_key; void advance(const int32_t amount) { - auto selected = recent.find(selected_key); + auto selected = find(recent, selected_key); if( selected == std::end(recent) ) { if( recent.empty() ) { selected_key = Entry::invalid_key; diff --git a/firmware/application/tpms_app.cpp b/firmware/application/tpms_app.cpp index a00ff16f..c435bce1 100644 --- a/firmware/application/tpms_app.cpp +++ b/firmware/application/tpms_app.cpp @@ -199,7 +199,7 @@ void TPMSAppView::on_packet(const tpms::Packet& packet) { const auto reading_opt = packet.reading(); if( reading_opt.is_valid() ) { const auto reading = reading_opt.value(); - auto& entry = recent.on_packet({ reading.type(), reading.id() }); + auto& entry = ::on_packet(recent, TPMSRecentEntry::Key { reading.type(), reading.id() }); entry.update(reading); recent_entries_view.set_dirty(); } From 50e2dfa0b9c87fb70c9036f8a5cbd84a83021172 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 3 Sep 2016 22:53:44 -0700 Subject: [PATCH 041/100] RecentEntries: Make templated type of std::list. --- firmware/application/recent_entries.hpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 22576588..795bf602 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -34,14 +34,7 @@ #include template -class RecentEntries : public std::list { -public: - using EntryType = Entry; - using Key = typename EntryType::Key; - using ContainerType = std::list; - using const_reference = typename ContainerType::const_reference; - using const_iterator = typename ContainerType::const_iterator; -}; +using RecentEntries = std::list; template typename ContainerType::const_iterator find(const ContainerType& entries, const Key key) { @@ -105,7 +98,7 @@ using RecentEntriesColumn = std::pair; template class RecentEntriesView : public View { public: - using Entry = typename Entries::EntryType; + using Entry = typename Entries::value_type; std::function on_select; From 61f0d97c39f22a5ac2f73f0efec8009320945bbd Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 5 Sep 2016 12:09:29 -0700 Subject: [PATCH 042/100] RecentEntriesView: Extract header and table widget, package in to top-level View. --- firmware/application/ais_app.cpp | 2 +- firmware/application/ert_app.cpp | 2 +- firmware/application/recent_entries.hpp | 134 +++++++++++++++++------- firmware/application/tpms_app.cpp | 2 +- 4 files changed, 97 insertions(+), 43 deletions(-) diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index 5c50dbac..2db3f7df 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -186,7 +186,7 @@ void AISRecentEntry::update(const ais::Packet& packet) { namespace ui { template<> -void RecentEntriesView::draw( +void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, diff --git a/firmware/application/ert_app.cpp b/firmware/application/ert_app.cpp index 8c1b305f..108c7937 100644 --- a/firmware/application/ert_app.cpp +++ b/firmware/application/ert_app.cpp @@ -76,7 +76,7 @@ void ERTRecentEntry::update(const ert::Packet& packet) { namespace ui { template<> -void RecentEntriesView::draw( +void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 795bf602..fc2c08ca 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -95,19 +95,8 @@ namespace ui { using RecentEntriesColumn = std::pair; -template -class RecentEntriesView : public View { +class RecentEntriesHeader : public Widget { public: - using Entry = typename Entries::value_type; - - std::function on_select; - - RecentEntriesView( - Entries& recent - ) : recent { recent } - { - set_focusable(true); - } template void set_columns( @@ -119,6 +108,47 @@ public: } } + void paint(Painter& painter) override { + const auto r = screen_rect(); + const auto& parent_style = style(); + + const Style style { + .font = parent_style.font, + .background = Color::blue(), + .foreground = parent_style.foreground, + }; + + auto p = r.pos; + for(const auto& column : _columns) { + const auto width = column.second; + auto text = column.first; + if( width > text.length() ) { + text.append(width - text.length(), ' '); + } + + painter.draw_string(p, style, text); + p += { static_cast((width * 8) + 8), 0 }; + } + } + +private: + std::vector _columns; +}; + +template +class RecentEntriesTable : public Widget { +public: + using Entry = typename Entries::value_type; + + std::function on_select; + + RecentEntriesTable( + Entries& recent + ) : recent { recent } + { + set_focusable(true); + } + void paint(Painter& painter) override { const auto r = screen_rect(); const auto& s = style(); @@ -126,15 +156,6 @@ public: Rect target_rect { r.pos, { r.width(), s.font.line_height() }}; const size_t visible_item_count = r.height() / s.font.line_height(); - const Style style_header { - .font = font::fixed_8x16, - .background = Color::blue(), - .foreground = Color::white(), - }; - - draw_header(target_rect, painter, style_header); - target_rect.pos.y += target_rect.height(); - auto selected = find(recent, selected_key); if( selected == std::end(recent) ) { selected = std::begin(recent); @@ -180,7 +201,6 @@ public: private: Entries& recent; - std::vector> _columns; using EntryKey = typename Entry::Key; EntryKey selected_key = Entry::invalid_key; @@ -211,24 +231,6 @@ private: set_dirty(); } - void draw_header( - const Rect& target_rect, - Painter& painter, - const Style& style - ) { - auto x = 0; - for(const auto& column : _columns) { - const auto width = column.second; - auto text = column.first; - if( width > text.length() ) { - text.append(width - text.length(), ' '); - } - - painter.draw_string({ x, target_rect.pos.y }, style, text); - x += (width * 8) + 8; - } - } - void draw( const Entry& entry, const Rect& target_rect, @@ -237,6 +239,58 @@ private: ); }; +template +class RecentEntriesView : public View { +public: + using Entry = typename Entries::value_type; + + std::function on_select; + + RecentEntriesView( + Entries& recent + ) : _table { recent } + { + add_children({ { + &_header, + &_table, + } }); + + _table.on_select = [this](const Entry& entry) { if( this->on_select ) { this->on_select(entry); } }; + } + + template + void set_columns( + const std::array& columns + ) { + _header.set_columns(columns); + } + + void set_parent_rect(const Rect new_parent_rect) override { + constexpr Dim scale_height = 16; + + View::set_parent_rect(new_parent_rect); + _header.set_parent_rect({ 0, 0, new_parent_rect.width(), scale_height }); + _table.set_parent_rect({ + 0, scale_height, + new_parent_rect.width(), + new_parent_rect.height() - scale_height + }); + } + + void paint(Painter&) override { + // Children completely cover this View, do not paint. + // TODO: What happens here shouldn't matter if I do proper damage detection! + } + + void on_focus() override { + _table.focus(); + } + +private: + RecentEntriesHeader _header; + RecentEntriesTable _table; +}; + } /* namespace ui */ #endif/*__RECENT_ENTRIES_H__*/ diff --git a/firmware/application/tpms_app.cpp b/firmware/application/tpms_app.cpp index c435bce1..e7f38e37 100644 --- a/firmware/application/tpms_app.cpp +++ b/firmware/application/tpms_app.cpp @@ -96,7 +96,7 @@ void TPMSRecentEntry::update(const tpms::Reading& reading) { namespace ui { template<> -void RecentEntriesView::draw( +void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, From 298324d6e47c31fd01b5d5fc9193064ab891867b Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 5 Sep 2016 12:34:41 -0700 Subject: [PATCH 043/100] RecentEntries: Extract Columns model. --- firmware/application/ais_app.cpp | 6 ---- firmware/application/ais_app.hpp | 6 +++- firmware/application/ert_app.cpp | 8 ----- firmware/application/ert_app.hpp | 8 ++++- firmware/application/recent_entries.hpp | 43 ++++++++++++++----------- firmware/application/tpms_app.cpp | 10 ------ firmware/application/tpms_app.hpp | 10 +++++- 7 files changed, 46 insertions(+), 45 deletions(-) diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index 2db3f7df..c79f3050 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -276,12 +276,6 @@ AISAppView::AISAppView(NavigationView&) { &recent_entry_detail_view, } }); - const std::array columns { { - { "MMSI", 9 }, - { "Name/Call", 20 }, - } }; - recent_entries_view.set_columns(columns); - recent_entry_detail_view.hidden(true); target_frequency_ = initial_target_frequency; diff --git a/firmware/application/ais_app.hpp b/firmware/application/ais_app.hpp index 556002e1..2e26d2ab 100644 --- a/firmware/application/ais_app.hpp +++ b/firmware/application/ais_app.hpp @@ -161,7 +161,11 @@ private: AISRecentEntries recent; std::unique_ptr logger; - AISRecentEntriesView recent_entries_view { recent }; + const RecentEntriesColumns columns { { + { "MMSI", 9 }, + { "Name/Call", 20 }, + } }; + AISRecentEntriesView recent_entries_view { columns, recent }; AISRecentEntryDetailView recent_entry_detail_view; static constexpr auto header_height = 1 * 16; diff --git a/firmware/application/ert_app.cpp b/firmware/application/ert_app.cpp index 108c7937..d8d20fa4 100644 --- a/firmware/application/ert_app.cpp +++ b/firmware/application/ert_app.cpp @@ -105,14 +105,6 @@ ERTAppView::ERTAppView(NavigationView&) { &recent_entries_view, } }); - const std::array columns { { - { "ID", 10 }, - { "Tp", 2 }, - { "Consumpt", 10 }, - { "Cnt", 3 }, - } }; - recent_entries_view.set_columns(columns); - radio::enable({ initial_target_frequency, sampling_rate, diff --git a/firmware/application/ert_app.hpp b/firmware/application/ert_app.hpp index 49c7701e..6a774ede 100644 --- a/firmware/application/ert_app.hpp +++ b/firmware/application/ert_app.hpp @@ -129,7 +129,13 @@ private: ERTRecentEntries recent; std::unique_ptr logger; - ERTRecentEntriesView recent_entries_view { recent }; + const RecentEntriesColumns columns { { + { "ID", 10 }, + { "Tp", 2 }, + { "Consumpt", 10 }, + { "Cnt", 3 }, + } }; + ERTRecentEntriesView recent_entries_view { columns, recent }; static constexpr auto header_height = 1 * 16; diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index fc2c08ca..5bfa3e12 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -95,17 +95,29 @@ namespace ui { using RecentEntriesColumn = std::pair; +class RecentEntriesColumns { +public: + using ContainerType = std::vector; + + RecentEntriesColumns( + const std::initializer_list columns + ) : _columns { columns } + { + } + + ContainerType::const_iterator begin() const { return std::begin(_columns); } + ContainerType::const_iterator end() const { return std::end(_columns); } + +private: + const ContainerType _columns; +}; + class RecentEntriesHeader : public Widget { public: - - template - void set_columns( - const std::array& columns - ) { - _columns.clear(); - for(const auto& column : columns) { - _columns.emplace_back(column); - } + RecentEntriesHeader( + const RecentEntriesColumns& columns + ) : _columns { columns } + { } void paint(Painter& painter) override { @@ -132,7 +144,7 @@ public: } private: - std::vector _columns; + const RecentEntriesColumns& _columns; }; template @@ -247,8 +259,10 @@ public: std::function on_select; RecentEntriesView( + const RecentEntriesColumns& columns, Entries& recent - ) : _table { recent } + ) : _header { columns }, + _table { recent } { add_children({ { &_header, @@ -258,13 +272,6 @@ public: _table.on_select = [this](const Entry& entry) { if( this->on_select ) { this->on_select(entry); } }; } - template - void set_columns( - const std::array& columns - ) { - _header.set_columns(columns); - } - void set_parent_rect(const Rect new_parent_rect) override { constexpr Dim scale_height = 16; diff --git a/firmware/application/tpms_app.cpp b/firmware/application/tpms_app.cpp index e7f38e37..989e98d9 100644 --- a/firmware/application/tpms_app.cpp +++ b/firmware/application/tpms_app.cpp @@ -145,16 +145,6 @@ TPMSAppView::TPMSAppView(NavigationView&) { &recent_entries_view, } }); - const std::array columns { { - { "Tp", 2 }, - { "ID", 8 }, - { "kPa", 3 }, - { "C", 3 }, - { "Cnt", 3 }, - { "Fl", 2 }, - } }; - recent_entries_view.set_columns(columns); - radio::enable({ tuning_frequency(), sampling_rate, diff --git a/firmware/application/tpms_app.hpp b/firmware/application/tpms_app.hpp index c294862e..771efd7f 100644 --- a/firmware/application/tpms_app.hpp +++ b/firmware/application/tpms_app.hpp @@ -153,7 +153,15 @@ private: TPMSRecentEntries recent; std::unique_ptr logger; - TPMSRecentEntriesView recent_entries_view { recent }; + const RecentEntriesColumns columns { { + { "Tp", 2 }, + { "ID", 8 }, + { "kPa", 3 }, + { "C", 3 }, + { "Cnt", 3 }, + { "Fl", 2 }, + } }; + TPMSRecentEntriesView recent_entries_view { columns, recent }; uint32_t target_frequency_ = initial_target_frequency; From 8a69b0523e2123491cfa5fd40de125e34bf1a78d Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 5 Sep 2016 14:53:04 -0700 Subject: [PATCH 044/100] View::add_children: Use std::list_initializer as argument. Improvement in code size -- 944 bytes. Some day I will understand C++11 well enough to do the right thing the first time. --- firmware/application/ais_app.cpp | 8 ++++---- firmware/application/analog_audio_app.cpp | 12 ++++++------ firmware/application/capture_app.cpp | 4 ++-- firmware/application/ert_app.cpp | 4 ++-- firmware/application/recent_entries.hpp | 4 ++-- firmware/application/tpms_app.cpp | 4 ++-- firmware/application/ui_baseband_stats_view.cpp | 4 ++-- firmware/application/ui_debug.cpp | 12 ++++++------ firmware/application/ui_navigation.cpp | 16 ++++++++-------- firmware/application/ui_receiver.cpp | 8 ++++---- firmware/application/ui_record_view.cpp | 4 ++-- firmware/application/ui_sd_card_debug.cpp | 4 ++-- firmware/application/ui_setup.cpp | 16 ++++++++-------- firmware/application/ui_touch_calibration.cpp | 4 ++-- firmware/common/ui_widget.cpp | 6 ++++-- firmware/common/ui_widget.hpp | 2 +- 16 files changed, 57 insertions(+), 55 deletions(-) diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index c79f3050..8a53da71 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -204,9 +204,9 @@ void RecentEntriesTable::draw( } AISRecentEntryDetailView::AISRecentEntryDetailView() { - add_children({ { + add_children({ &button_done, - } }); + }); button_done.on_select = [this](const ui::Button&) { if( this->on_close ) { @@ -264,7 +264,7 @@ void AISRecentEntryDetailView::set_entry(const AISRecentEntry& entry) { AISAppView::AISAppView(NavigationView&) { baseband::run_image(portapack::spi_flash::image_tag_ais); - add_children({ { + add_children({ &label_channel, &options_channel, &field_rf_amp, @@ -274,7 +274,7 @@ AISAppView::AISAppView(NavigationView&) { &channel, &recent_entries_view, &recent_entry_detail_view, - } }); + }); recent_entry_detail_view.hidden(true); diff --git a/firmware/application/analog_audio_app.cpp b/firmware/application/analog_audio_app.cpp index 417d1ea1..0a6009ca 100644 --- a/firmware/application/analog_audio_app.cpp +++ b/firmware/application/analog_audio_app.cpp @@ -44,10 +44,10 @@ AMOptionsView::AMOptionsView( { set_style(style); - add_children({ { + add_children({ &label_config, &options_config, - } }); + }); options_config.set_selected_index(receiver_model.am_configuration()); options_config.on_change = [this](size_t n, OptionsField::value_t) { @@ -63,10 +63,10 @@ NBFMOptionsView::NBFMOptionsView( { set_style(style); - add_children({ { + add_children({ &label_config, &options_config, - } }); + }); options_config.set_selected_index(receiver_model.nbfm_configuration()); options_config.on_change = [this](size_t n, OptionsField::value_t) { @@ -79,7 +79,7 @@ NBFMOptionsView::NBFMOptionsView( AnalogAudioView::AnalogAudioView( NavigationView& nav ) { - add_children({ { + add_children({ &rssi, &channel, &audio, @@ -90,7 +90,7 @@ AnalogAudioView::AnalogAudioView( &field_volume, &record_view, &waterfall, - } }); + }); field_frequency.set_value(receiver_model.tuning_frequency()); field_frequency.set_step(receiver_model.frequency_step()); diff --git a/firmware/application/capture_app.cpp b/firmware/application/capture_app.cpp index 6ebcd02f..cd2a3824 100644 --- a/firmware/application/capture_app.cpp +++ b/firmware/application/capture_app.cpp @@ -34,7 +34,7 @@ namespace ui { CaptureAppView::CaptureAppView(NavigationView& nav) { baseband::run_image(portapack::spi_flash::image_tag_capture); - add_children({ { + add_children({ &rssi, &channel, &field_frequency, @@ -44,7 +44,7 @@ CaptureAppView::CaptureAppView(NavigationView& nav) { &field_vga, &record_view, &waterfall, - } }); + }); field_frequency.set_value(target_frequency()); field_frequency.set_step(receiver_model.frequency_step()); diff --git a/firmware/application/ert_app.cpp b/firmware/application/ert_app.cpp index d8d20fa4..5df0f336 100644 --- a/firmware/application/ert_app.cpp +++ b/firmware/application/ert_app.cpp @@ -97,13 +97,13 @@ void RecentEntriesTable::draw( ERTAppView::ERTAppView(NavigationView&) { baseband::run_image(portapack::spi_flash::image_tag_ert); - add_children({ { + add_children({ &field_rf_amp, &field_lna, &field_vga, &rssi, &recent_entries_view, - } }); + }); radio::enable({ initial_target_frequency, diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 5bfa3e12..e4068270 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -264,10 +264,10 @@ public: ) : _header { columns }, _table { recent } { - add_children({ { + add_children({ &_header, &_table, - } }); + }); _table.on_select = [this](const Entry& entry) { if( this->on_select ) { this->on_select(entry); } }; } diff --git a/firmware/application/tpms_app.cpp b/firmware/application/tpms_app.cpp index 989e98d9..7fe5ab6f 100644 --- a/firmware/application/tpms_app.cpp +++ b/firmware/application/tpms_app.cpp @@ -135,7 +135,7 @@ void RecentEntriesTable::draw( TPMSAppView::TPMSAppView(NavigationView&) { baseband::run_image(portapack::spi_flash::image_tag_tpms); - add_children({ { + add_children({ &rssi, &channel, &options_band, @@ -143,7 +143,7 @@ TPMSAppView::TPMSAppView(NavigationView&) { &field_lna, &field_vga, &recent_entries_view, - } }); + }); radio::enable({ tuning_frequency(), diff --git a/firmware/application/ui_baseband_stats_view.cpp b/firmware/application/ui_baseband_stats_view.cpp index 44774465..b6a9525f 100644 --- a/firmware/application/ui_baseband_stats_view.cpp +++ b/firmware/application/ui_baseband_stats_view.cpp @@ -34,9 +34,9 @@ namespace ui { /* BasebandStatsView *****************************************************/ BasebandStatsView::BasebandStatsView() { - add_children({ { + add_children({ &text_stats, - } }); + }); } static std::string ticks_to_percent_string(const uint32_t ticks) { diff --git a/firmware/application/ui_debug.cpp b/firmware/application/ui_debug.cpp index e3d673a0..a5cc61e6 100644 --- a/firmware/application/ui_debug.cpp +++ b/firmware/application/ui_debug.cpp @@ -35,7 +35,7 @@ namespace ui { /* DebugMemoryView *******************************************************/ DebugMemoryView::DebugMemoryView(NavigationView& nav) { - add_children({ { + add_children({ &text_title, &text_label_m0_core_free, &text_label_m0_core_free_value, @@ -44,7 +44,7 @@ DebugMemoryView::DebugMemoryView(NavigationView& nav) { &text_label_m0_heap_fragments, &text_label_m0_heap_fragments_value, &button_done - } }); + }); const auto m0_core_free = chCoreStatus(); text_label_m0_core_free_value.set(to_string_dec_uint(m0_core_free, 5)); @@ -135,11 +135,11 @@ Coord TemperatureWidget::screen_y( /* TemperatureView *******************************************************/ TemperatureView::TemperatureView(NavigationView& nav) { - add_children({ { + add_children({ &text_title, &temperature_widget, &button_done, - } }); + }); button_done.on_select = [&nav](Button&){ nav.pop(); }; } @@ -219,12 +219,12 @@ RegistersView::RegistersView( std::function&& reader ) : registers_widget { std::move(config), std::move(reader) } { - add_children({ { + add_children({ &text_title, ®isters_widget, &button_update, &button_done, - } }); + }); button_update.on_select = [this](Button&){ this->registers_widget.update(); diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index ba8e783c..b8882620 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -43,13 +43,13 @@ namespace ui { /* SystemStatusView ******************************************************/ SystemStatusView::SystemStatusView() { - add_children({ { + add_children({ &button_back, &title, &button_camera, &button_sleep, &sd_card_status_view, - } }); + }); button_back.on_select = [this](Button&){ if( this->on_back ) { @@ -259,7 +259,7 @@ HackRFFirmwareView::HackRFFirmwareView(NavigationView& nav) { nav.pop(); }; - add_children({ { + add_children({ &text_title, &text_description_1, &text_description_2, @@ -267,7 +267,7 @@ HackRFFirmwareView::HackRFFirmwareView(NavigationView& nav) { &text_description_4, &button_yes, &button_no, - } }); + }); } void HackRFFirmwareView::focus() { @@ -281,10 +281,10 @@ NotImplementedView::NotImplementedView(NavigationView& nav) { nav.pop(); }; - add_children({ { + add_children({ &text_title, &button_done, - } }); + }); } void NotImplementedView::focus() { @@ -303,10 +303,10 @@ ModalMessageView::ModalMessageView( nav.pop(); }; - add_children({ { + add_children({ &text_message, &button_done, - } }); + }); text_message.set(message); diff --git a/firmware/application/ui_receiver.cpp b/firmware/application/ui_receiver.cpp index d48917a4..1e7c648d 100644 --- a/firmware/application/ui_receiver.cpp +++ b/firmware/application/ui_receiver.cpp @@ -241,12 +241,12 @@ FrequencyOptionsView::FrequencyOptionsView( this->on_reference_ppm_correction_changed(v); }; - add_children({ { + add_children({ &text_step, &field_step, &field_ppm, &text_ppm, - } }); + }); } void FrequencyOptionsView::set_step(rf::Frequency f) { @@ -297,10 +297,10 @@ RadioGainOptionsView::RadioGainOptionsView( { set_style(style); - add_children({ { + add_children({ &label_rf_amp, &field_rf_amp, - } }); + }); } /* LNAGainField **********************************************************/ diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index 71211dfc..01155490 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -177,13 +177,13 @@ RecordView::RecordView( write_size { write_size }, buffer_count { buffer_count } { - add_children({ { + add_children({ &rect_background, &button_record, &text_record_filename, &text_record_dropped, &text_time_available, - } }); + }); rect_background.set_parent_rect({ { 0, 0 }, size() }); diff --git a/firmware/application/ui_sd_card_debug.cpp b/firmware/application/ui_sd_card_debug.cpp index 22db218c..12dc5293 100644 --- a/firmware/application/ui_sd_card_debug.cpp +++ b/firmware/application/ui_sd_card_debug.cpp @@ -230,7 +230,7 @@ Thread* SDCardTestThread::thread { nullptr }; namespace ui { SDCardDebugView::SDCardDebugView(NavigationView& nav) { - add_children({ { + add_children({ &text_title, &text_csd_title, &text_csd_value_3, @@ -257,7 +257,7 @@ SDCardDebugView::SDCardDebugView(NavigationView& nav) { &text_test_read_rate_value, &button_test, &button_ok, - } }); + }); button_test.on_select = [this](Button&){ this->on_test(); }; button_ok.on_select = [&nav](Button&){ nav.pop(); }; diff --git a/firmware/application/ui_setup.cpp b/firmware/application/ui_setup.cpp index 9bc75389..2beddf7a 100644 --- a/firmware/application/ui_setup.cpp +++ b/firmware/application/ui_setup.cpp @@ -51,7 +51,7 @@ SetDateTimeView::SetDateTimeView( nav.pop(); }, - add_children({ { + add_children({ &text_title, &field_year, &text_slash1, @@ -66,7 +66,7 @@ SetDateTimeView::SetDateTimeView( &text_format, &button_ok, &button_cancel, - } }); + }); rtc::RTC datetime; rtcGetTime(&RTCD1, &datetime); @@ -119,13 +119,13 @@ SetFrequencyCorrectionView::SetFrequencyCorrectionView( nav.pop(); }, - add_children({ { + add_children({ &text_title, &field_ppm, &text_ppm, &button_ok, &button_cancel, - } }); + }); SetFrequencyCorrectionModel model { static_cast(portapack::persistent_memory::correction_ppb() / 1000) @@ -149,7 +149,7 @@ SetFrequencyCorrectionModel SetFrequencyCorrectionView::form_collect() { } AntennaBiasSetupView::AntennaBiasSetupView(NavigationView& nav) { - add_children({ { + add_children({ &text_title, &text_description_1, &text_description_2, @@ -157,7 +157,7 @@ AntennaBiasSetupView::AntennaBiasSetupView(NavigationView& nav) { &text_description_4, &options_bias, &button_done, - } }); + }); options_bias.set_by_value(receiver_model.antenna_bias() ? 1 : 0); options_bias.on_change = [this](size_t, OptionsField::value_t v) { @@ -172,13 +172,13 @@ void AntennaBiasSetupView::focus() { } AboutView::AboutView(NavigationView& nav) { - add_children({ { + add_children({ &text_title, &text_firmware, &text_cpld_hackrf, &text_cpld_hackrf_status, &button_ok, - } }); + }); button_ok.on_select = [&nav](Button&){ nav.pop(); }; diff --git a/firmware/application/ui_touch_calibration.cpp b/firmware/application/ui_touch_calibration.cpp index 6fe20027..37cd0df8 100644 --- a/firmware/application/ui_touch_calibration.cpp +++ b/firmware/application/ui_touch_calibration.cpp @@ -33,7 +33,7 @@ TouchCalibrationView::TouchCalibrationView( ) : nav { nav }, calibration { touch::default_calibration() } { - add_children({ { + add_children({ &image_calibrate_0, &image_calibrate_1, &image_calibrate_2, @@ -46,7 +46,7 @@ TouchCalibrationView::TouchCalibrationView( &label_failure, &button_cancel, &button_ok, - } }); + }); button_cancel.on_select = [this](Button&){ this->on_cancel(); }; button_ok.on_select = [this](Button&){ this->on_ok(); }; diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 31a71d51..d760faaa 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -232,9 +232,11 @@ void View::add_child(Widget* const widget) { } } -void View::add_children(const std::vector& children) { +void View::add_children(const std::initializer_list children) { + children_.insert(std::end(children_), children); for(auto child : children) { - add_child(child); + child->set_parent(this); + child->set_dirty(); } } diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 2c427249..18a03158 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -154,7 +154,7 @@ public: void paint(Painter& painter) override; void add_child(Widget* const widget); - void add_children(const std::vector& children); + void add_children(const std::initializer_list children); void remove_child(Widget* const widget); const std::vector& children() const override; From 09222f004455ddbb5ae4c023664b48038cd39c52 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 5 Sep 2016 15:04:28 -0700 Subject: [PATCH 045/100] Widget/View: Consolidate dirty code inside Widget. --- firmware/common/ui_widget.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index d760faaa..94716753 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -70,12 +70,19 @@ Widget* Widget::parent() const { } void Widget::set_parent(Widget* const widget) { + if( widget == parent_ ) { + return; + } + if( parent_ && !widget ) { // We have a parent, but are losing it. Update visible status. + dirty_overlapping_children_in_rect(screen_rect()); visible(false); } parent_ = widget; + + set_dirty(); } void Widget::set_dirty() { @@ -227,7 +234,6 @@ void View::add_child(Widget* const widget) { if( widget->parent() == nullptr ) { widget->set_parent(this); children_.push_back(widget); - widget->set_dirty(); } } } @@ -236,14 +242,12 @@ void View::add_children(const std::initializer_list children) { children_.insert(std::end(children_), children); for(auto child : children) { child->set_parent(this); - child->set_dirty(); } } void View::remove_child(Widget* const widget) { if( widget ) { children_.erase(std::remove(children_.begin(), children_.end(), widget), children_.end()); - dirty_overlapping_children_in_rect(widget->screen_rect()); widget->set_parent(nullptr); } } From 9a01d598224f004ab2d5272857d731318ec1b481 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 5 Sep 2016 15:20:50 -0700 Subject: [PATCH 046/100] MenuView: Change add_items arg to initializer_list. Another code size improvement, and makes maintaining lists of menu items less stupid (you don't need to change the template arg when the item count changes). --- firmware/application/ui_debug.cpp | 8 ++++---- firmware/application/ui_menu.hpp | 3 +-- firmware/application/ui_navigation.cpp | 12 ++++++------ firmware/application/ui_setup.cpp | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/firmware/application/ui_debug.cpp b/firmware/application/ui_debug.cpp index a5cc61e6..6e1bdcca 100644 --- a/firmware/application/ui_debug.cpp +++ b/firmware/application/ui_debug.cpp @@ -247,7 +247,7 @@ void RegistersView::focus() { /* DebugPeripheralsMenuView **********************************************/ DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) { - add_items<4>({ { + add_items({ { "RFFC5072", [&nav](){ nav.push( "RFFC5072", RegistersWidgetConfig { 31, 2, 4, 4 }, [](const size_t register_number) { return radio::debug::first_if::register_read(register_number); } @@ -264,20 +264,20 @@ DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) { "WM8731", RegistersWidgetConfig { audio::debug::reg_count(), 1, 3, 4 }, [](const size_t register_number) { return audio::debug::reg_read(register_number); } ); } }, - } }); + }); on_left = [&nav](){ nav.pop(); }; } /* DebugMenuView *********************************************************/ DebugMenuView::DebugMenuView(NavigationView& nav) { - add_items<5>({ { + add_items({ { "Memory", [&nav](){ nav.push(); } }, { "Radio State", [&nav](){ nav.push(); } }, { "SD Card", [&nav](){ nav.push(); } }, { "Peripherals", [&nav](){ nav.push(); } }, { "Temperature", [&nav](){ nav.push(); } }, - } }); + }); on_left = [&nav](){ nav.pop(); }; } diff --git a/firmware/application/ui_menu.hpp b/firmware/application/ui_menu.hpp index 0bd19c11..6873e5da 100644 --- a/firmware/application/ui_menu.hpp +++ b/firmware/application/ui_menu.hpp @@ -71,8 +71,7 @@ public: void add_item(const MenuItem item); - template - void add_items(const std::array& items) { + void add_items(const std::initializer_list items) { for(const auto& item : items) { add_item(item); } diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index b8882620..cc5c40f7 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -170,28 +170,28 @@ void NavigationView::focus() { /* TransceiversMenuView **************************************************/ TranspondersMenuView::TranspondersMenuView(NavigationView& nav) { - add_items<3>({ { + add_items({ { "AIS: Boats", [&nav](){ nav.push(); } }, { "ERT: Utility Meters", [&nav](){ nav.push(); } }, { "TPMS: Cars", [&nav](){ nav.push(); } }, - } }); + }); on_left = [&nav](){ nav.pop(); }; } /* ReceiverMenuView ******************************************************/ ReceiverMenuView::ReceiverMenuView(NavigationView& nav) { - add_items<2>({ { + add_items({ { "Audio", [&nav](){ nav.push(); } }, { "Transponders", [&nav](){ nav.push(); } }, - } }); + }); on_left = [&nav](){ nav.pop(); }; } /* SystemMenuView ********************************************************/ SystemMenuView::SystemMenuView(NavigationView& nav) { - add_items<7>({ { + add_items({ { "Receiver", [&nav](){ nav.push(); } }, { "Capture", [&nav](){ nav.push(); } }, { "Analyze", [&nav](){ nav.push(); } }, @@ -199,7 +199,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) { { "About", [&nav](){ nav.push(); } }, { "Debug", [&nav](){ nav.push(); } }, { "HackRF", [&nav](){ nav.push(); } }, - } }); + }); } /* SystemView ************************************************************/ diff --git a/firmware/application/ui_setup.cpp b/firmware/application/ui_setup.cpp index 2beddf7a..550af2dd 100644 --- a/firmware/application/ui_setup.cpp +++ b/firmware/application/ui_setup.cpp @@ -194,12 +194,12 @@ void AboutView::focus() { } SetupMenuView::SetupMenuView(NavigationView& nav) { - add_items<4>({ { + add_items({ { "Date/Time", [&nav](){ nav.push(); } }, { "Frequency Correction", [&nav](){ nav.push(); } }, { "Antenna Bias Voltage", [&nav](){ nav.push(); } }, { "Touch", [&nav](){ nav.push(); } }, - } }); + }); on_left = [&nav](){ nav.pop(); }; } From 722f9b68861bb48fb0c1d0d01f8edf6cafae483a Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 5 Sep 2016 15:30:45 -0700 Subject: [PATCH 047/100] MenuItem: Misc const and constructor consistency adjustments. Also moved add_items() body to .cpp file. --- firmware/application/ui_menu.cpp | 8 +++++++- firmware/application/ui_menu.hpp | 11 +++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/firmware/application/ui_menu.cpp b/firmware/application/ui_menu.cpp index 4b79cbb1..4a04e817 100644 --- a/firmware/application/ui_menu.cpp +++ b/firmware/application/ui_menu.cpp @@ -69,10 +69,16 @@ MenuView::~MenuView() { } } -void MenuView::add_item(const MenuItem item) { +void MenuView::add_item(MenuItem item) { add_child(new MenuItemView { item }); } +void MenuView::add_items(std::initializer_list items) { + for(auto item : items) { + add_item(item); + } +} + void MenuView::set_parent_rect(const Rect new_parent_rect) { View::set_parent_rect(new_parent_rect); diff --git a/firmware/application/ui_menu.hpp b/firmware/application/ui_menu.hpp index 6873e5da..7d10dbf6 100644 --- a/firmware/application/ui_menu.hpp +++ b/firmware/application/ui_menu.hpp @@ -45,7 +45,7 @@ class MenuItemView : public Widget { public: MenuItemView( MenuItem item - ) : item(item) + ) : item { item } { } @@ -69,13 +69,8 @@ public: ~MenuView(); - void add_item(const MenuItem item); - - void add_items(const std::initializer_list items) { - for(const auto& item : items) { - add_item(item); - } - } + void add_item(MenuItem item); + void add_items(std::initializer_list items); void set_parent_rect(const Rect new_parent_rect) override; From 2740761ed789e9239afa5691e363f88cb75d40e4 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 5 Sep 2016 16:49:44 -0700 Subject: [PATCH 048/100] RecentEntriesX: Move non-templated bits to .cpp. --- firmware/application/recent_entries.cpp | 39 +++++++++++++++++++++++++ firmware/application/recent_entries.hpp | 31 ++------------------ 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/firmware/application/recent_entries.cpp b/firmware/application/recent_entries.cpp index bfb21aee..0521238a 100644 --- a/firmware/application/recent_entries.cpp +++ b/firmware/application/recent_entries.cpp @@ -20,3 +20,42 @@ */ #include "recent_entries.hpp" + +namespace ui { + +RecentEntriesColumns::RecentEntriesColumns( + const std::initializer_list columns +) : _columns { columns } +{ +} + +RecentEntriesHeader::RecentEntriesHeader( + const RecentEntriesColumns& columns +) : _columns { columns } +{ +} + +void RecentEntriesHeader::paint(Painter& painter) { + const auto r = screen_rect(); + const auto& parent_style = style(); + + const Style style { + .font = parent_style.font, + .background = Color::blue(), + .foreground = parent_style.foreground, + }; + + auto p = r.pos; + for(const auto& column : _columns) { + const auto width = column.second; + auto text = column.first; + if( width > text.length() ) { + text.append(width - text.length(), ' '); + } + + painter.draw_string(p, style, text); + p += { static_cast((width * 8) + 8), 0 }; + } +} + +} /* namespace ui */ diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index e4068270..568f5e6c 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -101,9 +101,7 @@ public: RecentEntriesColumns( const std::initializer_list columns - ) : _columns { columns } - { - } + ); ContainerType::const_iterator begin() const { return std::begin(_columns); } ContainerType::const_iterator end() const { return std::end(_columns); } @@ -116,32 +114,9 @@ class RecentEntriesHeader : public Widget { public: RecentEntriesHeader( const RecentEntriesColumns& columns - ) : _columns { columns } - { - } + ); - void paint(Painter& painter) override { - const auto r = screen_rect(); - const auto& parent_style = style(); - - const Style style { - .font = parent_style.font, - .background = Color::blue(), - .foreground = parent_style.foreground, - }; - - auto p = r.pos; - for(const auto& column : _columns) { - const auto width = column.second; - auto text = column.first; - if( width > text.length() ) { - text.append(width - text.length(), ' '); - } - - painter.draw_string(p, style, text); - p += { static_cast((width * 8) + 8), 0 }; - } - } + void paint(Painter& painter) override; private: const RecentEntriesColumns& _columns; From 79330015ed8f0b992f30552bb5ad6684ab903380 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 7 Sep 2016 20:46:45 -0700 Subject: [PATCH 049/100] File: Clean up directory_iterator construction, preserve pattern. FatFs requires pattern pointer to be stable during search. --- firmware/application/file.cpp | 11 ++++++----- firmware/application/file.hpp | 5 +++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index 8c45c8b3..487e53e4 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -126,7 +126,7 @@ Optional File::sync() { static std::filesystem::path find_last_file_matching_pattern(const std::filesystem::path& pattern) { std::filesystem::path last_match; - for(const auto& entry : std::filesystem::directory_iterator(u"", pattern.c_str())) { + for(const auto& entry : std::filesystem::directory_iterator(u"", pattern)) { if( std::filesystem::is_regular_file(entry.status()) ) { const auto match = entry.path(); if( match > last_match ) { @@ -211,11 +211,12 @@ std::string filesystem_error::what() const { } directory_iterator::directory_iterator( - const std::filesystem::path::value_type* path, - const std::filesystem::path::value_type* wild -) { + std::filesystem::path path, + std::filesystem::path wild +) : pattern { wild } +{ impl = std::make_shared(); - const auto result = f_findfirst(&impl->dir, &impl->filinfo, reinterpret_cast(path), reinterpret_cast(wild)); + const auto result = f_findfirst(&impl->dir, &impl->filinfo, reinterpret_cast(path.c_str()), reinterpret_cast(pattern.c_str())); if( result != FR_OK ) { impl.reset(); // TODO: Throw exception if/when I enable exceptions... diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index 64691703..bc004015 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -97,6 +97,7 @@ class directory_iterator { }; std::shared_ptr impl; + const path pattern; friend bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs); @@ -108,8 +109,8 @@ public: using iterator_category = std::input_iterator_tag; directory_iterator() noexcept { }; - directory_iterator(const std::filesystem::path::value_type* path, const std::filesystem::path::value_type* wild); - + directory_iterator(std::filesystem::path path, std::filesystem::path wild); + ~directory_iterator() { } directory_iterator& operator++(); From f722497b01d6e34b8f0f7fbe84dfa3bc83446c1a Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 7 Sep 2016 22:18:11 -0700 Subject: [PATCH 050/100] Rect: operator method to offset by a Point. --- firmware/common/ui.cpp | 5 +++++ firmware/common/ui.hpp | 1 + 2 files changed, 6 insertions(+) diff --git a/firmware/common/ui.cpp b/firmware/common/ui.cpp index 4fd40088..d6d881d6 100644 --- a/firmware/common/ui.cpp +++ b/firmware/common/ui.cpp @@ -57,4 +57,9 @@ Rect& Rect::operator+=(const Rect& p) { return *this; } +Rect& Rect::operator+=(const Point& p) { + pos += p; + return *this; +} + } /* namespace ui */ diff --git a/firmware/common/ui.hpp b/firmware/common/ui.hpp index 0a10a9f2..f363a841 100644 --- a/firmware/common/ui.hpp +++ b/firmware/common/ui.hpp @@ -208,6 +208,7 @@ struct Rect { } Rect& operator+=(const Rect& p); + Rect& operator+=(const Point& p); operator bool() const { return !size.is_empty(); From df0fc30fdae6f42d29dc6b51f6bd9424cd90833b Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 7 Sep 2016 22:19:30 -0700 Subject: [PATCH 051/100] Rect: Comment about a bad API method. In retrospect, I don't like Rect + Rect = union -- it doesn't make as much sense and isn't as readable as Rect.union(Rect). --- firmware/common/ui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firmware/common/ui.cpp b/firmware/common/ui.cpp index d6d881d6..5b0f55b2 100644 --- a/firmware/common/ui.cpp +++ b/firmware/common/ui.cpp @@ -42,6 +42,8 @@ Rect Rect::intersect(const Rect& o) const { } } +// TODO: This violates the principle of least surprise! +// This does a union, but that might not be obvious from "+=" syntax. Rect& Rect::operator+=(const Rect& p) { if( is_empty() ) { *this = p; From f80706cb343f8aca86a8bf81c3f4e6882571481f Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 7 Sep 2016 22:20:51 -0700 Subject: [PATCH 052/100] File: Extract function to convert path to string. --- firmware/application/file.cpp | 7 +++++++ firmware/application/file.hpp | 2 ++ firmware/application/ui_record_view.cpp | 8 +------- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index 487e53e4..6ba485d9 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -22,6 +22,8 @@ #include "file.hpp" #include +#include +#include /* Values added to FatFs FRESULT enum, values outside the FRESULT data type */ static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected."); @@ -210,6 +212,11 @@ std::string filesystem_error::what() const { } } +std::string path_to_string(const path& p) { + std::wstring_convert, path::value_type> conv; + return conv.to_bytes(p); +} + directory_iterator::directory_iterator( std::filesystem::path path, std::filesystem::path wild diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index bc004015..6c6ab0ee 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -70,6 +70,8 @@ using file_status = BYTE; static_assert(sizeof(path::value_type) == 2, "sizeof(std::filesystem::path::value_type) != 2"); static_assert(sizeof(path::value_type) == sizeof(TCHAR), "FatFs TCHAR size != std::filesystem::path::value_type"); +std::string path_to_string(const path& p); + struct space_info { static_assert(sizeof(std::uintmax_t) >= 8, "std::uintmax_t too small ( -#include -#include - class FileWriter : public Writer { public: FileWriter() = default; @@ -289,10 +286,7 @@ void RecordView::start() { }; if( writer ) { - std::wstring_convert, std::filesystem::path::value_type> conv; - const auto filename_stem_s = conv.to_bytes(filename_stem); - - text_record_filename.set(filename_stem_s); + text_record_filename.set(std::filesystem::path_to_string(filename_stem)); button_record.set_bitmap(&bitmap_stop); capture_thread = std::make_unique( std::move(writer), From b87d1456a286f8551d8aa947ab10901762b192e6 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 8 Sep 2016 12:57:34 -0700 Subject: [PATCH 053/100] File: Make path a first-class object, add some methods from C++17. --- firmware/application/file.cpp | 80 +++++++++++++----- firmware/application/file.hpp | 103 ++++++++++++++++++++++-- firmware/application/ui_navigation.cpp | 6 +- firmware/application/ui_record_view.cpp | 16 ++-- 4 files changed, 166 insertions(+), 39 deletions(-) diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index 6ba485d9..e7fec84e 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -139,18 +139,12 @@ static std::filesystem::path find_last_file_matching_pattern(const std::filesyst return last_match; } -static std::filesystem::path remove_filename_extension(const std::filesystem::path& filename) { - const auto extension_index = filename.find_last_of('.'); - return filename.substr(0, extension_index); -} - -static std::filesystem::path increment_filename_stem_ordinal(const std::filesystem::path& filename_stem) { - std::filesystem::path result { filename_stem }; - - auto it = result.rbegin(); +static std::filesystem::path increment_filename_stem_ordinal(std::filesystem::path path) { + auto t = path.replace_extension().native(); + auto it = t.rbegin(); // Increment decimal number before the extension. - for(; it != result.rend(); ++it) { + for(; it != t.rend(); ++it) { const auto c = *it; if( c < '0' ) { return { }; @@ -164,19 +158,18 @@ static std::filesystem::path increment_filename_stem_ordinal(const std::filesyst } } - return result; + return t; } -std::filesystem::path next_filename_stem_matching_pattern(const std::filesystem::path& filename_stem_pattern) { - const auto filename = find_last_file_matching_pattern(filename_stem_pattern + u".*"); - auto filename_stem = remove_filename_extension(filename); - if( filename_stem.empty() ) { - filename_stem = filename_stem_pattern; - std::replace(std::begin(filename_stem), std::end(filename_stem), '?', '0'); +std::filesystem::path next_filename_stem_matching_pattern(std::filesystem::path filename_pattern) { + const auto next_filename = find_last_file_matching_pattern(filename_pattern.replace_extension(u".*")); + if( next_filename.empty() ) { + auto pattern_s = filename_pattern.replace_extension().native(); + std::replace(std::begin(pattern_s), std::end(pattern_s), '?', '0'); + return pattern_s; } else { - filename_stem = increment_filename_stem_ordinal(filename_stem); + return increment_filename_stem_ordinal(next_filename); } - return filename_stem; } namespace std { @@ -212,9 +205,54 @@ std::string filesystem_error::what() const { } } -std::string path_to_string(const path& p) { +path path::extension() const { + const auto t = filename().native(); + const auto index = t.find_last_of(u'.'); + if( index == t.npos ) { + return { }; + } else { + return t.substr(index); + } +} + +path path::filename() const { + const auto index = _s.find_last_of(preferred_separator); + if( index == _s.npos ) { + return _s; + } else { + return _s.substr(index + 1); + } +} + +path path::stem() const { + const auto t = filename().native(); + const auto index = t.find_last_of(u'.'); + if( index == t.npos ) { + return t; + } else { + return t.substr(0, index); + } +} + +std::string path::string() const { std::wstring_convert, path::value_type> conv; - return conv.to_bytes(p); + return conv.to_bytes(native()); +} + +path& path::replace_extension(const path& replacement) { + const auto t = extension().native(); + _s.erase(_s.size() - t.size()); + if( !replacement._s.empty() ) { + if( replacement._s.front() != u'.' ) { + _s += u'.'; + } + _s += replacement._s; + } + return *this; +} + +bool operator>(const path& lhs, const path& rhs) { + return lhs.native() > rhs.native(); } directory_iterator::directory_iterator( diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index 6c6ab0ee..cca7f220 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -64,14 +64,107 @@ private: uint32_t err; }; -using path = std::u16string; +struct path { + using string_type = std::u16string; + using value_type = string_type::value_type; + + static constexpr value_type preferred_separator = u'/'; + + path( + ) : _s { } + { + } + + path( + const path& p + ) : _s { p._s } + { + } + + path( + path&& p + ) : _s { std::move(p._s) } + { + } + + template + path( + const Source& source + ) : path { std::begin(source), std::end(source) } + { + } + + template + path( + InputIt first, + InputIt last + ) : _s { first, last } + { + } + + path( + const char16_t* const s + ) : _s { s } + { + } + + path( + const TCHAR* const s + ) : _s { reinterpret_cast(s) } + { + } + + path& operator=(const path& p) { + _s = p._s; + return *this; + } + + path& operator=(path&& p) { + _s = std::move(p._s); + return *this; + } + + path extension() const; + path filename() const; + path stem() const; + + bool empty() const { + return _s.empty(); + } + + const value_type* c_str() const { + return native().c_str(); + } + + const string_type& native() const { + return _s; + } + + std::string string() const; + + path& operator+=(const path& p) { + _s += p._s; + return *this; + } + + path& operator+=(const string_type& str) { + _s += str; + return *this; + } + + path& replace_extension(const path& replacement = path()); + +private: + string_type _s; +}; + +bool operator>(const path& lhs, const path& rhs); + using file_status = BYTE; static_assert(sizeof(path::value_type) == 2, "sizeof(std::filesystem::path::value_type) != 2"); static_assert(sizeof(path::value_type) == sizeof(TCHAR), "FatFs TCHAR size != std::filesystem::path::value_type"); -std::string path_to_string(const path& p); - struct space_info { static_assert(sizeof(std::uintmax_t) >= 8, "std::uintmax_t too small ((fname); }; + const std::filesystem::path path() const noexcept { return { fname }; }; }; class directory_iterator { @@ -135,7 +228,7 @@ space_info space(const path& p); } /* namespace filesystem */ } /* namespace std */ -std::filesystem::path next_filename_stem_matching_pattern(const std::filesystem::path& filename_stem_pattern); +std::filesystem::path next_filename_stem_matching_pattern(std::filesystem::path filename_stem_pattern); class File { public: diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index cc5c40f7..8572d6d7 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -81,13 +81,13 @@ void SystemStatusView::set_title(const std::string new_value) { } void SystemStatusView::on_camera() { - const auto filename_stem = next_filename_stem_matching_pattern(u"SCR_????"); - if( filename_stem.empty() ) { + auto path = next_filename_stem_matching_pattern(u"SCR_????"); + if( path.empty() ) { return; } PNGWriter png; - auto create_error = png.create(filename_stem + u".PNG"); + auto create_error = png.create(path.replace_extension(u".PNG")); if( create_error.is_valid() ) { return; } diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index 296952d1..0b43ed0f 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -238,8 +238,8 @@ void RecordView::start() { return; } - const auto filename_stem = next_filename_stem_matching_pattern(filename_stem_pattern); - if( filename_stem.empty() ) { + auto base_path = next_filename_stem_matching_pattern(filename_stem_pattern); + if( base_path.empty() ) { return; } @@ -250,9 +250,7 @@ void RecordView::start() { auto p = std::make_unique( sampling_rate ); - auto create_error = p->create( - filename_stem + u".WAV" - ); + auto create_error = p->create(base_path.replace_extension(u".WAV")); if( create_error.is_valid() ) { handle_error(create_error.value()); } else { @@ -263,16 +261,14 @@ void RecordView::start() { case FileType::RawS16: { - const auto metadata_file_error = write_metadata_file(filename_stem + u".TXT"); + const auto metadata_file_error = write_metadata_file(base_path.replace_extension(u".TXT")); if( metadata_file_error.is_valid() ) { handle_error(metadata_file_error.value()); return; } auto p = std::make_unique(); - auto create_error = p->create( - filename_stem + u".C16" - ); + auto create_error = p->create(base_path.replace_extension(u".C16")); if( create_error.is_valid() ) { handle_error(create_error.value()); } else { @@ -286,7 +282,7 @@ void RecordView::start() { }; if( writer ) { - text_record_filename.set(std::filesystem::path_to_string(filename_stem)); + text_record_filename.set(base_path.replace_extension().string()); button_record.set_bitmap(&bitmap_stop); capture_thread = std::make_unique( std::move(writer), From b9e475cbf4b6cc24da95183309c358577cabe3ca Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 23 Sep 2016 12:18:42 -0700 Subject: [PATCH 054/100] Silence CMakeForceCompiler deprecation warning for >=3.6. --- firmware/toolchain-arm-cortex-m.cmake | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/firmware/toolchain-arm-cortex-m.cmake b/firmware/toolchain-arm-cortex-m.cmake index 033fb185..2304f6f8 100644 --- a/firmware/toolchain-arm-cortex-m.cmake +++ b/firmware/toolchain-arm-cortex-m.cmake @@ -23,10 +23,16 @@ set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_PROCESSOR arm) -include(CMakeForceCompiler) +if(CMAKE_VERSION VERSION_LESS 3.6) + include(CMakeForceCompiler) -CMAKE_FORCE_C_COMPILER(arm-none-eabi-gcc GNU) -CMAKE_FORCE_CXX_COMPILER(arm-none-eabi-g++ GNU) + CMAKE_FORCE_C_COMPILER(arm-none-eabi-gcc GNU) + CMAKE_FORCE_CXX_COMPILER(arm-none-eabi-g++ GNU) +else() + set(CMAKE_C_COMPILER arm-none-eabi-gcc) + set(CMAKE_CXX_COMPILER arm-none-eabi-g++) + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +endif() execute_process( COMMAND ${CMAKE_C_COMPILER} -print-file-name=libc.a From 86f672af2b9b7ef645d4ebf325c5803e847202e2 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 1 Oct 2016 10:44:11 -0700 Subject: [PATCH 055/100] File: Add misc useful API from C++17. --- firmware/application/file.cpp | 8 ++++++++ firmware/application/file.hpp | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index e7fec84e..46d5fe81 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -251,6 +251,10 @@ path& path::replace_extension(const path& replacement) { return *this; } +bool operator<(const path& lhs, const path& rhs) { + return lhs.native() < rhs.native(); +} + bool operator>(const path& lhs, const path& rhs) { return lhs.native() > rhs.native(); } @@ -276,6 +280,10 @@ directory_iterator& directory_iterator::operator++() { return *this; } +bool is_directory(const file_status s) { + return (s & AM_DIR); +} + bool is_regular_file(const file_status s) { return !(s & AM_DIR); } diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index cca7f220..57e63fd9 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -158,6 +158,7 @@ private: string_type _s; }; +bool operator<(const path& lhs, const path& rhs); bool operator>(const path& lhs, const path& rhs); using file_status = BYTE; @@ -178,6 +179,10 @@ struct directory_entry : public FILINFO { return fattrib; } + std::uintmax_t size() const { + return fsize; + }; + const std::filesystem::path path() const noexcept { return { fname }; }; }; @@ -221,6 +226,7 @@ inline directory_iterator end(const directory_iterator&) noexcept { return { }; inline bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs) { return lhs.impl != rhs.impl; }; +bool is_directory(const file_status s); bool is_regular_file(const file_status s); space_info space(const path& p); From aed58f2a3f534b786e58034530592da311725b82 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 1 Oct 2016 10:47:21 -0700 Subject: [PATCH 056/100] File: Stop copying path when iterating. TODO: I bet I've made this mistake a billion other places... --- firmware/application/file.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index 46d5fe81..ae846892 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -130,7 +130,7 @@ static std::filesystem::path find_last_file_matching_pattern(const std::filesyst std::filesystem::path last_match; for(const auto& entry : std::filesystem::directory_iterator(u"", pattern)) { if( std::filesystem::is_regular_file(entry.status()) ) { - const auto match = entry.path(); + const auto& match = entry.path(); if( match > last_match ) { last_match = match; } From f756ac4eac4a8c964f6a8270eabb3f5a24013c84 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 3 Oct 2016 11:34:59 -0700 Subject: [PATCH 057/100] CMake: Produce linker .map files for application, basebands. --- firmware/application/CMakeLists.txt | 1 + firmware/baseband/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 23d61736..d481844f 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -322,6 +322,7 @@ add_definitions(${DEFS}) include_directories(. ${INCDIR}) link_directories(${LLIBDIR}) target_link_libraries(${PROJECT_NAME}.elf ${LIBS}) +target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map) add_custom_command( OUTPUT ${PROJECT_NAME}.bin diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index 2a8b1b53..eade87c1 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -258,6 +258,7 @@ macro(DeclareTargets chunk_tag name) include_directories(. ${INCDIR}) link_directories(${LLIBDIR}) target_link_libraries(${PROJECT_NAME}.elf ${LIBS}) + target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map) add_custom_command( OUTPUT ${PROJECT_NAME}.bin ${PROJECT_NAME}.img From 415399594454bfd9e94937e33f474ad2732a67d7 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 3 Oct 2016 11:58:42 -0700 Subject: [PATCH 058/100] CPLD: Invert GCK2 to improve ADC sample timing. --- firmware/application/clock_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index 5ec51aec..c8b75cce 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -219,7 +219,7 @@ constexpr ClockControl::Type si5351_clock_control_ms_src_clkin = ClockControl::M constexpr ClockControls si5351_clock_control_common { ClockControl::CLK_IDRV_6mA | ClockControl::CLK_SRC_MS_Self | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Fractional | ClockControl::CLK_PDN_Power_Off, - ClockControl::CLK_IDRV_6mA | ClockControl::CLK_SRC_MS_Group | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off, + ClockControl::CLK_IDRV_6mA | ClockControl::CLK_SRC_MS_Group | ClockControl::CLK_INV_Invert | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_IDRV_6mA | ClockControl::CLK_SRC_MS_Group | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_IDRV_8mA | ClockControl::CLK_SRC_MS_Self | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_IDRV_8mA | ClockControl::CLK_SRC_MS_Self | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off, From b69a3abcb5d727e6456ef922f430d312c9cf7843 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 3 Oct 2016 12:31:40 -0700 Subject: [PATCH 059/100] Spectrum: Templatize various window functions. --- firmware/baseband/spectrum_collector.cpp | 29 +++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/firmware/baseband/spectrum_collector.cpp b/firmware/baseband/spectrum_collector.cpp index 0d288bee..ebe55a24 100644 --- a/firmware/baseband/spectrum_collector.cpp +++ b/firmware/baseband/spectrum_collector.cpp @@ -103,6 +103,31 @@ void SpectrumCollector::post_message(const buffer_c16_t& data) { } } +template +static typename T::value_type spectrum_window_none(const T& s, const size_t i) { + static_assert(power_of_two(s.size()), "Array size must be power of 2"); + return s[i]; +}; + +template +static typename T::value_type spectrum_window_hamming_3(const T& s, const size_t i) { + static_assert(power_of_two(s.size()), "Array size must be power of 2"); + constexpr size_t mask = s.size() - 1; + // Three point Hamming window. + return s[i] * 0.54f + (s[(i-1) & mask] + s[(i+1) & mask]) * -0.23f; +}; + +template +static typename T::value_type spectrum_window_blackman_3(const T& s, const size_t i) { + static_assert(power_of_two(s.size()), "Array size must be power of 2"); + constexpr size_t mask = s.size() - 1; + // Three term Blackman window. + constexpr float alpha = 0.42f; + constexpr float beta = 0.5f * 0.5f; + constexpr float gamma = 0.08f * 0.05f; + return s[i] * alpha - (s[(i-1) & mask] + s[(i+1) & mask]) * beta + (s[(i-2) & mask] + s[(i+2) & mask]) * gamma; +}; + void SpectrumCollector::update() { // Called from idle thread (after EVT_MASK_SPECTRUM is flagged) if( streaming && channel_spectrum_request_update ) { @@ -114,9 +139,7 @@ void SpectrumCollector::update() { spectrum.channel_filter_pass_frequency = channel_filter_pass_frequency; spectrum.channel_filter_stop_frequency = channel_filter_stop_frequency; for(size_t i=0; i Date: Tue, 4 Oct 2016 09:15:19 -0700 Subject: [PATCH 060/100] Allow modification of StreamBuffer (data values, size). --- firmware/common/message.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index a4030b5f..290d7dd8 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -406,7 +406,7 @@ public: return used_ >= capacity_; } - const void* data() const { + void* data() const { return data_; } @@ -414,6 +414,10 @@ public: return used_; } + void set_size(const size_t value) { + used_ = value; + } + void empty() { used_ = 0; } From 1bdca0fd8d7ae34c5f67cd279d55499ca89ca024 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 4 Oct 2016 09:36:52 -0700 Subject: [PATCH 061/100] Extract (some) Writer classes to separate files. TODO: PNGWriter could probably reuse this stuff too, but... --- firmware/application/capture_thread.hpp | 8 +- firmware/application/io.hpp | 30 ++++++ firmware/application/io_file.hpp | 57 ++++++++++ firmware/application/io_wave.hpp | 130 +++++++++++++++++++++++ firmware/application/ui_record_view.cpp | 132 +----------------------- 5 files changed, 221 insertions(+), 136 deletions(-) create mode 100644 firmware/application/io.hpp create mode 100644 firmware/application/io_file.hpp create mode 100644 firmware/application/io_wave.hpp diff --git a/firmware/application/capture_thread.hpp b/firmware/application/capture_thread.hpp index 6e2bbaa8..e867e4ec 100644 --- a/firmware/application/capture_thread.hpp +++ b/firmware/application/capture_thread.hpp @@ -26,19 +26,13 @@ #include "event_m0.hpp" -#include "file.hpp" +#include "io.hpp" #include "optional.hpp" #include #include #include -class Writer { -public: - virtual File::Result write(const void* const buffer, const File::Size bytes) = 0; - virtual ~Writer() = default; -}; - class CaptureThread { public: CaptureThread( diff --git a/firmware/application/io.hpp b/firmware/application/io.hpp new file mode 100644 index 00000000..6333b001 --- /dev/null +++ b/firmware/application/io.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "file.hpp" + +class Writer { +public: + virtual File::Result write(const void* const buffer, const File::Size bytes) = 0; + virtual ~Writer() = default; +}; diff --git a/firmware/application/io_file.hpp b/firmware/application/io_file.hpp new file mode 100644 index 00000000..65f44826 --- /dev/null +++ b/firmware/application/io_file.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "io.hpp" + +#include "file.hpp" +#include "optional.hpp" + +#include + +class FileWriter : public Writer { +public: + FileWriter() = default; + + FileWriter(const FileWriter&) = delete; + FileWriter& operator=(const FileWriter&) = delete; + FileWriter(FileWriter&& file) = delete; + FileWriter& operator=(FileWriter&&) = delete; + + Optional create(const std::filesystem::path& filename) { + return file.create(filename); + } + + File::Result write(const void* const buffer, const File::Size bytes) override { + auto write_result = file.write(buffer, bytes) ; + if( write_result.is_ok() ) { + bytes_written += write_result.value(); + } + return write_result; + } + +protected: + File file; + uint64_t bytes_written { 0 }; +}; + +using RawFileWriter = FileWriter; diff --git a/firmware/application/io_wave.hpp b/firmware/application/io_wave.hpp new file mode 100644 index 00000000..304d98e7 --- /dev/null +++ b/firmware/application/io_wave.hpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "io_file.hpp" + +#include "file.hpp" +#include "optional.hpp" + +#include +#include + +class WAVFileWriter : public FileWriter { +public: + WAVFileWriter( + size_t sampling_rate + ) : header { sampling_rate } + { + } + + + WAVFileWriter(const WAVFileWriter&) = delete; + WAVFileWriter& operator=(const WAVFileWriter&) = delete; + WAVFileWriter(WAVFileWriter&&) = delete; + WAVFileWriter& operator=(WAVFileWriter&&) = delete; + + ~WAVFileWriter() { + update_header(); + } + + Optional create( + const std::filesystem::path& filename + ) { + const auto create_error = FileWriter::create(filename); + if( create_error.is_valid() ) { + return create_error; + } else { + return update_header(); + } + } + +private: + struct fmt_pcm_t { + constexpr fmt_pcm_t( + const uint32_t sampling_rate + ) : nSamplesPerSec { sampling_rate }, + nAvgBytesPerSec { nSamplesPerSec * nBlockAlign } + { + } + + private: + const uint8_t ckID[4] { 'f', 'm', 't', ' ' }; + const uint32_t cksize { 16 }; + const uint16_t wFormatTag { 0x0001 }; + const uint16_t nChannels { 1 }; + const uint32_t nSamplesPerSec; + const uint32_t nAvgBytesPerSec; + const uint16_t nBlockAlign { 2 }; + const uint16_t wBitsPerSample { 16 }; + }; + + struct data_t { + void set_size(const uint32_t value) { + cksize = value; + } + + private: + const uint8_t ckID[4] { 'd', 'a', 't', 'a' }; + uint32_t cksize { 0 }; + }; + + struct header_t { + constexpr header_t( + const uint32_t sampling_rate + ) : fmt { sampling_rate } + { + } + + void set_data_size(const uint32_t value) { + data.set_size(value); + cksize = sizeof(header_t) + value - 8; + } + + private: + const uint8_t riff_id[4] { 'R', 'I', 'F', 'F' }; + uint32_t cksize { 0 }; + const uint8_t wave_id[4] { 'W', 'A', 'V', 'E' }; + fmt_pcm_t fmt; + data_t data; + }; + + header_t header; + + Optional update_header() { + header.set_data_size(bytes_written); + const auto seek_0_result = file.seek(0); + if( seek_0_result.is_error() ) { + return seek_0_result.error(); + } + const auto old_position = seek_0_result.value(); + const auto write_result = file.write(&header, sizeof(header)); + if( write_result.is_error() ) { + return write_result.error(); + } + const auto seek_old_result = file.seek(old_position); + if( seek_old_result.is_error() ) { + return seek_old_result.error(); + } + return { }; + } +}; diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index 0b43ed0f..60ff0ddb 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -24,7 +24,9 @@ #include "portapack.hpp" using namespace portapack; -#include "file.hpp" +#include "io_file.hpp" +#include "io_wave.hpp" + #include "rtc_time.hpp" #include "string_format.hpp" @@ -32,134 +34,6 @@ using namespace portapack; #include -class FileWriter : public Writer { -public: - FileWriter() = default; - - FileWriter(const FileWriter&) = delete; - FileWriter& operator=(const FileWriter&) = delete; - FileWriter(FileWriter&& file) = delete; - FileWriter& operator=(FileWriter&&) = delete; - - Optional create(const std::filesystem::path& filename) { - return file.create(filename); - } - - File::Result write(const void* const buffer, const File::Size bytes) override { - auto write_result = file.write(buffer, bytes) ; - if( write_result.is_ok() ) { - bytes_written += write_result.value(); - } - return write_result; - } - -protected: - File file; - uint64_t bytes_written { 0 }; -}; - -using RawFileWriter = FileWriter; - -class WAVFileWriter : public FileWriter { -public: - WAVFileWriter( - size_t sampling_rate - ) : header { sampling_rate } - { - } - - - WAVFileWriter(const WAVFileWriter&) = delete; - WAVFileWriter& operator=(const WAVFileWriter&) = delete; - WAVFileWriter(WAVFileWriter&&) = delete; - WAVFileWriter& operator=(WAVFileWriter&&) = delete; - - ~WAVFileWriter() { - update_header(); - } - - Optional create( - const std::filesystem::path& filename - ) { - const auto create_error = FileWriter::create(filename); - if( create_error.is_valid() ) { - return create_error; - } else { - return update_header(); - } - } - -private: - struct fmt_pcm_t { - constexpr fmt_pcm_t( - const uint32_t sampling_rate - ) : nSamplesPerSec { sampling_rate }, - nAvgBytesPerSec { nSamplesPerSec * nBlockAlign } - { - } - - private: - const uint8_t ckID[4] { 'f', 'm', 't', ' ' }; - const uint32_t cksize { 16 }; - const uint16_t wFormatTag { 0x0001 }; - const uint16_t nChannels { 1 }; - const uint32_t nSamplesPerSec; - const uint32_t nAvgBytesPerSec; - const uint16_t nBlockAlign { 2 }; - const uint16_t wBitsPerSample { 16 }; - }; - - struct data_t { - void set_size(const uint32_t value) { - cksize = value; - } - - private: - const uint8_t ckID[4] { 'd', 'a', 't', 'a' }; - uint32_t cksize { 0 }; - }; - - struct header_t { - constexpr header_t( - const uint32_t sampling_rate - ) : fmt { sampling_rate } - { - } - - void set_data_size(const uint32_t value) { - data.set_size(value); - cksize = sizeof(header_t) + value - 8; - } - - private: - const uint8_t riff_id[4] { 'R', 'I', 'F', 'F' }; - uint32_t cksize { 0 }; - const uint8_t wave_id[4] { 'W', 'A', 'V', 'E' }; - fmt_pcm_t fmt; - data_t data; - }; - - header_t header; - - Optional update_header() { - header.set_data_size(bytes_written); - const auto seek_0_result = file.seek(0); - if( seek_0_result.is_error() ) { - return seek_0_result.error(); - } - const auto old_position = seek_0_result.value(); - const auto write_result = file.write(&header, sizeof(header)); - if( write_result.is_error() ) { - return write_result.error(); - } - const auto seek_old_result = file.seek(old_position); - if( seek_old_result.is_error() ) { - return seek_old_result.error(); - } - return { }; - } -}; - namespace ui { RecordView::RecordView( From 1a2fd3e127045f2a5cb5ffe8f9317e78609b63db Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 4 Oct 2016 09:59:47 -0700 Subject: [PATCH 062/100] Prepare WAV structures for extraction. --- firmware/application/io_wave.hpp | 56 ++++++++++++------------- firmware/application/ui_record_view.cpp | 6 +-- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/firmware/application/io_wave.hpp b/firmware/application/io_wave.hpp index 304d98e7..d315aadc 100644 --- a/firmware/application/io_wave.hpp +++ b/firmware/application/io_wave.hpp @@ -31,12 +31,7 @@ class WAVFileWriter : public FileWriter { public: - WAVFileWriter( - size_t sampling_rate - ) : header { sampling_rate } - { - } - + WAVFileWriter() = default; WAVFileWriter(const WAVFileWriter&) = delete; WAVFileWriter& operator=(const WAVFileWriter&) = delete; @@ -48,8 +43,10 @@ public: } Optional create( - const std::filesystem::path& filename + const std::filesystem::path& filename, + size_t sampling_rate ) { + sampling_rate = sampling_rate; const auto create_error = FileWriter::create(filename); if( create_error.is_valid() ) { return create_error; @@ -68,50 +65,51 @@ private: } private: - const uint8_t ckID[4] { 'f', 'm', 't', ' ' }; - const uint32_t cksize { 16 }; - const uint16_t wFormatTag { 0x0001 }; - const uint16_t nChannels { 1 }; - const uint32_t nSamplesPerSec; - const uint32_t nAvgBytesPerSec; - const uint16_t nBlockAlign { 2 }; - const uint16_t wBitsPerSample { 16 }; + uint8_t ckID[4] { 'f', 'm', 't', ' ' }; + uint32_t cksize { 16 }; + uint16_t wFormatTag { 0x0001 }; + uint16_t nChannels { 1 }; + uint32_t nSamplesPerSec; + uint32_t nAvgBytesPerSec; + uint16_t nBlockAlign { 2 }; + uint16_t wBitsPerSample { 16 }; }; struct data_t { - void set_size(const uint32_t value) { - cksize = value; + constexpr data_t( + const uint32_t size + ) : cksize { size } + { } private: - const uint8_t ckID[4] { 'd', 'a', 't', 'a' }; + uint8_t ckID[4] { 'd', 'a', 't', 'a' }; uint32_t cksize { 0 }; }; struct header_t { constexpr header_t( - const uint32_t sampling_rate - ) : fmt { sampling_rate } + const uint32_t sampling_rate, + const uint32_t data_chunk_size + ) : cksize { sizeof(header_t) + data_chunk_size - 8 }, + fmt { sampling_rate }, + data { data_chunk_size } { } - void set_data_size(const uint32_t value) { - data.set_size(value); - cksize = sizeof(header_t) + value - 8; - } - private: - const uint8_t riff_id[4] { 'R', 'I', 'F', 'F' }; + uint8_t riff_id[4] { 'R', 'I', 'F', 'F' }; uint32_t cksize { 0 }; - const uint8_t wave_id[4] { 'W', 'A', 'V', 'E' }; + uint8_t wave_id[4] { 'W', 'A', 'V', 'E' }; fmt_pcm_t fmt; data_t data; }; - header_t header; + uint32_t sampling_rate; + uint32_t bytes_written; Optional update_header() { - header.set_data_size(bytes_written); + header_t header { sampling_rate, bytes_written }; const auto seek_0_result = file.seek(0); if( seek_0_result.is_error() ) { return seek_0_result.error(); diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index 60ff0ddb..48d48659 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -121,10 +121,8 @@ void RecordView::start() { switch(file_type) { case FileType::WAV: { - auto p = std::make_unique( - sampling_rate - ); - auto create_error = p->create(base_path.replace_extension(u".WAV")); + auto p = std::make_unique(); + auto create_error = p->create(base_path.replace_extension(u".WAV"), sampling_rate); if( create_error.is_valid() ) { handle_error(create_error.value()); } else { From 43c4584a3264cf52c929114dca0c0f25ce97d356 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 4 Oct 2016 10:04:38 -0700 Subject: [PATCH 063/100] Move WAV structs outside of writer. --- firmware/application/io_wave.hpp | 98 ++++++++++++++++---------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/firmware/application/io_wave.hpp b/firmware/application/io_wave.hpp index d315aadc..186b2fb2 100644 --- a/firmware/application/io_wave.hpp +++ b/firmware/application/io_wave.hpp @@ -29,6 +29,55 @@ #include #include +struct fmt_pcm_t { + constexpr fmt_pcm_t( + const uint32_t sampling_rate + ) : nSamplesPerSec { sampling_rate }, + nAvgBytesPerSec { nSamplesPerSec * nBlockAlign } + { + } + +private: + uint8_t ckID[4] { 'f', 'm', 't', ' ' }; + uint32_t cksize { 16 }; + uint16_t wFormatTag { 0x0001 }; + uint16_t nChannels { 1 }; + uint32_t nSamplesPerSec; + uint32_t nAvgBytesPerSec; + uint16_t nBlockAlign { 2 }; + uint16_t wBitsPerSample { 16 }; +}; + +struct data_t { + constexpr data_t( + const uint32_t size + ) : cksize { size } + { + } + +private: + uint8_t ckID[4] { 'd', 'a', 't', 'a' }; + uint32_t cksize { 0 }; +}; + +struct header_t { + constexpr header_t( + const uint32_t sampling_rate, + const uint32_t data_chunk_size + ) : cksize { sizeof(header_t) + data_chunk_size - 8 }, + fmt { sampling_rate }, + data { data_chunk_size } + { + } + +private: + uint8_t riff_id[4] { 'R', 'I', 'F', 'F' }; + uint32_t cksize { 0 }; + uint8_t wave_id[4] { 'W', 'A', 'V', 'E' }; + fmt_pcm_t fmt; + data_t data; +}; + class WAVFileWriter : public FileWriter { public: WAVFileWriter() = default; @@ -56,55 +105,6 @@ public: } private: - struct fmt_pcm_t { - constexpr fmt_pcm_t( - const uint32_t sampling_rate - ) : nSamplesPerSec { sampling_rate }, - nAvgBytesPerSec { nSamplesPerSec * nBlockAlign } - { - } - - private: - uint8_t ckID[4] { 'f', 'm', 't', ' ' }; - uint32_t cksize { 16 }; - uint16_t wFormatTag { 0x0001 }; - uint16_t nChannels { 1 }; - uint32_t nSamplesPerSec; - uint32_t nAvgBytesPerSec; - uint16_t nBlockAlign { 2 }; - uint16_t wBitsPerSample { 16 }; - }; - - struct data_t { - constexpr data_t( - const uint32_t size - ) : cksize { size } - { - } - - private: - uint8_t ckID[4] { 'd', 'a', 't', 'a' }; - uint32_t cksize { 0 }; - }; - - struct header_t { - constexpr header_t( - const uint32_t sampling_rate, - const uint32_t data_chunk_size - ) : cksize { sizeof(header_t) + data_chunk_size - 8 }, - fmt { sampling_rate }, - data { data_chunk_size } - { - } - - private: - uint8_t riff_id[4] { 'R', 'I', 'F', 'F' }; - uint32_t cksize { 0 }; - uint8_t wave_id[4] { 'W', 'A', 'V', 'E' }; - fmt_pcm_t fmt; - data_t data; - }; - uint32_t sampling_rate; uint32_t bytes_written; From f3bfd50399be5068672542348c10e198aac8ad56 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 4 Oct 2016 10:12:10 -0700 Subject: [PATCH 064/100] Move IO functions into .cpp files. --- firmware/application/CMakeLists.txt | 2 ++ firmware/application/io_file.cpp | 30 ++++++++++++++++ firmware/application/io_file.hpp | 10 ++---- firmware/application/io_wave.cpp | 53 +++++++++++++++++++++++++++++ firmware/application/io_wave.hpp | 28 ++------------- 5 files changed, 89 insertions(+), 34 deletions(-) create mode 100644 firmware/application/io_file.cpp create mode 100644 firmware/application/io_wave.cpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index d481844f..2af3460e 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -175,6 +175,8 @@ set(CPPSRC log_file.cpp ${COMMON}/png_writer.cpp capture_thread.cpp + io_file.cpp + io_wave.cpp ${COMMON}/manchester.cpp string_format.cpp temperature_logger.cpp diff --git a/firmware/application/io_file.cpp b/firmware/application/io_file.cpp new file mode 100644 index 00000000..7daa75bc --- /dev/null +++ b/firmware/application/io_file.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "io_file.hpp" + +File::Result FileWriter::write(const void* const buffer, const File::Size bytes) { + auto write_result = file.write(buffer, bytes) ; + if( write_result.is_ok() ) { + bytes_written += write_result.value(); + } + return write_result; +} diff --git a/firmware/application/io_file.hpp b/firmware/application/io_file.hpp index 65f44826..f0647caf 100644 --- a/firmware/application/io_file.hpp +++ b/firmware/application/io_file.hpp @@ -41,14 +41,8 @@ public: return file.create(filename); } - File::Result write(const void* const buffer, const File::Size bytes) override { - auto write_result = file.write(buffer, bytes) ; - if( write_result.is_ok() ) { - bytes_written += write_result.value(); - } - return write_result; - } - + File::Result write(const void* const buffer, const File::Size bytes) override; + protected: File file; uint64_t bytes_written { 0 }; diff --git a/firmware/application/io_wave.cpp b/firmware/application/io_wave.cpp new file mode 100644 index 00000000..5222c3f9 --- /dev/null +++ b/firmware/application/io_wave.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "io_wave.hpp" + +Optional WAVFileWriter::create( + const std::filesystem::path& filename, + size_t sampling_rate +) { + sampling_rate = sampling_rate; + const auto create_error = FileWriter::create(filename); + if( create_error.is_valid() ) { + return create_error; + } else { + return update_header(); + } +} + +Optional WAVFileWriter::update_header() { + header_t header { sampling_rate, bytes_written }; + const auto seek_0_result = file.seek(0); + if( seek_0_result.is_error() ) { + return seek_0_result.error(); + } + const auto old_position = seek_0_result.value(); + const auto write_result = file.write(&header, sizeof(header)); + if( write_result.is_error() ) { + return write_result.error(); + } + const auto seek_old_result = file.seek(old_position); + if( seek_old_result.is_error() ) { + return seek_old_result.error(); + } + return { }; +} diff --git a/firmware/application/io_wave.hpp b/firmware/application/io_wave.hpp index 186b2fb2..e048be8e 100644 --- a/firmware/application/io_wave.hpp +++ b/firmware/application/io_wave.hpp @@ -94,35 +94,11 @@ public: Optional create( const std::filesystem::path& filename, size_t sampling_rate - ) { - sampling_rate = sampling_rate; - const auto create_error = FileWriter::create(filename); - if( create_error.is_valid() ) { - return create_error; - } else { - return update_header(); - } - } + ); private: uint32_t sampling_rate; uint32_t bytes_written; - Optional update_header() { - header_t header { sampling_rate, bytes_written }; - const auto seek_0_result = file.seek(0); - if( seek_0_result.is_error() ) { - return seek_0_result.error(); - } - const auto old_position = seek_0_result.value(); - const auto write_result = file.write(&header, sizeof(header)); - if( write_result.is_error() ) { - return write_result.error(); - } - const auto seek_old_result = file.seek(old_position); - if( seek_old_result.is_error() ) { - return seek_old_result.error(); - } - return { }; - } + Optional update_header(); }; From 01320d9806bb5de655d6148d04bcb889eb0973d1 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 4 Oct 2016 17:04:49 -0700 Subject: [PATCH 065/100] Add a Reader interface. --- firmware/application/io.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/firmware/application/io.hpp b/firmware/application/io.hpp index 6333b001..8e21d19f 100644 --- a/firmware/application/io.hpp +++ b/firmware/application/io.hpp @@ -23,6 +23,12 @@ #include "file.hpp" +class Reader { +public: + virtual File::Result read(void* const buffer, const File::Size bytes) = 0; + virtual ~Reader() = default; +}; + class Writer { public: virtual File::Result write(const void* const buffer, const File::Size bytes) = 0; From a5793b8b9d0b9558a171866807372649e45f2e1d Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 4 Oct 2016 17:13:21 -0700 Subject: [PATCH 066/100] Put Reader, Writer inside "stream" namespace. --- firmware/application/capture_thread.cpp | 2 +- firmware/application/capture_thread.hpp | 4 ++-- firmware/application/io.hpp | 4 ++++ firmware/application/io_file.hpp | 2 +- firmware/application/ui_record_view.cpp | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/firmware/application/capture_thread.cpp b/firmware/application/capture_thread.cpp index 4de5cd53..a5751ac7 100644 --- a/firmware/application/capture_thread.cpp +++ b/firmware/application/capture_thread.cpp @@ -75,7 +75,7 @@ StreamOutput::~StreamOutput() { Thread* CaptureThread::thread = nullptr; CaptureThread::CaptureThread( - std::unique_ptr writer, + std::unique_ptr writer, size_t write_size, size_t buffer_count, std::function success_callback, diff --git a/firmware/application/capture_thread.hpp b/firmware/application/capture_thread.hpp index e867e4ec..01c45b2e 100644 --- a/firmware/application/capture_thread.hpp +++ b/firmware/application/capture_thread.hpp @@ -36,7 +36,7 @@ class CaptureThread { public: CaptureThread( - std::unique_ptr writer, + std::unique_ptr writer, size_t write_size, size_t buffer_count, std::function success_callback, @@ -54,7 +54,7 @@ private: static constexpr auto event_mask_loop_wake = EVENT_MASK(0); CaptureConfig config; - std::unique_ptr writer; + std::unique_ptr writer; std::function success_callback; std::function error_callback; static Thread* thread; diff --git a/firmware/application/io.hpp b/firmware/application/io.hpp index 8e21d19f..5c29da9d 100644 --- a/firmware/application/io.hpp +++ b/firmware/application/io.hpp @@ -23,6 +23,8 @@ #include "file.hpp" +namespace stream { + class Reader { public: virtual File::Result read(void* const buffer, const File::Size bytes) = 0; @@ -34,3 +36,5 @@ public: virtual File::Result write(const void* const buffer, const File::Size bytes) = 0; virtual ~Writer() = default; }; + +} /* namespace stream */ diff --git a/firmware/application/io_file.hpp b/firmware/application/io_file.hpp index f0647caf..c9049dde 100644 --- a/firmware/application/io_file.hpp +++ b/firmware/application/io_file.hpp @@ -28,7 +28,7 @@ #include -class FileWriter : public Writer { +class FileWriter : public stream::Writer { public: FileWriter() = default; diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index 48d48659..d617ac39 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -117,7 +117,7 @@ void RecordView::start() { return; } - std::unique_ptr writer; + std::unique_ptr writer; switch(file_type) { case FileType::WAV: { From 2433ea30ad4175c47204ad676713a50cac4e49b8 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 4 Oct 2016 22:17:27 -0700 Subject: [PATCH 067/100] Generalize StreamOutput a bit. --- firmware/application/capture_thread.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/firmware/application/capture_thread.cpp b/firmware/application/capture_thread.cpp index a5751ac7..08af9e4a 100644 --- a/firmware/application/capture_thread.cpp +++ b/firmware/application/capture_thread.cpp @@ -30,19 +30,25 @@ public: StreamOutput(CaptureConfig* const config); ~StreamOutput(); - size_t available() { - return fifo_buffers_full->len(); + StreamBuffer* get_empty() { + StreamBuffer* p { nullptr }; + fifo_buffers_empty->out(p); + return p; } - StreamBuffer* get_buffer() { + bool put_empty(StreamBuffer* const p) { + p->empty(); + return fifo_buffers_empty->in(p); + } + + StreamBuffer* get_full() { StreamBuffer* p { nullptr }; fifo_buffers_full->out(p); return p; } - bool release_buffer(StreamBuffer* const p) { - p->empty(); - return fifo_buffers_empty->in(p); + bool put_full(StreamBuffer* const p) { + return fifo_buffers_full->in(p); } static FIFO* fifo_buffers_empty; @@ -126,13 +132,13 @@ Optional CaptureThread::run() { StreamOutput stream { &config }; while( !chThdShouldTerminate() ) { - if( stream.available() ) { - auto buffer = stream.get_buffer(); + auto buffer = stream.get_full(); + if( buffer ) { auto write_result = writer->write(buffer->data(), buffer->size()); if( write_result.is_error() ) { return write_result.error(); } - stream.release_buffer(buffer); + stream.put_empty(buffer); } else { chEvtWaitAny(event_mask_loop_wake); } From fadbbcc5816eeb35903aac595c196a9dd54790c4 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 4 Oct 2016 22:17:57 -0700 Subject: [PATCH 068/100] Move buffer reset out of buffer exchange class. --- firmware/application/capture_thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/application/capture_thread.cpp b/firmware/application/capture_thread.cpp index 08af9e4a..c2fe4487 100644 --- a/firmware/application/capture_thread.cpp +++ b/firmware/application/capture_thread.cpp @@ -37,7 +37,6 @@ public: } bool put_empty(StreamBuffer* const p) { - p->empty(); return fifo_buffers_empty->in(p); } @@ -138,6 +137,7 @@ Optional CaptureThread::run() { if( write_result.is_error() ) { return write_result.error(); } + buffer->empty(); stream.put_empty(buffer); } else { chEvtWaitAny(event_mask_loop_wake); From 84334ef818653864b976d870eda0ada0194b37a8 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 4 Oct 2016 22:52:12 -0700 Subject: [PATCH 069/100] Further generalize StreamOutput -> BufferExchange. --- firmware/application/capture_thread.cpp | 56 +++++++++++++------------ 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/firmware/application/capture_thread.cpp b/firmware/application/capture_thread.cpp index c2fe4487..0ac35a60 100644 --- a/firmware/application/capture_thread.cpp +++ b/firmware/application/capture_thread.cpp @@ -23,55 +23,59 @@ #include "baseband_api.hpp" -// StreamOutput /////////////////////////////////////////////////////////// +// BufferExchange /////////////////////////////////////////////////////////// -class StreamOutput { +class BufferExchange { public: - StreamOutput(CaptureConfig* const config); - ~StreamOutput(); + BufferExchange(CaptureConfig* const config); + ~BufferExchange(); - StreamBuffer* get_empty() { +#if defined(LPC43XX_M0) + StreamBuffer* get() { StreamBuffer* p { nullptr }; - fifo_buffers_empty->out(p); + fifo_buffers_for_application->out(p); return p; } - bool put_empty(StreamBuffer* const p) { - return fifo_buffers_empty->in(p); + bool put(StreamBuffer* const p) { + return fifo_buffers_for_baseband->in(p); } +#endif - StreamBuffer* get_full() { +#if defined(LPC43XX_M4) + StreamBuffer* get() { StreamBuffer* p { nullptr }; - fifo_buffers_full->out(p); + fifo_buffers_for_baseband->out(p); return p; } - bool put_full(StreamBuffer* const p) { - return fifo_buffers_full->in(p); + bool put(StreamBuffer* const p) { + return fifo_buffers_for_application->in(p); } +#endif - static FIFO* fifo_buffers_empty; - static FIFO* fifo_buffers_full; + static FIFO* fifo_buffers_for_baseband; + static FIFO* fifo_buffers_for_application; private: CaptureConfig* const config; }; -FIFO* StreamOutput::fifo_buffers_empty = nullptr; -FIFO* StreamOutput::fifo_buffers_full = nullptr; +FIFO* BufferExchange::fifo_buffers_for_baseband = nullptr; +FIFO* BufferExchange::fifo_buffers_for_application = nullptr; -StreamOutput::StreamOutput( +BufferExchange::BufferExchange( CaptureConfig* const config ) : config { config } { baseband::capture_start(config); - fifo_buffers_empty = config->fifo_buffers_empty; - fifo_buffers_full = config->fifo_buffers_full; + fifo_buffers_for_baseband = config->fifo_buffers_empty; + fifo_buffers_for_application = config->fifo_buffers_full; } -StreamOutput::~StreamOutput() { - fifo_buffers_full = nullptr; - fifo_buffers_empty = nullptr; +BufferExchange::~BufferExchange() { + fifo_buffers_for_baseband = nullptr; + fifo_buffers_for_application = nullptr; baseband::capture_stop(); } @@ -106,7 +110,7 @@ CaptureThread::~CaptureThread() { void CaptureThread::check_fifo_isr() { // TODO: Prevent over-signalling by transmitting a set of // flags from the baseband core. - const auto fifo = StreamOutput::fifo_buffers_full; + const auto fifo = BufferExchange::fifo_buffers_for_application; if( fifo ) { if( !fifo->is_empty() ) { chEvtSignalI(thread, event_mask_loop_wake); @@ -128,17 +132,17 @@ msg_t CaptureThread::static_fn(void* arg) { } Optional CaptureThread::run() { - StreamOutput stream { &config }; + BufferExchange buffers { &config }; while( !chThdShouldTerminate() ) { - auto buffer = stream.get_full(); + auto buffer = buffers.get(); if( buffer ) { auto write_result = writer->write(buffer->data(), buffer->size()); if( write_result.is_error() ) { return write_result.error(); } buffer->empty(); - stream.put_empty(buffer); + buffers.put(buffer); } else { chEvtWaitAny(event_mask_loop_wake); } From 5dfb53263ac749605fb6745a1f41f0da6a4caeff Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 6 Oct 2016 13:38:56 -0700 Subject: [PATCH 070/100] Extract BufferExchange, simplify threading. --- firmware/application/CMakeLists.txt | 1 + firmware/application/capture_thread.cpp | 86 ++++------------------- firmware/application/capture_thread.hpp | 6 +- firmware/application/event_m0.cpp | 4 +- firmware/common/buffer_exchange.cpp | 54 +++++++++++++++ firmware/common/buffer_exchange.hpp | 91 +++++++++++++++++++++++++ 6 files changed, 161 insertions(+), 81 deletions(-) create mode 100644 firmware/common/buffer_exchange.cpp create mode 100644 firmware/common/buffer_exchange.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 2af3460e..81e1d4bd 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -174,6 +174,7 @@ set(CPPSRC file.cpp log_file.cpp ${COMMON}/png_writer.cpp + ${COMMON}/buffer_exchange.cpp capture_thread.cpp io_file.cpp io_wave.cpp diff --git a/firmware/application/capture_thread.cpp b/firmware/application/capture_thread.cpp index 0ac35a60..50558a44 100644 --- a/firmware/application/capture_thread.cpp +++ b/firmware/application/capture_thread.cpp @@ -22,67 +22,20 @@ #include "capture_thread.hpp" #include "baseband_api.hpp" +#include "buffer_exchange.hpp" -// BufferExchange /////////////////////////////////////////////////////////// - -class BufferExchange { -public: - BufferExchange(CaptureConfig* const config); - ~BufferExchange(); - -#if defined(LPC43XX_M0) - StreamBuffer* get() { - StreamBuffer* p { nullptr }; - fifo_buffers_for_application->out(p); - return p; +struct BasebandCapture { + BasebandCapture(CaptureConfig* const config) { + baseband::capture_start(config); } - bool put(StreamBuffer* const p) { - return fifo_buffers_for_baseband->in(p); + ~BasebandCapture() { + baseband::capture_stop(); } -#endif - -#if defined(LPC43XX_M4) - StreamBuffer* get() { - StreamBuffer* p { nullptr }; - fifo_buffers_for_baseband->out(p); - return p; - } - - bool put(StreamBuffer* const p) { - return fifo_buffers_for_application->in(p); - } -#endif - - static FIFO* fifo_buffers_for_baseband; - static FIFO* fifo_buffers_for_application; - -private: - CaptureConfig* const config; }; -FIFO* BufferExchange::fifo_buffers_for_baseband = nullptr; -FIFO* BufferExchange::fifo_buffers_for_application = nullptr; - -BufferExchange::BufferExchange( - CaptureConfig* const config -) : config { config } -{ - baseband::capture_start(config); - fifo_buffers_for_baseband = config->fifo_buffers_empty; - fifo_buffers_for_application = config->fifo_buffers_full; -} - -BufferExchange::~BufferExchange() { - fifo_buffers_for_baseband = nullptr; - fifo_buffers_for_application = nullptr; - baseband::capture_stop(); -} - // CaptureThread ////////////////////////////////////////////////////////// -Thread* CaptureThread::thread = nullptr; - CaptureThread::CaptureThread( std::unique_ptr writer, size_t write_size, @@ -101,23 +54,11 @@ CaptureThread::CaptureThread( CaptureThread::~CaptureThread() { if( thread ) { chThdTerminate(thread); - chEvtSignal(thread, event_mask_loop_wake); chThdWait(thread); thread = nullptr; } } -void CaptureThread::check_fifo_isr() { - // TODO: Prevent over-signalling by transmitting a set of - // flags from the baseband core. - const auto fifo = BufferExchange::fifo_buffers_for_application; - if( fifo ) { - if( !fifo->is_empty() ) { - chEvtSignalI(thread, event_mask_loop_wake); - } - } -} - msg_t CaptureThread::static_fn(void* arg) { auto obj = static_cast(arg); const auto error = obj->run(); @@ -132,20 +73,17 @@ msg_t CaptureThread::static_fn(void* arg) { } Optional CaptureThread::run() { + BasebandCapture capture { &config }; BufferExchange buffers { &config }; while( !chThdShouldTerminate() ) { auto buffer = buffers.get(); - if( buffer ) { - auto write_result = writer->write(buffer->data(), buffer->size()); - if( write_result.is_error() ) { - return write_result.error(); - } - buffer->empty(); - buffers.put(buffer); - } else { - chEvtWaitAny(event_mask_loop_wake); + auto write_result = writer->write(buffer->data(), buffer->size()); + if( write_result.is_error() ) { + return write_result.error(); } + buffer->empty(); + buffers.put(buffer); } return { }; diff --git a/firmware/application/capture_thread.hpp b/firmware/application/capture_thread.hpp index 01c45b2e..5a4398b0 100644 --- a/firmware/application/capture_thread.hpp +++ b/firmware/application/capture_thread.hpp @@ -48,16 +48,12 @@ public: return config; } - static void check_fifo_isr(); - private: - static constexpr auto event_mask_loop_wake = EVENT_MASK(0); - CaptureConfig config; std::unique_ptr writer; std::function success_callback; std::function error_callback; - static Thread* thread; + Thread* thread; static msg_t static_fn(void* arg); diff --git a/firmware/application/event_m0.cpp b/firmware/application/event_m0.cpp index a7fae4a3..bb32041c 100644 --- a/firmware/application/event_m0.cpp +++ b/firmware/application/event_m0.cpp @@ -31,7 +31,7 @@ #include "irq_controls.hpp" -#include "capture_thread.hpp" +#include "buffer_exchange.hpp" #include "ch.h" @@ -48,7 +48,7 @@ CH_IRQ_HANDLER(M4Core_IRQHandler) { CH_IRQ_PROLOGUE(); chSysLockFromIsr(); - CaptureThread::check_fifo_isr(); + BufferExchange::handle_isr(); EventDispatcher::check_fifo_isr(); chSysUnlockFromIsr(); diff --git a/firmware/common/buffer_exchange.cpp b/firmware/common/buffer_exchange.cpp new file mode 100644 index 00000000..37bc47f1 --- /dev/null +++ b/firmware/common/buffer_exchange.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "buffer_exchange.hpp" + +BufferExchange* BufferExchange::obj { nullptr }; + +BufferExchange::BufferExchange( + CaptureConfig* const config +) : config { config } +{ + obj = this; + fifo_buffers_for_baseband = config->fifo_buffers_empty; + fifo_buffers_for_application = config->fifo_buffers_full; +} + +BufferExchange::~BufferExchange() { + obj = nullptr; + fifo_buffers_for_baseband = nullptr; + fifo_buffers_for_application = nullptr; +} + +StreamBuffer* BufferExchange::get(FIFO* fifo) { + while(true) { + StreamBuffer* p { nullptr }; + fifo->out(p); + if( p ) { + return p; + } + + chSysLock(); + thread = chThdSelf(); + chSchGoSleepS(THD_STATE_SUSPENDED); + chSysUnlock(); + } +} diff --git a/firmware/common/buffer_exchange.hpp b/firmware/common/buffer_exchange.hpp new file mode 100644 index 00000000..091a6ea2 --- /dev/null +++ b/firmware/common/buffer_exchange.hpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "ch.h" + +#include "message.hpp" +#include "fifo.hpp" +#include "io.hpp" + +class BufferExchange { +public: + BufferExchange(CaptureConfig* const config); + ~BufferExchange(); + +#if defined(LPC43XX_M0) + bool empty() const { + return fifo_buffers_for_application->is_empty(); + } + + StreamBuffer* get() { + return get(fifo_buffers_for_application); + } + + bool put(StreamBuffer* const p) { + return fifo_buffers_for_baseband->in(p); + } +#endif + +#if defined(LPC43XX_M4) + bool empty() const { + return fifo_buffers_for_baseband->is_empty(); + } + + StreamBuffer* get() { + return get(fifo_buffers_for_baseband); + } + + bool put(StreamBuffer* const p) { + return fifo_buffers_for_application->in(p); + } +#endif + + static void handle_isr() { + if( obj ) { + obj->check_fifo_isr(); + } + } + +private: + CaptureConfig* const config; + FIFO* fifo_buffers_for_baseband; + FIFO* fifo_buffers_for_application; + Thread* thread; + static BufferExchange* obj; + + void check_fifo_isr() { + if( !empty() ) { + wakeup_isr(); + } + } + + void wakeup_isr() { + auto thread_tmp = thread; + if( thread_tmp ) { + thread = nullptr; + chSchReadyI(thread_tmp); + } + } + + StreamBuffer* get(FIFO* fifo); +}; From a33476259e07891b6abe02f161c08061d4bc697e Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 24 Oct 2016 11:16:48 -0700 Subject: [PATCH 071/100] Create buffer.cpp, reduce #include dependencies and impl leakage. --- firmware/application/CMakeLists.txt | 1 + firmware/baseband/CMakeLists.txt | 1 + firmware/common/buffer.cpp | 36 +++++++++++++++++++++++++++++ firmware/common/buffer.hpp | 12 +--------- 4 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 firmware/common/buffer.cpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 81e1d4bd..856ed999 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -130,6 +130,7 @@ set(CPPSRC rffc507x_spi.cpp max2837.cpp max5864.cpp + ${COMMON}/buffer.cpp debounce.cpp touch.cpp touch_adc.cpp diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index eade87c1..f029f7c0 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -106,6 +106,7 @@ set(CPPSRC baseband_dma.cpp ${COMMON}/baseband_sgpio.cpp ${COMMON}/portapack_shared_memory.cpp + ${COMMON}/buffer.cpp baseband_thread.cpp baseband_processor.cpp baseband_stats_collector.cpp diff --git a/firmware/common/buffer.cpp b/firmware/common/buffer.cpp new file mode 100644 index 00000000..344b91a4 --- /dev/null +++ b/firmware/common/buffer.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "buffer.hpp" + +#if defined(LPC43XX_M4) +#include "lpc43xx_m4.h" + +Timestamp Timestamp::now() { + // Code stolen from LPC43xx rtc_lld.c + Timestamp timestamp; + do { + timestamp.tv_time = LPC_RTC->CTIME0; + timestamp.tv_date = LPC_RTC->CTIME1; + } while( (timestamp.tv_time != LPC_RTC->CTIME0) || (timestamp.tv_date != LPC_RTC->CTIME1) ); + return timestamp; +} +#endif diff --git a/firmware/common/buffer.hpp b/firmware/common/buffer.hpp index 2b3d7170..8676f6f5 100644 --- a/firmware/common/buffer.hpp +++ b/firmware/common/buffer.hpp @@ -33,21 +33,11 @@ * a knot to tackle at the moment, though... */ #if defined(LPC43XX_M4) -#include "lpc43xx_m4.h" - struct Timestamp { uint32_t tv_date { 0 }; uint32_t tv_time { 0 }; - static Timestamp now() { - // Code stolen from LPC43xx rtc_lld.c - Timestamp timestamp; - do { - timestamp.tv_time = LPC_RTC->CTIME0; - timestamp.tv_date = LPC_RTC->CTIME1; - } while( (timestamp.tv_time != LPC_RTC->CTIME0) || (timestamp.tv_date != LPC_RTC->CTIME1) ); - return timestamp; - } + static Timestamp now(); }; #endif From 8c8678909b97cb0a2554fc423dc48c85e0db79dc Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 5 Nov 2016 09:39:12 -0700 Subject: [PATCH 072/100] Update CI ARM GCC toolchain to 5.4-2016q3. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0c75d2be..363625ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,9 @@ notifications: - "Firmware download : https://portapack-h1-builds.s3.amazonaws.com/%{repository_slug}/%{build_number}/%{build_number}.1/build/firmware/portapack-h1-firmware-%{commit}.tar.bz2" before_script: - - wget https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q2-update/+download/gcc-arm-none-eabi-5_4-2016q2-20160622-linux.tar.bz2 -O /tmp/gcc-arm.tar.bz2 + - wget https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q3-update/+download/gcc-arm-none-eabi-5_4-2016q3-20160926-linux.tar.bz2 -O /tmp/gcc-arm.tar.bz2 - tar -xf /tmp/gcc-arm.tar.bz2 - - export PATH=$PWD/gcc-arm-none-eabi-5_4-2016q2/bin:$PATH + - export PATH=$PWD/gcc-arm-none-eabi-5_4-2016q3/bin:$PATH - export CC="arm-none-eabi-gcc" - export CXX="arm-none-eabi-g++" From 94528ea5729f4f4790243d1c4f74ff01e6111936 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 5 Nov 2016 10:40:05 -0700 Subject: [PATCH 073/100] Allow overriding baseband mcuconf.h M4 clock config. --- firmware/baseband/mcuconf.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/firmware/baseband/mcuconf.h b/firmware/baseband/mcuconf.h index eae37545..0accb63a 100755 --- a/firmware/baseband/mcuconf.h +++ b/firmware/baseband/mcuconf.h @@ -43,5 +43,10 @@ #define LPC43XX_M0APPTXEVENT_IRQ_PRIORITY 4 /* M4 is initialized by M0, which has already started PLL1 */ +#if !defined(LPC43XX_M4_CLK) || defined(__DOXYGEN__) #define LPC43XX_M4_CLK 200000000 -#define LPC43XX_M4_CLK_SRC 0x09 \ No newline at end of file +#endif + +#if !defined(LPC43XX_M4_CLK_SRC) || defined(__DOXYGEN__) +#define LPC43XX_M4_CLK_SRC 0x09 +#endif From 193bf0b744dd1c85b6651b3634b0abc13a249cf4 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 5 Nov 2016 10:40:58 -0700 Subject: [PATCH 074/100] Add "BASEBAND" path variable for CMake. --- firmware/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 2c338ce3..371d715b 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -20,6 +20,7 @@ project(firmware) +set(BASEBAND ${PROJECT_SOURCE_DIR}/baseband) set(COMMON ${PROJECT_SOURCE_DIR}/common) set(CHIBIOS ${PROJECT_SOURCE_DIR}/chibios) set(CHIBIOS_PORTAPACK ${PROJECT_SOURCE_DIR}/chibios-portapack) From 229616491c61bd3f06133f1a1b28a8b263bb32ca Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 26 Nov 2016 16:28:11 -0800 Subject: [PATCH 075/100] Enable Effective C++ and uninitialized members warnings. --- firmware/application/CMakeLists.txt | 2 +- firmware/baseband/CMakeLists.txt | 2 +- firmware/bootstrap/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 856ed999..d0860c15 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -35,7 +35,7 @@ set(USE_OPT "-Os -g --specs=nano.specs") set(USE_COPT "-std=gnu99") # C++ specific options here (added to USE_OPT). -set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions") +set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") # Enable this if you want the linker to remove unused code and data set(USE_LINK_GC yes) diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index f029f7c0..faf5c2cb 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -35,7 +35,7 @@ set(USE_OPT "-O3 -g -falign-functions=16 -fno-math-errno --specs=nano.specs") set(USE_COPT "-std=gnu99") # C++ specific options here (added to USE_OPT). -set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions") +set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") # Enable this if you want the linker to remove unused code and data set(USE_LINK_GC yes) diff --git a/firmware/bootstrap/CMakeLists.txt b/firmware/bootstrap/CMakeLists.txt index 7f837998..6756f5b0 100644 --- a/firmware/bootstrap/CMakeLists.txt +++ b/firmware/bootstrap/CMakeLists.txt @@ -35,7 +35,7 @@ set(USE_OPT "-Os -g -falign-functions=16 -fno-math-errno --specs=nano.specs") set(USE_COPT "-std=gnu99") # C++ specific options here (added to USE_OPT). -set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions") +set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") # Enable this if you want the linker to remove unused code and data set(USE_LINK_GC yes) From cd31ae86d72a06958a04cc4d84cd5e2e39c9c778 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 26 Nov 2016 16:42:03 -0800 Subject: [PATCH 076/100] Add single-arg constructor for vec2_s16. --- firmware/common/simd.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/firmware/common/simd.hpp b/firmware/common/simd.hpp index 0d65b8dd..c2e9be95 100644 --- a/firmware/common/simd.hpp +++ b/firmware/common/simd.hpp @@ -41,6 +41,12 @@ struct vec2_s16 { { } + constexpr vec2_s16( + const int16_t v + ) : v { v, v } + { + } + constexpr vec2_s16( const int16_t v0, const int16_t v1 From 4eb0facacb891b758f5457c7cec3660155a290c4 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 26 Nov 2016 16:50:44 -0800 Subject: [PATCH 077/100] Add lots of value constructors. --- firmware/application/ais_app.hpp | 19 +++++----- firmware/application/analog_audio_app.hpp | 4 +-- firmware/application/capture_app.hpp | 2 +- firmware/application/capture_thread.hpp | 2 +- firmware/application/dirty_registers.hpp | 2 +- firmware/application/ert_app.hpp | 8 ++--- firmware/application/event_m0.cpp | 2 +- firmware/application/event_m0.hpp | 2 +- firmware/application/file.hpp | 13 +++---- firmware/application/io_file.hpp | 2 +- firmware/application/io_wave.hpp | 4 +-- firmware/application/log_file.hpp | 2 +- firmware/application/max2837.hpp | 2 +- firmware/application/recent_entries.hpp | 4 +-- firmware/application/rffc507x.hpp | 4 +-- firmware/application/signal.hpp | 2 +- firmware/application/temperature_logger.hpp | 2 +- firmware/application/touch.hpp | 22 +++++------- firmware/application/tpms_app.hpp | 12 +++---- firmware/application/ui_debug.hpp | 2 +- firmware/application/ui_menu.hpp | 2 +- firmware/application/ui_navigation.hpp | 12 +++---- firmware/application/ui_receiver.hpp | 26 +++++++------- firmware/application/ui_record_view.hpp | 6 ++-- firmware/application/ui_sd_card_debug.cpp | 2 +- firmware/application/ui_sd_card_debug.hpp | 2 +- .../application/ui_sd_card_status_view.hpp | 2 +- firmware/application/ui_spectrum.hpp | 4 +-- firmware/application/ui_touch_calibration.hpp | 4 +-- firmware/baseband/audio_output.hpp | 10 +++--- firmware/baseband/audio_stats_collector.hpp | 2 +- firmware/baseband/baseband_processor.hpp | 2 +- firmware/baseband/baseband_thread.hpp | 4 +-- firmware/baseband/block_decimator.hpp | 2 +- firmware/baseband/channel_decimator.hpp | 24 +++++-------- firmware/baseband/clock_recovery.hpp | 8 ++--- firmware/baseband/dsp_decimate.hpp | 35 ++++++++----------- firmware/baseband/matched_filter.hpp | 4 +-- firmware/baseband/packet_builder.hpp | 10 +++--- firmware/baseband/proc_ais.hpp | 8 ++--- firmware/baseband/proc_am_audio.hpp | 22 ++++++------ firmware/baseband/proc_capture.hpp | 10 +++--- firmware/baseband/proc_nfm_audio.hpp | 16 ++++----- firmware/baseband/proc_tpms.hpp | 6 ++-- firmware/baseband/proc_wfm_audio.hpp | 18 +++++----- firmware/baseband/proc_wideband_spectrum.hpp | 4 +-- firmware/baseband/rssi_stats_collector.hpp | 2 +- firmware/baseband/spectrum_collector.hpp | 14 +++----- firmware/baseband/stream_input.hpp | 8 ++--- firmware/common/baseband_packet.hpp | 2 +- firmware/common/buffer_exchange.hpp | 6 ++-- firmware/common/message_queue.hpp | 2 +- firmware/common/png_writer.hpp | 4 +-- firmware/common/ui_widget.hpp | 16 ++++----- 54 files changed, 194 insertions(+), 217 deletions(-) diff --git a/firmware/application/ais_app.hpp b/firmware/application/ais_app.hpp index 2e26d2ab..ba95e70b 100644 --- a/firmware/application/ais_app.hpp +++ b/firmware/application/ais_app.hpp @@ -49,8 +49,8 @@ using namespace lpc43xx; struct AISPosition { rtc::RTC timestamp { }; - ais::Latitude latitude; - ais::Longitude longitude; + ais::Latitude latitude { }; + ais::Longitude longitude { }; ais::RateOfTurn rate_of_turn { -128 }; ais::SpeedOverGround speed_over_ground { 1023 }; ais::CourseOverGround course_over_ground { 3600 }; @@ -78,6 +78,9 @@ struct AISRecentEntry { AISRecentEntry( const ais::MMSI& mmsi ) : mmsi { mmsi }, + name { }, + call_sign { }, + destination { }, last_position { }, received_count { 0 }, navigational_status { -1 } @@ -102,7 +105,7 @@ public: void on_packet(const ais::Packet& packet); private: - LogFile log_file; + LogFile log_file { }; }; namespace ui { @@ -111,7 +114,7 @@ using AISRecentEntriesView = RecentEntriesView; class AISRecentEntryDetailView : public View { public: - std::function on_close; + std::function on_close { }; AISRecentEntryDetailView(); @@ -122,7 +125,7 @@ public: void paint(Painter&) override; private: - AISRecentEntry entry_; + AISRecentEntry entry_ { }; Button button_done { { 72, 216, 96, 24 }, @@ -158,15 +161,15 @@ private: static constexpr uint32_t sampling_rate = 2457600; static constexpr uint32_t baseband_bandwidth = 1750000; - AISRecentEntries recent; - std::unique_ptr logger; + AISRecentEntries recent { }; + std::unique_ptr logger { }; const RecentEntriesColumns columns { { { "MMSI", 9 }, { "Name/Call", 20 }, } }; AISRecentEntriesView recent_entries_view { columns, recent }; - AISRecentEntryDetailView recent_entry_detail_view; + AISRecentEntryDetailView recent_entry_detail_view { }; static constexpr auto header_height = 1 * 16; diff --git a/firmware/application/analog_audio_app.hpp b/firmware/application/analog_audio_app.hpp index 697a199a..566b63d9 100644 --- a/firmware/application/analog_audio_app.hpp +++ b/firmware/application/analog_audio_app.hpp @@ -139,14 +139,14 @@ private: ' ', }; - std::unique_ptr options_widget; + std::unique_ptr options_widget { }; RecordView record_view { { 0 * 8, 2 * 16, 30 * 8, 1 * 16 }, u"AUD_????", RecordView::FileType::WAV, 4096, 4 }; - spectrum::WaterfallWidget waterfall; + spectrum::WaterfallWidget waterfall { }; void on_tuning_frequency_changed(rf::Frequency f); void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); diff --git a/firmware/application/capture_app.hpp b/firmware/application/capture_app.hpp index 76d287f0..7764c76b 100644 --- a/firmware/application/capture_app.hpp +++ b/firmware/application/capture_app.hpp @@ -92,7 +92,7 @@ private: u"BBD_????", RecordView::FileType::RawS16, 16384, 3 }; - spectrum::WaterfallWidget waterfall; + spectrum::WaterfallWidget waterfall { }; }; } /* namespace ui */ diff --git a/firmware/application/capture_thread.hpp b/firmware/application/capture_thread.hpp index 5a4398b0..218a2867 100644 --- a/firmware/application/capture_thread.hpp +++ b/firmware/application/capture_thread.hpp @@ -53,7 +53,7 @@ private: std::unique_ptr writer; std::function success_callback; std::function error_callback; - Thread* thread; + Thread* thread { nullptr }; static msg_t static_fn(void* arg); diff --git a/firmware/application/dirty_registers.hpp b/firmware/application/dirty_registers.hpp index cde1914c..e2143ab2 100644 --- a/firmware/application/dirty_registers.hpp +++ b/firmware/application/dirty_registers.hpp @@ -58,7 +58,7 @@ public: } private: - mask_t mask; + mask_t mask { }; }; #endif/*__DIRTY_REGISTERS_H__*/ diff --git a/firmware/application/ert_app.hpp b/firmware/application/ert_app.hpp index 6a774ede..d7488860 100644 --- a/firmware/application/ert_app.hpp +++ b/firmware/application/ert_app.hpp @@ -72,7 +72,7 @@ struct ERTRecentEntry { size_t received_count { 0 }; - ert::Consumption last_consumption; + ert::Consumption last_consumption { }; ERTRecentEntry( const Key& key @@ -97,7 +97,7 @@ public: void on_packet(const ert::Packet& packet); private: - LogFile log_file; + LogFile log_file { }; }; using ERTRecentEntries = RecentEntries; @@ -126,8 +126,8 @@ public: std::string title() const override { return "ERT"; }; private: - ERTRecentEntries recent; - std::unique_ptr logger; + ERTRecentEntries recent { }; + std::unique_ptr logger { }; const RecentEntriesColumns columns { { { "ID", 10 }, diff --git a/firmware/application/event_m0.cpp b/firmware/application/event_m0.cpp index bb32041c..8c35a523 100644 --- a/firmware/application/event_m0.cpp +++ b/firmware/application/event_m0.cpp @@ -85,7 +85,7 @@ public: private: using MapType = std::array; - MapType map_; + MapType map_ { }; }; static MessageHandlerMap message_map; diff --git a/firmware/application/event_m0.hpp b/firmware/application/event_m0.hpp index f89f0b75..f8442106 100644 --- a/firmware/application/event_m0.hpp +++ b/firmware/application/event_m0.hpp @@ -94,7 +94,7 @@ private: static Thread* thread_event_loop; - touch::Manager touch_manager; + touch::Manager touch_manager { }; ui::Widget* const top_widget; ui::Painter painter; ui::Context& context; diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index 57e63fd9..4c59f7f0 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -37,10 +37,7 @@ namespace std { namespace filesystem { struct filesystem_error { - constexpr filesystem_error( - ) : err { FR_OK } - { - } + constexpr filesystem_error() = default; constexpr filesystem_error( FRESULT fatfs_error @@ -61,7 +58,7 @@ struct filesystem_error { std::string what() const; private: - uint32_t err; + uint32_t err { FR_OK }; }; struct path { @@ -196,8 +193,8 @@ class directory_iterator { } }; - std::shared_ptr impl; - const path pattern; + std::shared_ptr impl { }; + const path pattern { }; friend bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs); @@ -320,7 +317,7 @@ public: Optional sync(); private: - FIL f; + FIL f { }; Optional open_fatfs(const std::filesystem::path& filename, BYTE mode); }; diff --git a/firmware/application/io_file.hpp b/firmware/application/io_file.hpp index c9049dde..a71b1afa 100644 --- a/firmware/application/io_file.hpp +++ b/firmware/application/io_file.hpp @@ -44,7 +44,7 @@ public: File::Result write(const void* const buffer, const File::Size bytes) override; protected: - File file; + File file { }; uint64_t bytes_written { 0 }; }; diff --git a/firmware/application/io_wave.hpp b/firmware/application/io_wave.hpp index e048be8e..13b3a432 100644 --- a/firmware/application/io_wave.hpp +++ b/firmware/application/io_wave.hpp @@ -97,8 +97,8 @@ public: ); private: - uint32_t sampling_rate; - uint32_t bytes_written; + uint32_t sampling_rate { 0 }; + uint32_t bytes_written { 0 }; Optional update_header(); }; diff --git a/firmware/application/log_file.hpp b/firmware/application/log_file.hpp index 5f222a79..01861c1b 100644 --- a/firmware/application/log_file.hpp +++ b/firmware/application/log_file.hpp @@ -38,7 +38,7 @@ public: Optional write_entry(const rtc::RTC& datetime, const std::string& entry); private: - File file; + File file { }; Optional write_line(const std::string& message); }; diff --git a/firmware/application/max2837.hpp b/firmware/application/max2837.hpp index 3f1beb70..20d319a8 100644 --- a/firmware/application/max2837.hpp +++ b/firmware/application/max2837.hpp @@ -897,7 +897,7 @@ private: spi::arbiter::Target& _target; RegisterMap _map { initial_register_values }; - DirtyRegisters _dirty; + DirtyRegisters _dirty { }; void flush_one(const Register reg); diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 568f5e6c..2af1e485 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -127,7 +127,7 @@ class RecentEntriesTable : public Widget { public: using Entry = typename Entries::value_type; - std::function on_select; + std::function on_select { }; RecentEntriesTable( Entries& recent @@ -231,7 +231,7 @@ class RecentEntriesView : public View { public: using Entry = typename Entries::value_type; - std::function on_select; + std::function on_select { }; RecentEntriesView( const RecentEntriesColumns& columns, diff --git a/firmware/application/rffc507x.hpp b/firmware/application/rffc507x.hpp index 016f0f11..e6c903a9 100644 --- a/firmware/application/rffc507x.hpp +++ b/firmware/application/rffc507x.hpp @@ -813,10 +813,10 @@ public: reg_t read(const address_t reg_num); private: - spi::SPI _bus; + spi::SPI _bus { }; RegisterMap _map { default_hackrf_one }; - DirtyRegisters _dirty; + DirtyRegisters _dirty { }; void write(const address_t reg_num, const reg_t value); diff --git a/firmware/application/signal.hpp b/firmware/application/signal.hpp index 09baf777..262e4798 100644 --- a/firmware/application/signal.hpp +++ b/firmware/application/signal.hpp @@ -78,7 +78,7 @@ private: using EntryType = std::unique_ptr; - std::list entries; + std::list entries { }; SignalToken next_token = 1; }; diff --git a/firmware/application/temperature_logger.hpp b/firmware/application/temperature_logger.hpp index 51f8e451..01f6715c 100644 --- a/firmware/application/temperature_logger.hpp +++ b/firmware/application/temperature_logger.hpp @@ -39,7 +39,7 @@ public: std::vector history() const; private: - std::array samples; + std::array samples { }; static constexpr size_t sample_interval = 5; size_t sample_phase = 0; diff --git a/firmware/application/touch.hpp b/firmware/application/touch.hpp index bc451e36..0c421f7d 100644 --- a/firmware/application/touch.hpp +++ b/firmware/application/touch.hpp @@ -154,13 +154,7 @@ const Calibration default_calibration(); template class Filter { public: - constexpr Filter( - ) : history(), - history_history { 0 }, - accumulator { 0 }, - n { 0 } - { - } + constexpr Filter() = default; void reset() { history.fill(0); @@ -196,10 +190,10 @@ public: private: static constexpr uint32_t history_history_mask { (1U << N) - 1 }; - std::array history; - uint32_t history_history; - uint32_t accumulator; - size_t n; + std::array history { }; + uint32_t history_history { 0 }; + uint32_t accumulator { 0 }; + size_t n { 0 }; bool history_valid() const { return (history_history & history_history_mask) == history_history_mask; @@ -208,7 +202,7 @@ private: class Manager { public: - std::function on_event; + std::function on_event { }; void feed(const Frame& frame); @@ -224,8 +218,8 @@ private: // Ensure filter length is equal or less than touch_count_threshold, // or coordinates from the last touch will be in the initial averages. - Filter filter_x; - Filter filter_y; + Filter filter_x { }; + Filter filter_y { }; //Debounce touch_debounce; diff --git a/firmware/application/tpms_app.hpp b/firmware/application/tpms_app.hpp index 771efd7f..ee17c6eb 100644 --- a/firmware/application/tpms_app.hpp +++ b/firmware/application/tpms_app.hpp @@ -54,9 +54,9 @@ struct TPMSRecentEntry { size_t received_count { 0 }; - Optional last_pressure; - Optional last_temperature; - Optional last_flags; + Optional last_pressure { }; + Optional last_temperature { }; + Optional last_flags { }; TPMSRecentEntry( const Key& key @@ -83,7 +83,7 @@ public: void on_packet(const tpms::Packet& packet, const uint32_t target_frequency); private: - LogFile log_file; + LogFile log_file { }; }; namespace ui { @@ -150,8 +150,8 @@ private: { 18 * 8, 0 * 16 } }; - TPMSRecentEntries recent; - std::unique_ptr logger; + TPMSRecentEntries recent { }; + std::unique_ptr logger { }; const RecentEntriesColumns columns { { { "Tp", 2 }, diff --git a/firmware/application/ui_debug.hpp b/firmware/application/ui_debug.hpp index e6297c14..33108dcc 100644 --- a/firmware/application/ui_debug.hpp +++ b/firmware/application/ui_debug.hpp @@ -193,7 +193,7 @@ public: void focus(); private: - Text text_title; + Text text_title { }; RegistersWidget registers_widget; diff --git a/firmware/application/ui_menu.hpp b/firmware/application/ui_menu.hpp index 7d10dbf6..d3f9c1e6 100644 --- a/firmware/application/ui_menu.hpp +++ b/firmware/application/ui_menu.hpp @@ -61,7 +61,7 @@ private: class MenuView : public View { public: - std::function on_left; + std::function on_left { }; MenuView() { set_focusable(true); diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 65fcf2fe..9e3c7df2 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -41,7 +41,7 @@ namespace ui { class SystemStatusView : public View { public: - std::function on_back; + std::function on_back { }; SystemStatusView(); @@ -86,9 +86,9 @@ private: class NavigationView : public View { public: - std::function on_view_changed; + std::function on_view_changed { }; - NavigationView() { } + NavigationView() = default; NavigationView(const NavigationView&) = delete; NavigationView(NavigationView&&) = delete; @@ -107,7 +107,7 @@ public: void focus() override; private: - std::vector> view_stack; + std::vector> view_stack { }; Widget* modal_view { nullptr }; Widget* view() const; @@ -142,8 +142,8 @@ public: Context& context() const override; private: - SystemStatusView status_view; - NavigationView navigation_view; + SystemStatusView status_view { }; + NavigationView navigation_view { }; Context& context_; }; diff --git a/firmware/application/ui_receiver.hpp b/firmware/application/ui_receiver.hpp index eb012f4d..162e5d2c 100644 --- a/firmware/application/ui_receiver.hpp +++ b/firmware/application/ui_receiver.hpp @@ -38,9 +38,9 @@ namespace ui { class FrequencyField : public Widget { public: - std::function on_change; - std::function on_edit; - std::function on_show_options; + std::function on_change { }; + std::function on_edit { }; + std::function on_show_options { }; using range_t = rf::FrequencyRange; @@ -61,7 +61,7 @@ public: private: const size_t length_; const range_t range; - rf::Frequency value_; + rf::Frequency value_ { 0 }; rf::Frequency step { 25000 }; rf::Frequency clamp_value(rf::Frequency value); @@ -135,8 +135,8 @@ public: private: using array_type = std::array; - array_type s; - Justify justify; + array_type s { }; + Justify justify { Justify::Left }; template void remove_zeros(Iterator begin, Iterator end) { @@ -174,7 +174,7 @@ private: class FrequencyKeypadView : public View { public: - std::function on_changed; + std::function on_changed { }; FrequencyKeypadView( NavigationView& nav, @@ -201,7 +201,7 @@ private: { 0, 0, text_digits * button_w, button_h } }; - std::array buttons; + std::array buttons { }; Button button_close { { 0, button_h * 4 + button_h, button_w * 3, button_h }, @@ -257,8 +257,8 @@ public: class FrequencyOptionsView : public View { public: - std::function on_change_step; - std::function on_change_reference_ppm_correction; + std::function on_change_step { }; + std::function on_change_reference_ppm_correction { }; FrequencyOptionsView(const Rect parent_rect, const Style* const style); @@ -314,7 +314,7 @@ private: class LNAGainField : public NumberField { public: - std::function on_show_options; + std::function on_show_options { }; LNAGainField(Point parent_pos); @@ -323,7 +323,7 @@ public: class VGAGainField : public NumberField { public: - std::function on_show_options; + std::function on_show_options { }; VGAGainField(Point parent_pos); @@ -332,7 +332,7 @@ public: class TXGainField : public NumberField { public: - std::function on_show_options; + std::function on_show_options { }; TXGainField(Point parent_pos); diff --git a/firmware/application/ui_record_view.hpp b/firmware/application/ui_record_view.hpp index 203d334c..7564d798 100644 --- a/firmware/application/ui_record_view.hpp +++ b/firmware/application/ui_record_view.hpp @@ -37,7 +37,7 @@ namespace ui { class RecordView : public View { public: - std::function on_error; + std::function on_error { }; enum FileType { RawS16 = 2, @@ -77,7 +77,7 @@ private: const size_t write_size; const size_t buffer_count; size_t sampling_rate { 0 }; - SignalToken signal_token_tick_second; + SignalToken signal_token_tick_second { }; Rectangle rect_background { Color::black() @@ -105,7 +105,7 @@ private: "", }; - std::unique_ptr capture_thread; + std::unique_ptr capture_thread { }; MessageHandlerRegistration message_handler_capture_thread_error { Message::ID::CaptureThreadDone, diff --git a/firmware/application/ui_sd_card_debug.cpp b/firmware/application/ui_sd_card_debug.cpp index 12dc5293..74367210 100644 --- a/firmware/application/ui_sd_card_debug.cpp +++ b/firmware/application/ui_sd_card_debug.cpp @@ -86,7 +86,7 @@ private: static Thread* thread; volatile Result _result { Result::Incomplete }; - Stats _stats; + Stats _stats { }; static msg_t static_fn(void* arg) { auto obj = static_cast(arg); diff --git a/firmware/application/ui_sd_card_debug.hpp b/firmware/application/ui_sd_card_debug.hpp index d051fd3c..170c2a68 100644 --- a/firmware/application/ui_sd_card_debug.hpp +++ b/firmware/application/ui_sd_card_debug.hpp @@ -39,7 +39,7 @@ public: void focus() override; private: - SignalToken sd_card_status_signal_token; + SignalToken sd_card_status_signal_token { }; void on_status(const sd_card::Status status); void on_test(); diff --git a/firmware/application/ui_sd_card_status_view.hpp b/firmware/application/ui_sd_card_status_view.hpp index 31388cfa..9e68c91f 100644 --- a/firmware/application/ui_sd_card_status_view.hpp +++ b/firmware/application/ui_sd_card_status_view.hpp @@ -37,7 +37,7 @@ public: void paint(Painter& painter) override; private: - SignalToken sd_card_status_signal_token; + SignalToken sd_card_status_signal_token { }; void on_status(const sd_card::Status status); }; diff --git a/firmware/application/ui_spectrum.hpp b/firmware/application/ui_spectrum.hpp index 70fe83d5..cbf45644 100644 --- a/firmware/application/ui_spectrum.hpp +++ b/firmware/application/ui_spectrum.hpp @@ -84,8 +84,8 @@ public: void paint(Painter& painter) override; private: - WaterfallView waterfall_view; - FrequencyScale frequency_scale; + WaterfallView waterfall_view { }; + FrequencyScale frequency_scale { }; ChannelSpectrumFIFO* fifo { nullptr }; MessageHandlerRegistration message_handler_spectrum_config { diff --git a/firmware/application/ui_touch_calibration.hpp b/firmware/application/ui_touch_calibration.hpp index 40329020..c9a17aed 100644 --- a/firmware/application/ui_touch_calibration.hpp +++ b/firmware/application/ui_touch_calibration.hpp @@ -65,9 +65,9 @@ private: uint32_t samples_count { 0 }; - touch::DigitizerPoint average; + touch::DigitizerPoint average { }; - std::array digitizer_points; + std::array digitizer_points { }; touch::Calibration calibration; diff --git a/firmware/baseband/audio_output.hpp b/firmware/baseband/audio_output.hpp index 6462e061..21d19f8d 100644 --- a/firmware/baseband/audio_output.hpp +++ b/firmware/baseband/audio_output.hpp @@ -55,13 +55,13 @@ private: BlockDecimator block_buffer { 1 }; - IIRBiquadFilter hpf; - IIRBiquadFilter deemph; - FMSquelch squelch; + IIRBiquadFilter hpf { }; + IIRBiquadFilter deemph { }; + FMSquelch squelch { }; - std::unique_ptr stream; + std::unique_ptr stream { }; - AudioStatsCollector audio_stats; + AudioStatsCollector audio_stats { }; uint64_t audio_present_history = 0; diff --git a/firmware/baseband/audio_stats_collector.hpp b/firmware/baseband/audio_stats_collector.hpp index 10e17edc..9eddfd88 100644 --- a/firmware/baseband/audio_stats_collector.hpp +++ b/firmware/baseband/audio_stats_collector.hpp @@ -50,7 +50,7 @@ private: float max_squared { 0 }; size_t count { 0 }; - AudioStatistics statistics; + AudioStatistics statistics { }; void consume_audio_buffer(const buffer_f32_t& src); diff --git a/firmware/baseband/baseband_processor.hpp b/firmware/baseband/baseband_processor.hpp index 28ef5063..074853b2 100644 --- a/firmware/baseband/baseband_processor.hpp +++ b/firmware/baseband/baseband_processor.hpp @@ -40,7 +40,7 @@ protected: void feed_channel_stats(const buffer_c16_t& channel); private: - ChannelStatsCollector channel_stats; + ChannelStatsCollector channel_stats { }; }; #endif/*__BASEBAND_PROCESSOR_H__*/ diff --git a/firmware/baseband/baseband_thread.hpp b/firmware/baseband/baseband_thread.hpp index 4d06e326..ce05dec7 100644 --- a/firmware/baseband/baseband_thread.hpp +++ b/firmware/baseband/baseband_thread.hpp @@ -48,8 +48,8 @@ private: static Thread* thread; BasebandProcessor* baseband_processor { nullptr }; - baseband::Direction _direction; - uint32_t sampling_rate; + baseband::Direction _direction { baseband::Direction::Receive }; + uint32_t sampling_rate { 0 }; void run() override; }; diff --git a/firmware/baseband/block_decimator.hpp b/firmware/baseband/block_decimator.hpp index 7782dc37..a3a771c4 100644 --- a/firmware/baseband/block_decimator.hpp +++ b/firmware/baseband/block_decimator.hpp @@ -85,7 +85,7 @@ public: } private: - std::array buffer; + std::array buffer { }; uint32_t input_sampling_rate_ { 0 }; size_t factor_ { 1 }; size_t src_i { 0 }; diff --git a/firmware/baseband/channel_decimator.hpp b/firmware/baseband/channel_decimator.hpp index 956964b0..4b527ee2 100644 --- a/firmware/baseband/channel_decimator.hpp +++ b/firmware/baseband/channel_decimator.hpp @@ -38,12 +38,6 @@ public: By16, By32, }; - - constexpr ChannelDecimator( - ) : decimation_factor { DecimationFactor::By32 }, - fs_over_4_downconvert { true } - { - } constexpr ChannelDecimator( const DecimationFactor decimation_factor, @@ -64,17 +58,17 @@ public: } private: - std::array work_baseband; + std::array work_baseband { }; - dsp::decimate::TranslateByFSOver4AndDecimateBy2CIC3 translate; - dsp::decimate::Complex8DecimateBy2CIC3 cic_0; - dsp::decimate::DecimateBy2CIC3 cic_1; - dsp::decimate::DecimateBy2CIC3 cic_2; - dsp::decimate::DecimateBy2CIC3 cic_3; - dsp::decimate::DecimateBy2CIC3 cic_4; + dsp::decimate::TranslateByFSOver4AndDecimateBy2CIC3 translate { }; + dsp::decimate::Complex8DecimateBy2CIC3 cic_0 { }; + dsp::decimate::DecimateBy2CIC3 cic_1 { }; + dsp::decimate::DecimateBy2CIC3 cic_2 { }; + dsp::decimate::DecimateBy2CIC3 cic_3 { }; + dsp::decimate::DecimateBy2CIC3 cic_4 { }; - DecimationFactor decimation_factor; - const bool fs_over_4_downconvert; + DecimationFactor decimation_factor { DecimationFactor::By32 }; + const bool fs_over_4_downconvert { true }; buffer_c16_t execute_decimation(const buffer_c8_t& buffer); diff --git a/firmware/baseband/clock_recovery.hpp b/firmware/baseband/clock_recovery.hpp index 9a6904a3..70d7f003 100644 --- a/firmware/baseband/clock_recovery.hpp +++ b/firmware/baseband/clock_recovery.hpp @@ -60,7 +60,7 @@ public: } private: - std::array t { { 0.0f, 0.0f, 0.0f } }; + std::array t { }; size_t symbol_phase { 0 }; }; @@ -154,9 +154,9 @@ public: } private: - dsp::interpolation::LinearResampler resampler; - GardnerTimingErrorDetector timing_error_detector; - ErrorFilter error_filter; + dsp::interpolation::LinearResampler resampler { }; + GardnerTimingErrorDetector timing_error_detector { }; + ErrorFilter error_filter { }; const SymbolHandler symbol_handler; void resampler_callback(const float interpolated_sample) { diff --git a/firmware/baseband/dsp_decimate.hpp b/firmware/baseband/dsp_decimate.hpp index 52727075..4437ef88 100644 --- a/firmware/baseband/dsp_decimate.hpp +++ b/firmware/baseband/dsp_decimate.hpp @@ -86,8 +86,8 @@ public: ); private: - std::array z; - std::array taps; + std::array z { }; + std::array taps { }; }; class FIRC8xR16x24FS4Decim4 { @@ -115,8 +115,8 @@ public: ); private: - std::array z_; - std::array taps_; + std::array z_ { }; + std::array taps_ { }; int32_t output_scale = 0; }; @@ -145,8 +145,8 @@ public: ); private: - std::array z_; - std::array taps_; + std::array z_ { }; + std::array taps_ { }; int32_t output_scale = 0; }; @@ -169,8 +169,8 @@ public: ); private: - std::array z_; - std::array taps_; + std::array z_ { }; + std::array taps_ { }; int32_t output_scale = 0; }; @@ -193,8 +193,8 @@ public: ); private: - std::array z_; - std::array taps_; + std::array z_ { }; + std::array taps_ { }; int32_t output_scale = 0; }; @@ -208,11 +208,6 @@ public: /* NOTE! Current code makes an assumption that block of samples to be * processed will be a multiple of the taps_count. */ - FIRAndDecimateComplex( - ) : taps_count_ { 0 }, - decimation_factor_ { 1 } - { - } template void configure( @@ -230,10 +225,10 @@ public: private: using samples_t = sample_t[]; - std::unique_ptr samples_; - std::unique_ptr taps_reversed_; - size_t taps_count_; - size_t decimation_factor_; + std::unique_ptr samples_ { }; + std::unique_ptr taps_reversed_ { }; + size_t taps_count_ { 0 }; + size_t decimation_factor_ { 1 }; template void configure( @@ -259,7 +254,7 @@ public: ); private: - int16_t z[5]; + int16_t z[5] { }; }; } /* namespace decimate */ diff --git a/firmware/baseband/matched_filter.hpp b/firmware/baseband/matched_filter.hpp index 49045db8..a9c6f099 100644 --- a/firmware/baseband/matched_filter.hpp +++ b/firmware/baseband/matched_filter.hpp @@ -66,8 +66,8 @@ public: private: using samples_t = sample_t[]; - std::unique_ptr samples_; - std::unique_ptr taps_reversed_; + std::unique_ptr samples_ { }; + std::unique_ptr taps_reversed_ { }; size_t taps_count_ { 0 }; size_t decimation_factor_ { 1 }; size_t decimation_phase { 0 }; diff --git a/firmware/baseband/packet_builder.hpp b/firmware/baseband/packet_builder.hpp index c8a9ea5b..6a24e803 100644 --- a/firmware/baseband/packet_builder.hpp +++ b/firmware/baseband/packet_builder.hpp @@ -122,13 +122,13 @@ private: const PayloadHandlerFunc payload_handler; - BitHistory bit_history; - PreambleMatcher preamble; - UnstuffMatcher unstuff; - EndMatcher end; + BitHistory bit_history { }; + PreambleMatcher preamble { }; + UnstuffMatcher unstuff { }; + EndMatcher end { }; State state { State::Preamble }; - baseband::Packet packet; + baseband::Packet packet { }; void reset_state() { packet.clear(); diff --git a/firmware/baseband/proc_ais.hpp b/firmware/baseband/proc_ais.hpp index 97e813ff..1130c05c 100644 --- a/firmware/baseband/proc_ais.hpp +++ b/firmware/baseband/proc_ais.hpp @@ -54,21 +54,21 @@ private: BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 }; RSSIThread rssi_thread { NORMALPRIO + 10 }; - std::array dst; + std::array dst { }; const buffer_c16_t dst_buffer { dst.data(), dst.size() }; - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0; - dsp::decimate::FIRC16xR16x32Decim8 decim_1; + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; + dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; dsp::matched_filter::MatchedFilter mf { baseband::ais::square_taps_38k4_1t_p, 2 }; clock_recovery::ClockRecovery clock_recovery { 19200, 9600, { 0.0555f }, [this](const float symbol) { this->consume_symbol(symbol); } }; - symbol_coding::NRZIDecoder nrzi_decode; + symbol_coding::NRZIDecoder nrzi_decode { }; PacketBuilder packet_builder { { 0b0101010101111110, 16, 1 }, { 0b111110, 6 }, diff --git a/firmware/baseband/proc_am_audio.hpp b/firmware/baseband/proc_am_audio.hpp index 08cca3e5..55827ea0 100644 --- a/firmware/baseband/proc_am_audio.hpp +++ b/firmware/baseband/proc_am_audio.hpp @@ -49,31 +49,31 @@ private: BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 }; RSSIThread rssi_thread { NORMALPRIO + 10 }; - std::array dst; + std::array dst { }; const buffer_c16_t dst_buffer { dst.data(), dst.size() }; - std::array audio; + std::array audio { }; const buffer_f32_t audio_buffer { audio.data(), audio.size() }; - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0; - dsp::decimate::FIRC16xR16x32Decim8 decim_1; - dsp::decimate::FIRAndDecimateComplex decim_2; - dsp::decimate::FIRAndDecimateComplex channel_filter; + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; + dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; + dsp::decimate::FIRAndDecimateComplex decim_2 { }; + dsp::decimate::FIRAndDecimateComplex channel_filter { }; uint32_t channel_filter_pass_f = 0; uint32_t channel_filter_stop_f = 0; bool modulation_ssb = false; - dsp::demodulate::AM demod_am; - dsp::demodulate::SSB demod_ssb; - FeedForwardCompressor audio_compressor; - AudioOutput audio_output; + dsp::demodulate::AM demod_am { }; + dsp::demodulate::SSB demod_ssb { }; + FeedForwardCompressor audio_compressor { }; + AudioOutput audio_output { }; - SpectrumCollector channel_spectrum; + SpectrumCollector channel_spectrum { }; bool configured { false }; void configure(const AMConfigureMessage& message); diff --git a/firmware/baseband/proc_capture.hpp b/firmware/baseband/proc_capture.hpp index c2b48b0c..b30a7889 100644 --- a/firmware/baseband/proc_capture.hpp +++ b/firmware/baseband/proc_capture.hpp @@ -51,20 +51,20 @@ private: BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 }; RSSIThread rssi_thread { NORMALPRIO + 10 }; - std::array dst; + std::array dst { }; const buffer_c16_t dst_buffer { dst.data(), dst.size() }; - dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0; - dsp::decimate::FIRC16xR16x16Decim2 decim_1; + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; + dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; uint32_t channel_filter_pass_f = 0; uint32_t channel_filter_stop_f = 0; - std::unique_ptr stream; + std::unique_ptr stream { }; - SpectrumCollector channel_spectrum; + SpectrumCollector channel_spectrum { }; size_t spectrum_interval_samples = 0; size_t spectrum_samples = 0; diff --git a/firmware/baseband/proc_nfm_audio.hpp b/firmware/baseband/proc_nfm_audio.hpp index f4529a73..9eba9ef7 100644 --- a/firmware/baseband/proc_nfm_audio.hpp +++ b/firmware/baseband/proc_nfm_audio.hpp @@ -46,28 +46,28 @@ private: BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 }; RSSIThread rssi_thread { NORMALPRIO + 10 }; - std::array dst; + std::array dst { }; const buffer_c16_t dst_buffer { dst.data(), dst.size() }; - std::array audio; + std::array audio { }; const buffer_f32_t audio_buffer { audio.data(), audio.size() }; - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0; - dsp::decimate::FIRC16xR16x32Decim8 decim_1; - dsp::decimate::FIRAndDecimateComplex channel_filter; + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; + dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; + dsp::decimate::FIRAndDecimateComplex channel_filter { }; uint32_t channel_filter_pass_f = 0; uint32_t channel_filter_stop_f = 0; - dsp::demodulate::FM demod; + dsp::demodulate::FM demod { }; - AudioOutput audio_output; + AudioOutput audio_output { }; - SpectrumCollector channel_spectrum; + SpectrumCollector channel_spectrum { }; bool configured { false }; void configure(const NBFMConfigureMessage& message); diff --git a/firmware/baseband/proc_tpms.hpp b/firmware/baseband/proc_tpms.hpp index f81c4800..2a09bb71 100644 --- a/firmware/baseband/proc_tpms.hpp +++ b/firmware/baseband/proc_tpms.hpp @@ -69,14 +69,14 @@ private: BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 }; RSSIThread rssi_thread { NORMALPRIO + 10 }; - std::array dst; + std::array dst { }; const buffer_c16_t dst_buffer { dst.data(), dst.size() }; - dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0; - dsp::decimate::FIRC16xR16x16Decim2 decim_1; + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; + dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; dsp::matched_filter::MatchedFilter mf_38k4_1t_19k2 { rect_taps_307k2_38k4_1t_19k2_p, 8 }; diff --git a/firmware/baseband/proc_wfm_audio.hpp b/firmware/baseband/proc_wfm_audio.hpp index 446990f5..2b21133d 100644 --- a/firmware/baseband/proc_wfm_audio.hpp +++ b/firmware/baseband/proc_wfm_audio.hpp @@ -45,7 +45,7 @@ private: BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 }; RSSIThread rssi_thread { NORMALPRIO + 10 }; - std::array dst; + std::array dst { }; const buffer_c16_t dst_buffer { dst.data(), dst.size() @@ -55,19 +55,19 @@ private: sizeof(dst) / sizeof(int16_t) }; - dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0; - dsp::decimate::FIRC16xR16x16Decim2 decim_1; + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; + dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; uint32_t channel_filter_pass_f = 0; uint32_t channel_filter_stop_f = 0; - dsp::demodulate::FM demod; - dsp::decimate::DecimateBy2CIC4Real audio_dec_1; - dsp::decimate::DecimateBy2CIC4Real audio_dec_2; - dsp::decimate::FIR64AndDecimateBy2Real audio_filter; + dsp::demodulate::FM demod { }; + dsp::decimate::DecimateBy2CIC4Real audio_dec_1 { }; + dsp::decimate::DecimateBy2CIC4Real audio_dec_2 { }; + dsp::decimate::FIR64AndDecimateBy2Real audio_filter { }; - AudioOutput audio_output; + AudioOutput audio_output { }; - SpectrumCollector channel_spectrum; + SpectrumCollector channel_spectrum { }; size_t spectrum_interval_samples = 0; size_t spectrum_samples = 0; diff --git a/firmware/baseband/proc_wideband_spectrum.hpp b/firmware/baseband/proc_wideband_spectrum.hpp index 7a650ee0..117a1f5b 100644 --- a/firmware/baseband/proc_wideband_spectrum.hpp +++ b/firmware/baseband/proc_wideband_spectrum.hpp @@ -46,9 +46,9 @@ private: BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 }; RSSIThread rssi_thread { NORMALPRIO + 10 }; - SpectrumCollector channel_spectrum; + SpectrumCollector channel_spectrum { }; - std::array spectrum; + std::array spectrum { }; size_t phase = 0; }; diff --git a/firmware/baseband/rssi_stats_collector.hpp b/firmware/baseband/rssi_stats_collector.hpp index 7a1cd494..68143768 100644 --- a/firmware/baseband/rssi_stats_collector.hpp +++ b/firmware/baseband/rssi_stats_collector.hpp @@ -69,7 +69,7 @@ public: private: static constexpr float update_interval { 0.1f }; - RSSIStatistics statistics; + RSSIStatistics statistics { }; }; #endif/*__RSSI_STATS_COLLECTOR_H__*/ diff --git a/firmware/baseband/spectrum_collector.hpp b/firmware/baseband/spectrum_collector.hpp index b238c1fe..83d30a42 100644 --- a/firmware/baseband/spectrum_collector.hpp +++ b/firmware/baseband/spectrum_collector.hpp @@ -34,12 +34,6 @@ class SpectrumCollector { public: - constexpr SpectrumCollector( - ) : channel_spectrum_decimator { 1 }, - fifo { fifo_data, ChannelSpectrumConfigMessage::fifo_k } - { - } - void on_message(const Message* const message); void set_decimation_factor(const size_t decimation_factor); @@ -51,13 +45,13 @@ public: ); private: - BlockDecimator channel_spectrum_decimator; - ChannelSpectrumFIFO fifo; - ChannelSpectrum fifo_data[1 << ChannelSpectrumConfigMessage::fifo_k]; + BlockDecimator channel_spectrum_decimator { 1 }; + ChannelSpectrum fifo_data[1 << ChannelSpectrumConfigMessage::fifo_k] { }; + ChannelSpectrumFIFO fifo { fifo_data, ChannelSpectrumConfigMessage::fifo_k }; volatile bool channel_spectrum_request_update { false }; bool streaming { false }; - std::array, 256> channel_spectrum; + std::array, 256> channel_spectrum { }; uint32_t channel_spectrum_sampling_rate { 0 }; uint32_t channel_filter_pass_frequency { 0 }; uint32_t channel_filter_stop_frequency { 0 }; diff --git a/firmware/baseband/stream_input.hpp b/firmware/baseband/stream_input.hpp index ce4a29a4..1e73b8c0 100644 --- a/firmware/baseband/stream_input.hpp +++ b/firmware/baseband/stream_input.hpp @@ -42,12 +42,12 @@ private: FIFO fifo_buffers_empty; FIFO fifo_buffers_full; - std::array buffers; - std::array buffers_empty; - std::array buffers_full; + std::array buffers { }; + std::array buffers_empty { }; + std::array buffers_full { }; StreamBuffer* active_buffer { nullptr }; CaptureConfig* const config { nullptr }; - std::unique_ptr data; + std::unique_ptr data { }; }; #endif/*__STREAM_INPUT_H__*/ diff --git a/firmware/common/baseband_packet.hpp b/firmware/common/baseband_packet.hpp index f5bf161c..eb9fd9fb 100644 --- a/firmware/common/baseband_packet.hpp +++ b/firmware/common/baseband_packet.hpp @@ -62,7 +62,7 @@ public: } private: - std::bitset<1408> data; + std::bitset<1408> data { }; Timestamp timestamp_ { }; size_t count { 0 }; }; diff --git a/firmware/common/buffer_exchange.hpp b/firmware/common/buffer_exchange.hpp index 091a6ea2..1f12da3d 100644 --- a/firmware/common/buffer_exchange.hpp +++ b/firmware/common/buffer_exchange.hpp @@ -68,9 +68,9 @@ public: private: CaptureConfig* const config; - FIFO* fifo_buffers_for_baseband; - FIFO* fifo_buffers_for_application; - Thread* thread; + FIFO* fifo_buffers_for_baseband { nullptr }; + FIFO* fifo_buffers_for_application { nullptr }; + Thread* thread { nullptr }; static BufferExchange* obj; void check_fifo_isr() { diff --git a/firmware/common/message_queue.hpp b/firmware/common/message_queue.hpp index 84bbaa9c..0a12fb62 100644 --- a/firmware/common/message_queue.hpp +++ b/firmware/common/message_queue.hpp @@ -80,7 +80,7 @@ public: private: FIFO fifo; - Mutex mutex_write; + Mutex mutex_write { }; Message* peek(std::array& buf) { Message* const p = reinterpret_cast(buf.data()); diff --git a/firmware/common/png_writer.hpp b/firmware/common/png_writer.hpp index 020a118a..bf0fcc8a 100644 --- a/firmware/common/png_writer.hpp +++ b/firmware/common/png_writer.hpp @@ -44,10 +44,10 @@ private: static constexpr int width { 240 }; static constexpr int height { 320 }; - File file; + File file { }; int scanline_count { 0 }; CRC<32, true, true> crc { 0x04c11db7, 0xffffffff, 0xffffffff }; - Adler32 adler_32; + Adler32 adler_32 { }; void write_chunk_header(const size_t length, const std::array& type); void write_chunk_content(const void* const p, const size_t count); diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 18a03158..008bdaaf 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -46,7 +46,7 @@ public: } private: - FocusManager focus_manager_; + FocusManager focus_manager_ { }; }; class Widget { @@ -161,7 +161,7 @@ public: virtual std::string title() const; protected: - std::vector children_; + std::vector children_ { }; void invalidate_child(Widget* const widget); }; @@ -198,7 +198,7 @@ private: class Button : public Widget { public: - std::function on_select; + std::function on_select { }; Button(Rect parent_rect, std::string text); @@ -243,7 +243,7 @@ private: class ImageButton : public Image { public: - std::function on_select; + std::function on_select { }; ImageButton( const Rect parent_rect, @@ -263,8 +263,8 @@ public: using option_t = std::pair; using options_t = std::vector; - std::function on_change; - std::function on_show_options; + std::function on_change { }; + std::function on_show_options { }; OptionsField(Point parent_pos, size_t length, options_t options); @@ -287,7 +287,7 @@ private: class NumberField : public Widget { public: - std::function on_change; + std::function on_change { }; using range_t = std::pair; @@ -309,7 +309,7 @@ private: const int32_t step; const size_t length_; const char fill_char; - int32_t value_; + int32_t value_ { 0 }; int32_t clip_value(int32_t value); }; From 46b3d9d0871505811ac4fe80a678bd636c8fa212 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 26 Nov 2016 16:52:57 -0800 Subject: [PATCH 078/100] Disallow copy constructors/assignments. For classes containing pointers/state that should not be copied. --- firmware/application/capture_thread.hpp | 5 +++++ firmware/application/event_m0.hpp | 5 +++++ firmware/application/ui_navigation.hpp | 2 ++ firmware/application/ui_spectrum.hpp | 5 +++++ firmware/baseband/baseband_thread.hpp | 5 +++++ firmware/baseband/stream_input.hpp | 5 +++++ firmware/common/buffer_exchange.hpp | 5 +++++ firmware/common/ui_widget.hpp | 7 +++++++ 8 files changed, 39 insertions(+) diff --git a/firmware/application/capture_thread.hpp b/firmware/application/capture_thread.hpp index 218a2867..225c1938 100644 --- a/firmware/application/capture_thread.hpp +++ b/firmware/application/capture_thread.hpp @@ -44,6 +44,11 @@ public: ); ~CaptureThread(); + CaptureThread(const CaptureThread&) = delete; + CaptureThread(CaptureThread&&) = delete; + CaptureThread& operator=(const CaptureThread&) = delete; + CaptureThread& operator=(CaptureThread&&) = delete; + const CaptureConfig& state() const { return config; } diff --git a/firmware/application/event_m0.hpp b/firmware/application/event_m0.hpp index f8442106..56f0a5c0 100644 --- a/firmware/application/event_m0.hpp +++ b/firmware/application/event_m0.hpp @@ -49,6 +49,11 @@ public: ui::Context& context ); + EventDispatcher(const EventDispatcher&) = delete; + EventDispatcher(EventDispatcher&&) = delete; + EventDispatcher& operator=(const EventDispatcher&) = delete; + EventDispatcher& operator=(EventDispatcher&&) = delete; + void run(); static void request_stop(); diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 9e3c7df2..2b94d2f3 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -92,6 +92,8 @@ public: NavigationView(const NavigationView&) = delete; NavigationView(NavigationView&&) = delete; + NavigationView& operator=(const NavigationView&) = delete; + NavigationView& operator=(NavigationView&&) = delete; bool is_top() const; diff --git a/firmware/application/ui_spectrum.hpp b/firmware/application/ui_spectrum.hpp index cbf45644..ce0a8133 100644 --- a/firmware/application/ui_spectrum.hpp +++ b/firmware/application/ui_spectrum.hpp @@ -76,6 +76,11 @@ class WaterfallWidget : public View { public: WaterfallWidget(); + WaterfallWidget(const WaterfallWidget&) = delete; + WaterfallWidget(WaterfallWidget&&) = delete; + WaterfallWidget& operator=(const WaterfallWidget&) = delete; + WaterfallWidget& operator=(WaterfallWidget&&) = delete; + void on_show() override; void on_hide() override; diff --git a/firmware/baseband/baseband_thread.hpp b/firmware/baseband/baseband_thread.hpp index ce05dec7..54fa9023 100644 --- a/firmware/baseband/baseband_thread.hpp +++ b/firmware/baseband/baseband_thread.hpp @@ -37,6 +37,11 @@ public: const baseband::Direction direction = baseband::Direction::Receive ); ~BasebandThread(); + + BasebandThread(const BasebandThread&) = delete; + BasebandThread(BasebandThread&&) = delete; + BasebandThread& operator=(const BasebandThread&) = delete; + BasebandThread& operator=(BasebandThread&&) = delete; // This getter should die, it's just here to leak information to code that // isn't in the right place to begin with. diff --git a/firmware/baseband/stream_input.hpp b/firmware/baseband/stream_input.hpp index 1e73b8c0..1500d2e5 100644 --- a/firmware/baseband/stream_input.hpp +++ b/firmware/baseband/stream_input.hpp @@ -34,6 +34,11 @@ class StreamInput { public: StreamInput(CaptureConfig* const config); + StreamInput(const StreamInput&) = delete; + StreamInput(StreamInput&&) = delete; + StreamInput& operator=(const StreamInput&) = delete; + StreamInput& operator=(StreamInput&&) = delete; + size_t write(const void* const data, const size_t length); private: diff --git a/firmware/common/buffer_exchange.hpp b/firmware/common/buffer_exchange.hpp index 1f12da3d..7029f606 100644 --- a/firmware/common/buffer_exchange.hpp +++ b/firmware/common/buffer_exchange.hpp @@ -32,6 +32,11 @@ public: BufferExchange(CaptureConfig* const config); ~BufferExchange(); + BufferExchange(const BufferExchange&) = delete; + BufferExchange(BufferExchange&&) = delete; + BufferExchange& operator=(const BufferExchange&) = delete; + BufferExchange& operator=(BufferExchange&&) = delete; + #if defined(LPC43XX_M0) bool empty() const { return fifo_buffers_for_application->is_empty(); diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 008bdaaf..3ec09d26 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -64,6 +64,8 @@ public: Widget(const Widget&) = delete; Widget(Widget&&) = delete; + Widget& operator=(const Widget&) = delete; + Widget& operator=(Widget&&) = delete; virtual ~Widget() = default; @@ -229,6 +231,11 @@ public: const Color background ); + Image(const Image&) = delete; + Image(Image&&) = delete; + Image& operator=(const Image&) = delete; + Image& operator=(Image&&) = delete; + void set_bitmap(const Bitmap* bitmap); void set_foreground(const Color color); void set_background(const Color color); From ed0d5331ad631589fef8088ac92b027309767b9d Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 26 Nov 2016 16:53:35 -0800 Subject: [PATCH 079/100] Fix member initialization order. --- firmware/baseband/baseband_thread.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/baseband/baseband_thread.cpp b/firmware/baseband/baseband_thread.cpp index 3f683a59..8901f528 100644 --- a/firmware/baseband/baseband_thread.cpp +++ b/firmware/baseband/baseband_thread.cpp @@ -47,8 +47,8 @@ BasebandThread::BasebandThread( const tprio_t priority, baseband::Direction direction ) : baseband_processor { baseband_processor }, - sampling_rate { sampling_rate }, - _direction { direction } + _direction { direction }, + sampling_rate { sampling_rate } { thread = chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa), priority, ThreadBase::fn, From 606c1cebac82abba4127ce7b5af5457563f048c5 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 26 Nov 2016 16:58:42 -0800 Subject: [PATCH 080/100] Work around apparent alignment bug in G++ 5.4.1? Padding struct to be sizeof() % 4 == 0, because it was crashing the baseband... I think. --- firmware/baseband/dsp_decimate.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/baseband/dsp_decimate.hpp b/firmware/baseband/dsp_decimate.hpp index 4437ef88..bf8a82bc 100644 --- a/firmware/baseband/dsp_decimate.hpp +++ b/firmware/baseband/dsp_decimate.hpp @@ -255,6 +255,7 @@ public: private: int16_t z[5] { }; + int16_t _dummy { }; // TODO: Addresses GCC bug when constructing a class that's not sizeof() % 4 == 0? }; } /* namespace decimate */ From aac2d31548da1087fef4a537df7593ada91ec5b3 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 28 Nov 2016 10:39:10 -0800 Subject: [PATCH 081/100] Hide ui::Point implementation. --- firmware/application/recent_entries.hpp | 2 +- firmware/application/touch.hpp | 12 ++-- firmware/application/ui_console.cpp | 17 +++--- firmware/application/ui_menu.cpp | 2 +- firmware/application/ui_touch_calibration.cpp | 4 +- firmware/common/lcd_ili9341.cpp | 12 ++-- firmware/common/ui.cpp | 9 ++- firmware/common/ui.hpp | 55 ++++++++++++------- firmware/common/ui_focus.cpp | 8 +-- firmware/common/ui_painter.cpp | 10 ++-- firmware/common/ui_widget.cpp | 4 +- 11 files changed, 78 insertions(+), 57 deletions(-) diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 2af1e485..335cf0b7 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -155,7 +155,7 @@ public: const auto is_selected_key = (selected_key == entry.key()); const auto item_style = (has_focus() && is_selected_key) ? s.invert() : s; draw(entry, target_rect, painter, item_style); - target_rect.pos.y += target_rect.height(); + target_rect += { 0, target_rect.height() }; } painter.fill_rectangle( diff --git a/firmware/application/touch.hpp b/firmware/application/touch.hpp index 0c421f7d..549d3809 100644 --- a/firmware/application/touch.hpp +++ b/firmware/application/touch.hpp @@ -128,12 +128,12 @@ struct Calibration { const std::array& s, const std::array& d ) : k { (s[0].x - s[2].x) * (s[1].y - s[2].y) - (s[1].x - s[2].x) * (s[0].y - s[2].y) }, - a { (d[0].x - d[2].x) * (s[1].y - s[2].y) - (d[1].x - d[2].x) * (s[0].y - s[2].y) }, - b { (s[0].x - s[2].x) * (d[1].x - d[2].x) - (d[0].x - d[2].x) * (s[1].x - s[2].x) }, - c { s[0].y * (s[2].x * d[1].x - s[1].x * d[2].x) + s[1].y * (s[0].x * d[2].x - s[2].x * d[0].x) + s[2].y * (s[1].x * d[0].x - s[0].x * d[1].x) }, - d { (d[0].y - d[2].y) * (s[1].y - s[2].y) - (d[1].y - d[2].y) * (s[0].y - s[2].y) }, - e { (s[0].x - s[2].x) * (d[1].y - d[2].y) - (d[0].y - d[2].y) * (s[1].x - s[2].x) }, - f { s[0].y * (s[2].x * d[1].y - s[1].x * d[2].y) + s[1].y * (s[0].x * d[2].y - s[2].x * d[0].y) + s[2].y * (s[1].x * d[0].y - s[0].x * d[1].y) } + a { (d[0].x() - d[2].x()) * (s[1].y - s[2].y) - (d[1].x() - d[2].x()) * (s[0].y - s[2].y) }, + b { (s[0].x - s[2].x) * (d[1].x() - d[2].x()) - (d[0].x() - d[2].x()) * (s[1].x - s[2].x) }, + c { s[0].y * (s[2].x * d[1].x() - s[1].x * d[2].x()) + s[1].y * (s[0].x * d[2].x() - s[2].x * d[0].x()) + s[2].y * (s[1].x * d[0].x() - s[0].x * d[1].x()) }, + d { (d[0].y() - d[2].y()) * (s[1].y - s[2].y) - (d[1].y() - d[2].y()) * (s[0].y - s[2].y) }, + e { (s[0].x - s[2].x) * (d[1].y() - d[2].y()) - (d[0].y() - d[2].y()) * (s[1].x - s[2].x) }, + f { s[0].y * (s[2].x * d[1].y() - s[1].x * d[2].y()) + s[1].y * (s[0].x * d[2].y() - s[2].x * d[0].y()) + s[2].y * (s[1].x * d[0].y() - s[0].x * d[1].y()) } { } diff --git a/firmware/application/ui_console.cpp b/firmware/application/ui_console.cpp index 28b9f298..af71ee14 100644 --- a/firmware/application/ui_console.cpp +++ b/firmware/application/ui_console.cpp @@ -45,15 +45,15 @@ void Console::write(const std::string& message) { } else { const auto glyph = font.glyph(c); const auto advance = glyph.advance(); - if( (pos.x + advance.x) > rect.width() ) { + if( (pos.x() + advance.x()) > rect.width() ) { crlf(); } const Point pos_glyph { - rect.pos.x + pos.x, - display.scroll_area_y(pos.y) + rect.pos.x() + pos.x(), + display.scroll_area_y(pos.y()) }; display.draw_glyph(pos_glyph, glyph, s.foreground, s.background); - pos.x += advance.x; + pos += { advance.x(), 0 }; } } } @@ -86,14 +86,13 @@ void Console::crlf() { const Style& s = style(); const auto sr = screen_rect(); const auto line_height = s.font.line_height(); - pos.x = 0; - pos.y += line_height; - const int32_t y_excess = pos.y + line_height - sr.height(); + pos = { 0, pos.y() + line_height }; + const int32_t y_excess = pos.y() + line_height - sr.height(); if( y_excess > 0 ) { display.scroll(-y_excess); - pos.y -= y_excess; + pos = { pos.x(), pos.y() - y_excess }; - const Rect dirty { sr.left(), display.scroll_area_y(pos.y), sr.width(), line_height }; + const Rect dirty { sr.left(), display.scroll_area_y(pos.y()), sr.width(), line_height }; display.fill_rectangle(dirty, s.background); } } diff --git a/firmware/application/ui_menu.cpp b/firmware/application/ui_menu.cpp index 4a04e817..1406875b 100644 --- a/firmware/application/ui_menu.cpp +++ b/firmware/application/ui_menu.cpp @@ -54,7 +54,7 @@ void MenuItemView::paint(Painter& painter) { ); painter.draw_string( - { r.pos.x + 8, r.pos.y + (r.size.h - font_height) / 2 }, + { r.pos.x() + 8, r.pos.y() + (r.size.h - font_height) / 2 }, paint_style, item.text ); diff --git a/firmware/application/ui_touch_calibration.cpp b/firmware/application/ui_touch_calibration.cpp index 37cd0df8..a5a7c348 100644 --- a/firmware/application/ui_touch_calibration.cpp +++ b/firmware/application/ui_touch_calibration.cpp @@ -92,8 +92,8 @@ void TouchCalibrationView::set_phase(const Phase value) { uint32_t TouchCalibrationView::distance_squared(const Point& touch_point, const Image& target) { const auto target_point = target.screen_rect().center(); - const int32_t dx = target_point.x - touch_point.x; - const int32_t dy = target_point.y - touch_point.y; + const int32_t dx = target_point.x() - touch_point.x(); + const int32_t dy = target_point.y() - touch_point.y(); const uint32_t dx2 = dx * dx; const uint32_t dy2 = dy * dy; return dx2 + dy2; diff --git a/firmware/common/lcd_ili9341.cpp b/firmware/common/lcd_ili9341.cpp index ddda0f8c..68a04ef9 100644 --- a/firmware/common/lcd_ili9341.cpp +++ b/firmware/common/lcd_ili9341.cpp @@ -196,8 +196,8 @@ void lcd_start_ram_write( const ui::Point p, const ui::Size s ) { - lcd_caset(p.x, p.x + s.w - 1); - lcd_paset(p.y, p.y + s.h - 1); + lcd_caset(p.x(), p.x() + s.w - 1); + lcd_paset(p.y(), p.y() + s.h - 1); lcd_ramwr_start(); } @@ -205,8 +205,8 @@ void lcd_start_ram_read( const ui::Point p, const ui::Size s ) { - lcd_caset(p.x, p.x + s.w - 1); - lcd_paset(p.y, p.y + s.h - 1); + lcd_caset(p.x(), p.x() + s.w - 1); + lcd_paset(p.y(), p.y() + s.h - 1); lcd_ramrd_start(); } @@ -292,8 +292,8 @@ void ILI9341::fill_circle( const bool inside = d2 < radius2; const auto color = inside ? foreground : background; draw_pixel({ - static_cast(x + center.x), - static_cast(y + center.y) + static_cast(x + center.x()), + static_cast(y + center.y()) }, color); } } diff --git a/firmware/common/ui.cpp b/firmware/common/ui.cpp index 5b0f55b2..a9a3eae9 100644 --- a/firmware/common/ui.cpp +++ b/firmware/common/ui.cpp @@ -26,8 +26,8 @@ namespace ui { bool Rect::contains(const Point p) const { - return (p.x >= left()) && (p.y >= top()) && - (p.x < right()) && (p.y < bottom()); + return (p.x() >= left()) && (p.y() >= top()) && + (p.x() < right()) && (p.y() < bottom()); } Rect Rect::intersect(const Rect& o) const { @@ -64,4 +64,9 @@ Rect& Rect::operator+=(const Point& p) { return *this; } +Rect& Rect::operator-=(const Point& p) { + pos -= p; + return *this; +} + } /* namespace ui */ diff --git a/firmware/common/ui.hpp b/firmware/common/ui.hpp index f363a841..fb331f12 100644 --- a/firmware/common/ui.hpp +++ b/firmware/common/ui.hpp @@ -82,38 +82,54 @@ struct ColorRGB888 { }; struct Point { - Coord x; - Coord y; +private: + Coord _x; + Coord _y; +public: constexpr Point( - ) : x { 0 }, - y { 0 } + ) : _x { 0 }, + _y { 0 } { } constexpr Point( int x, int y - ) : x { static_cast(x) }, - y { static_cast(y) } + ) : _x { static_cast(x) }, + _y { static_cast(y) } { } - Point operator-() const { - return { -x, -y }; + constexpr int x() const { + return _x; } - Point operator+(const Point& p) const { - return { x + p.x, y + p.y }; + constexpr int y() const { + return _y; } - Point operator-(const Point& p) const { - return { x - p.x, y - p.y }; + constexpr Point operator-() const { + return { -_x, -_y }; + } + + constexpr Point operator+(const Point& p) const { + return { _x + p._x, _y + p._y }; + } + + constexpr Point operator-(const Point& p) const { + return { _x - p._x, _y - p._y }; } Point& operator+=(const Point& p) { - x += p.x; - y += p.y; + _x += p._x; + _y += p._y; + return *this; + } + + Point& operator-=(const Point& p) { + _x -= p._x; + _y -= p._y; return *this; } }; @@ -168,19 +184,19 @@ struct Rect { } int top() const { - return pos.y; + return pos.y(); } int bottom() const { - return pos.y + size.h; + return pos.y() + size.h; } int left() const { - return pos.x; + return pos.x(); } int right() const { - return pos.x + size.w; + return pos.x() + size.w; } int width() const { @@ -192,7 +208,7 @@ struct Rect { } Point center() const { - return { pos.x + size.w / 2, pos.y + size.h / 2 }; + return { pos.x() + size.w / 2, pos.y() + size.h / 2 }; } bool is_empty() const { @@ -209,6 +225,7 @@ struct Rect { Rect& operator+=(const Rect& p); Rect& operator+=(const Point& p); + Rect& operator-=(const Point& p); operator bool() const { return !size.is_empty(); diff --git a/firmware/common/ui_focus.cpp b/firmware/common/ui_focus.cpp index 92547d35..48d30b02 100644 --- a/firmware/common/ui_focus.cpp +++ b/firmware/common/ui_focus.cpp @@ -127,14 +127,14 @@ static int32_t rect_distances( switch(direction) { case KeyEvent::Right: case KeyEvent::Left: - perpendicular_axis_start = rect_start.center().y; - perpendicular_axis_end = rect_end.center().y; + perpendicular_axis_start = rect_start.center().y(); + perpendicular_axis_end = rect_end.center().y(); break; case KeyEvent::Up: case KeyEvent::Down: - perpendicular_axis_start = rect_start.center().x; - perpendicular_axis_end = rect_end.center().x; + perpendicular_axis_start = rect_start.center().x(); + perpendicular_axis_end = rect_end.center().x(); break; default: diff --git a/firmware/common/ui_painter.cpp b/firmware/common/ui_painter.cpp index 2b54e194..6ad11a0b 100644 --- a/firmware/common/ui_painter.cpp +++ b/firmware/common/ui_painter.cpp @@ -39,7 +39,7 @@ Style Style::invert() const { int Painter::draw_char(const Point p, const Style& style, const char c) { const auto glyph = style.font.glyph(c); display.draw_glyph(p, glyph, style.foreground, style.background); - return glyph.advance().x; + return glyph.advance().x(); } int Painter::draw_string(Point p, const Style& style, const std::string text) { @@ -49,7 +49,7 @@ int Painter::draw_string(Point p, const Style& style, const std::string text) { display.draw_glyph(p, glyph, style.foreground, style.background); const auto advance = glyph.advance(); p += advance; - width += advance.x; + width += advance.x(); } return width; } @@ -68,9 +68,9 @@ void Painter::draw_vline(Point p, int height, const Color c) { void Painter::draw_rectangle(const Rect r, const Color c) { draw_hline(r.pos, r.size.w, c); - draw_vline({ r.pos.x, r.pos.y + 1 }, r.size.h - 2, c); - draw_vline({ r.pos.x + r.size.w - 1, r.pos.y + 1 }, r.size.h - 2, c); - draw_hline({ r.pos.x, r.pos.y + r.size.h - 1 }, r.size.w, c); + draw_vline({ r.pos.x(), r.pos.y() + 1 }, r.size.h - 2, c); + draw_vline({ r.pos.x() + r.size.w - 1, r.pos.y() + 1 }, r.size.h - 2, c); + draw_hline({ r.pos.x(), r.pos.y() + r.size.h - 1 }, r.size.w, c); } void Painter::fill_rectangle(const Rect r, const Color c) { diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 94716753..a84b731a 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -351,13 +351,13 @@ void Button::paint(Painter& painter) { painter.draw_rectangle(r, style().foreground); painter.fill_rectangle( - { r.pos.x + 1, r.pos.y + 1, r.size.w - 2, r.size.h - 2 }, + { r.pos.x() + 1, r.pos.y() + 1, r.size.w - 2, r.size.h - 2 }, paint_style.background ); const auto label_r = paint_style.font.size_of(text_); painter.draw_string( - { r.pos.x + (r.size.w - label_r.w) / 2, r.pos.y + (r.size.h - label_r.h) / 2 }, + { r.pos.x() + (r.size.w - label_r.w) / 2, r.pos.y() + (r.size.h - label_r.h) / 2 }, paint_style, text_ ); From d15ace46766d8ade8a4451c9d3c2f767aa98ff00 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 28 Nov 2016 10:55:45 -0800 Subject: [PATCH 082/100] Hide ui::Size implementation. --- firmware/application/ui_debug.cpp | 2 +- firmware/application/ui_menu.cpp | 4 ++-- firmware/application/ui_spectrum.cpp | 2 +- firmware/common/lcd_ili9341.cpp | 12 +++++----- firmware/common/ui.hpp | 34 ++++++++++++++++++---------- firmware/common/ui_painter.cpp | 8 +++---- firmware/common/ui_text.cpp | 6 +++-- firmware/common/ui_text.hpp | 4 ++-- firmware/common/ui_widget.cpp | 4 ++-- 9 files changed, 44 insertions(+), 32 deletions(-) diff --git a/firmware/application/ui_debug.cpp b/firmware/application/ui_debug.cpp index 6e1bdcca..11aff219 100644 --- a/firmware/application/ui_debug.cpp +++ b/firmware/application/ui_debug.cpp @@ -164,7 +164,7 @@ void RegistersWidget::update() { } void RegistersWidget::paint(Painter& painter) { - const Coord left = (size().w - config.row_width()) / 2; + const Coord left = (size().width() - config.row_width()) / 2; draw_legend(left, painter); draw_values(left, painter); diff --git a/firmware/application/ui_menu.cpp b/firmware/application/ui_menu.cpp index 1406875b..2d6bf7ff 100644 --- a/firmware/application/ui_menu.cpp +++ b/firmware/application/ui_menu.cpp @@ -54,7 +54,7 @@ void MenuItemView::paint(Painter& painter) { ); painter.draw_string( - { r.pos.x() + 8, r.pos.y() + (r.size.h - font_height) / 2 }, + { r.pos.x() + 8, r.pos.y() + (r.size.height() - font_height) / 2 }, paint_style, item.text ); @@ -87,7 +87,7 @@ void MenuView::set_parent_rect(const Rect new_parent_rect) { for(auto child : children_) { child->set_parent_rect({ { 0, static_cast(i * item_height) }, - { size().w, item_height } + { size().width(), item_height } }); i++; } diff --git a/firmware/application/ui_spectrum.cpp b/firmware/application/ui_spectrum.cpp index 448e0766..07739ee0 100644 --- a/firmware/application/ui_spectrum.cpp +++ b/firmware/application/ui_spectrum.cpp @@ -112,7 +112,7 @@ void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) { (magnitude_n >= 6) ? "M" : (magnitude_n >= 3) ? "k" : ""; const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit; - const auto label_width = style().font.size_of(label).w; + const auto label_width = style().font.size_of(label).width(); const Coord offset_low = r.left() + x_center - pixel_offset; const Rect tick_low { offset_low, r.top(), 1, r.height() }; diff --git a/firmware/common/lcd_ili9341.cpp b/firmware/common/lcd_ili9341.cpp index 68a04ef9..77868949 100644 --- a/firmware/common/lcd_ili9341.cpp +++ b/firmware/common/lcd_ili9341.cpp @@ -196,8 +196,8 @@ void lcd_start_ram_write( const ui::Point p, const ui::Size s ) { - lcd_caset(p.x(), p.x() + s.w - 1); - lcd_paset(p.y(), p.y() + s.h - 1); + lcd_caset(p.x(), p.x() + s.width() - 1); + lcd_paset(p.y(), p.y() + s.height() - 1); lcd_ramwr_start(); } @@ -205,8 +205,8 @@ void lcd_start_ram_read( const ui::Point p, const ui::Size s ) { - lcd_caset(p.x(), p.x() + s.w - 1); - lcd_paset(p.y(), p.y() + s.h - 1); + lcd_caset(p.x(), p.x() + s.width() - 1); + lcd_paset(p.y(), p.y() + s.height() - 1); lcd_ramrd_start(); } @@ -272,7 +272,7 @@ void ILI9341::fill_rectangle(ui::Rect r, const ui::Color c) { const auto r_clipped = r.intersect(screen_rect()); if( !r_clipped.is_empty() ) { lcd_start_ram_write(r_clipped); - size_t count = r_clipped.size.w * r_clipped.size.h; + size_t count = r_clipped.size.width() * r_clipped.size.height(); io.lcd_write_pixels(c, count); } } @@ -341,7 +341,7 @@ void ILI9341::draw_bitmap( ) { lcd_start_ram_write(p, size); - const size_t count = size.w * size.h; + const size_t count = size.width() * size.height(); for(size_t i=0; i> 3] & (1U << (i & 0x7)); io.lcd_write_pixel(pixel ? foreground : background); diff --git a/firmware/common/ui.hpp b/firmware/common/ui.hpp index fb331f12..8861eb7c 100644 --- a/firmware/common/ui.hpp +++ b/firmware/common/ui.hpp @@ -135,25 +135,35 @@ public: }; struct Size { - Dim w; - Dim h; +private: + Dim _w; + Dim _h; +public: constexpr Size( - ) : w { 0 }, - h { 0 } + ) : _w { 0 }, + _h { 0 } { } constexpr Size( int w, int h - ) : w { static_cast(w) }, - h { static_cast(h) } + ) : _w { static_cast(w) }, + _h { static_cast(h) } { } + int width() const { + return _w; + } + + int height() const { + return _h; + } + bool is_empty() const { - return (w < 1) || (h < 1); + return (_w < 1) || (_h < 1); } }; @@ -188,7 +198,7 @@ struct Rect { } int bottom() const { - return pos.y() + size.h; + return pos.y() + size.height(); } int left() const { @@ -196,19 +206,19 @@ struct Rect { } int right() const { - return pos.x() + size.w; + return pos.x() + size.width(); } int width() const { - return size.w; + return size.width(); } int height() const { - return size.h; + return size.height(); } Point center() const { - return { pos.x() + size.w / 2, pos.y() + size.h / 2 }; + return { pos.x() + size.width() / 2, pos.y() + size.height() / 2 }; } bool is_empty() const { diff --git a/firmware/common/ui_painter.cpp b/firmware/common/ui_painter.cpp index 6ad11a0b..47f5b02b 100644 --- a/firmware/common/ui_painter.cpp +++ b/firmware/common/ui_painter.cpp @@ -67,10 +67,10 @@ void Painter::draw_vline(Point p, int height, const Color c) { } void Painter::draw_rectangle(const Rect r, const Color c) { - draw_hline(r.pos, r.size.w, c); - draw_vline({ r.pos.x(), r.pos.y() + 1 }, r.size.h - 2, c); - draw_vline({ r.pos.x() + r.size.w - 1, r.pos.y() + 1 }, r.size.h - 2, c); - draw_hline({ r.pos.x(), r.pos.y() + r.size.h - 1 }, r.size.w, c); + draw_hline(r.pos, r.size.width(), c); + draw_vline({ r.pos.x(), r.pos.y() + 1 }, r.size.height() - 2, c); + draw_vline({ r.pos.x() + r.size.width() - 1, r.pos.y() + 1 }, r.size.height() - 2, c); + draw_hline({ r.pos.x(), r.pos.y() + r.size.height() - 1 }, r.size.width(), c); } void Painter::fill_rectangle(const Rect r, const Color c) { diff --git a/firmware/common/ui_text.cpp b/firmware/common/ui_text.cpp index 0f55c5f6..e926ba0d 100644 --- a/firmware/common/ui_text.cpp +++ b/firmware/common/ui_text.cpp @@ -44,8 +44,10 @@ Size Font::size_of(const std::string s) const { for(const auto c : s) { const auto glyph_data = glyph(c); - size.w += glyph_data.w(); - size.h = std::max(size.h, glyph_data.h()); + size = { + size.width() + glyph_data.w(), + std::max(size.height(), glyph_data.h()) + }; } return size; diff --git a/firmware/common/ui_text.hpp b/firmware/common/ui_text.hpp index ae3c94b7..d60c746f 100644 --- a/firmware/common/ui_text.hpp +++ b/firmware/common/ui_text.hpp @@ -42,11 +42,11 @@ public: { } - Dim w() const { + int w() const { return w_; } - Dim h() const { + int h() const { return h_; } diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index a84b731a..897c9b85 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -351,13 +351,13 @@ void Button::paint(Painter& painter) { painter.draw_rectangle(r, style().foreground); painter.fill_rectangle( - { r.pos.x() + 1, r.pos.y() + 1, r.size.w - 2, r.size.h - 2 }, + { r.pos.x() + 1, r.pos.y() + 1, r.size.width() - 2, r.size.height() - 2 }, paint_style.background ); const auto label_r = paint_style.font.size_of(text_); painter.draw_string( - { r.pos.x() + (r.size.w - label_r.w) / 2, r.pos.y() + (r.size.h - label_r.h) / 2 }, + { r.pos.x() + (r.size.width() - label_r.width()) / 2, r.pos.y() + (r.size.height() - label_r.height()) / 2 }, paint_style, text_ ); From e820bed09746419b36a285178c5f2991c464c34e Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 28 Nov 2016 11:25:27 -0800 Subject: [PATCH 083/100] Hide ui::Rect implementation. --- firmware/application/ais_app.cpp | 2 +- firmware/application/ert_app.cpp | 2 +- firmware/application/recent_entries.cpp | 2 +- firmware/application/recent_entries.hpp | 2 +- firmware/application/tpms_app.cpp | 2 +- firmware/application/ui_console.cpp | 2 +- firmware/application/ui_menu.cpp | 2 +- firmware/common/lcd_ili9341.cpp | 8 ++--- firmware/common/ui.cpp | 8 ++--- firmware/common/ui.hpp | 46 +++++++++++++++---------- firmware/common/ui_painter.cpp | 8 ++--- firmware/common/ui_widget.cpp | 10 +++--- 12 files changed, 52 insertions(+), 42 deletions(-) diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index 8a53da71..0823d050 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -200,7 +200,7 @@ void RecentEntriesTable::draw( } line.resize(target_rect.width() / 8, ' '); - painter.draw_string(target_rect.pos, style, line); + painter.draw_string(target_rect.location(), style, line); } AISRecentEntryDetailView::AISRecentEntryDetailView() { diff --git a/firmware/application/ert_app.cpp b/firmware/application/ert_app.cpp index 5df0f336..91b3e5e1 100644 --- a/firmware/application/ert_app.cpp +++ b/firmware/application/ert_app.cpp @@ -91,7 +91,7 @@ void RecentEntriesTable::draw( } line.resize(target_rect.width() / 8, ' '); - painter.draw_string(target_rect.pos, style, line); + painter.draw_string(target_rect.location(), style, line); } ERTAppView::ERTAppView(NavigationView&) { diff --git a/firmware/application/recent_entries.cpp b/firmware/application/recent_entries.cpp index 0521238a..bc6ef17a 100644 --- a/firmware/application/recent_entries.cpp +++ b/firmware/application/recent_entries.cpp @@ -45,7 +45,7 @@ void RecentEntriesHeader::paint(Painter& painter) { .foreground = parent_style.foreground, }; - auto p = r.pos; + auto p = r.location(); for(const auto& column : _columns) { const auto width = column.second; auto text = column.first; diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 335cf0b7..90fd5203 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -140,7 +140,7 @@ public: const auto r = screen_rect(); const auto& s = style(); - Rect target_rect { r.pos, { r.width(), s.font.line_height() }}; + Rect target_rect { r.location(), { r.width(), s.font.line_height() }}; const size_t visible_item_count = r.height() / s.font.line_height(); auto selected = find(recent, selected_key); diff --git a/firmware/application/tpms_app.cpp b/firmware/application/tpms_app.cpp index 7fe5ab6f..82cdbd3f 100644 --- a/firmware/application/tpms_app.cpp +++ b/firmware/application/tpms_app.cpp @@ -129,7 +129,7 @@ void RecentEntriesTable::draw( } line.resize(target_rect.width() / 8, ' '); - painter.draw_string(target_rect.pos, style, line); + painter.draw_string(target_rect.location(), style, line); } TPMSAppView::TPMSAppView(NavigationView&) { diff --git a/firmware/application/ui_console.cpp b/firmware/application/ui_console.cpp index af71ee14..fc37b8ce 100644 --- a/firmware/application/ui_console.cpp +++ b/firmware/application/ui_console.cpp @@ -49,7 +49,7 @@ void Console::write(const std::string& message) { crlf(); } const Point pos_glyph { - rect.pos.x() + pos.x(), + rect.left() + pos.x(), display.scroll_area_y(pos.y()) }; display.draw_glyph(pos_glyph, glyph, s.foreground, s.background); diff --git a/firmware/application/ui_menu.cpp b/firmware/application/ui_menu.cpp index 2d6bf7ff..7498a611 100644 --- a/firmware/application/ui_menu.cpp +++ b/firmware/application/ui_menu.cpp @@ -54,7 +54,7 @@ void MenuItemView::paint(Painter& painter) { ); painter.draw_string( - { r.pos.x() + 8, r.pos.y() + (r.size.height() - font_height) / 2 }, + { r.left() + 8, r.top() + (r.height() - font_height) / 2 }, paint_style, item.text ); diff --git a/firmware/common/lcd_ili9341.cpp b/firmware/common/lcd_ili9341.cpp index 77868949..db434d6c 100644 --- a/firmware/common/lcd_ili9341.cpp +++ b/firmware/common/lcd_ili9341.cpp @@ -213,13 +213,13 @@ void lcd_start_ram_read( void lcd_start_ram_write( const ui::Rect& r ) { - lcd_start_ram_write(r.pos, r.size); + lcd_start_ram_write(r.location(), r.size()); } void lcd_start_ram_read( const ui::Rect& r ) { - lcd_start_ram_read(r.pos, r.size); + lcd_start_ram_read(r.location(), r.size()); } void lcd_vertical_scrolling_definition( @@ -272,7 +272,7 @@ void ILI9341::fill_rectangle(ui::Rect r, const ui::Color c) { const auto r_clipped = r.intersect(screen_rect()); if( !r_clipped.is_empty() ) { lcd_start_ram_write(r_clipped); - size_t count = r_clipped.size.width() * r_clipped.size.height(); + size_t count = r_clipped.width() * r_clipped.height(); io.lcd_write_pixels(c, count); } } @@ -315,7 +315,7 @@ void ILI9341::draw_pixels( const size_t count ) { /* TODO: Assert that rectangle width x height < count */ - lcd_start_ram_write(r.pos, r.size); + lcd_start_ram_write(r); io.lcd_write_pixels(colors, count); } diff --git a/firmware/common/ui.cpp b/firmware/common/ui.cpp index a9a3eae9..06255c2d 100644 --- a/firmware/common/ui.cpp +++ b/firmware/common/ui.cpp @@ -51,21 +51,21 @@ Rect& Rect::operator+=(const Rect& p) { if( !p.is_empty() ) { const auto x1 = std::min(left(), p.left()); const auto y1 = std::min(top(), p.top()); - pos = { x1, y1 }; + _pos = { x1, y1 }; const auto x2 = std::max(right(), p.right()); const auto y2 = std::max(bottom(), p.bottom()); - size = { x2 - x1, y2 - y1 }; + _size = { x2 - x1, y2 - y1 }; } return *this; } Rect& Rect::operator+=(const Point& p) { - pos += p; + _pos += p; return *this; } Rect& Rect::operator-=(const Point& p) { - pos -= p; + _pos -= p; return *this; } diff --git a/firmware/common/ui.hpp b/firmware/common/ui.hpp index 8861eb7c..368f4959 100644 --- a/firmware/common/ui.hpp +++ b/firmware/common/ui.hpp @@ -168,61 +168,71 @@ public: }; struct Rect { - Point pos; - Size size; +private: + Point _pos; + Size _size; +public: constexpr Rect( - ) : pos { }, - size { } + ) : _pos { }, + _size { } { } constexpr Rect( int x, int y, int w, int h - ) : pos { x, y }, - size { w, h } + ) : _pos { x, y }, + _size { w, h } { } constexpr Rect( Point pos, Size size - ) : pos(pos), - size(size) + ) : _pos(pos), + _size(size) { } + Point location() const { + return _pos; + } + + Size size() const { + return _size; + } + int top() const { - return pos.y(); + return _pos.y(); } int bottom() const { - return pos.y() + size.height(); + return _pos.y() + _size.height(); } int left() const { - return pos.x(); + return _pos.x(); } int right() const { - return pos.x() + size.width(); + return _pos.x() + _size.width(); } int width() const { - return size.width(); + return _size.width(); } int height() const { - return size.height(); + return _size.height(); } Point center() const { - return { pos.x() + size.width() / 2, pos.y() + size.height() / 2 }; + return { _pos.x() + _size.width() / 2, _pos.y() + _size.height() / 2 }; } bool is_empty() const { - return size.is_empty(); + return _size.is_empty(); } bool contains(const Point p) const; @@ -230,7 +240,7 @@ struct Rect { Rect intersect(const Rect& o) const; Rect operator+(const Point& p) const { - return { pos + p, size }; + return { _pos + p, _size }; } Rect& operator+=(const Rect& p); @@ -238,7 +248,7 @@ struct Rect { Rect& operator-=(const Point& p); operator bool() const { - return !size.is_empty(); + return !_size.is_empty(); } }; diff --git a/firmware/common/ui_painter.cpp b/firmware/common/ui_painter.cpp index 47f5b02b..97f223a2 100644 --- a/firmware/common/ui_painter.cpp +++ b/firmware/common/ui_painter.cpp @@ -67,10 +67,10 @@ void Painter::draw_vline(Point p, int height, const Color c) { } void Painter::draw_rectangle(const Rect r, const Color c) { - draw_hline(r.pos, r.size.width(), c); - draw_vline({ r.pos.x(), r.pos.y() + 1 }, r.size.height() - 2, c); - draw_vline({ r.pos.x() + r.size.width() - 1, r.pos.y() + 1 }, r.size.height() - 2, c); - draw_hline({ r.pos.x(), r.pos.y() + r.size.height() - 1 }, r.size.width(), c); + draw_hline(r.location(), r.width(), c); + draw_vline({ r.left(), r.top() + 1 }, r.height() - 2, c); + draw_vline({ r.left() + r.width() - 1, r.top() + 1 }, r.height() - 2, c); + draw_hline({ r.left(), r.top() + r.height() - 1 }, r.width(), c); } void Painter::fill_rectangle(const Rect r, const Color c) { diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 897c9b85..61abaecf 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -49,11 +49,11 @@ bool is_dirty() { const std::vector Widget::no_children { }; Point Widget::screen_pos() { - return screen_rect().pos; + return screen_rect().location(); } Size Widget::size() const { - return parent_rect.size; + return parent_rect.size(); } Rect Widget::screen_rect() const { @@ -317,7 +317,7 @@ void Text::paint(Painter& painter) { painter.fill_rectangle(rect, s.background); painter.draw_string( - rect.pos, + rect.location(), s, text ); @@ -351,13 +351,13 @@ void Button::paint(Painter& painter) { painter.draw_rectangle(r, style().foreground); painter.fill_rectangle( - { r.pos.x() + 1, r.pos.y() + 1, r.size.width() - 2, r.size.height() - 2 }, + { r.left() + 1, r.top() + 1, r.width() - 2, r.height() - 2 }, paint_style.background ); const auto label_r = paint_style.font.size_of(text_); painter.draw_string( - { r.pos.x() + (r.size.width() - label_r.width()) / 2, r.pos.y() + (r.size.height() - label_r.height()) / 2 }, + { r.left() + (r.width() - label_r.width()) / 2, r.top() + (r.height() - label_r.height()) / 2 }, paint_style, text_ ); From 86d2576d3e76008580a7255114ba29c1960632f6 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 28 Nov 2016 12:00:56 -0800 Subject: [PATCH 084/100] Fix types on touch Filter accumulator/value. --- firmware/application/touch.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/application/touch.hpp b/firmware/application/touch.hpp index 549d3809..ebea3c92 100644 --- a/firmware/application/touch.hpp +++ b/firmware/application/touch.hpp @@ -171,7 +171,7 @@ public: history_history = (history_history << 1) | 1U; } - uint32_t value() const { + int32_t value() const { return accumulator / N; } @@ -192,7 +192,7 @@ private: std::array history { }; uint32_t history_history { 0 }; - uint32_t accumulator { 0 }; + int32_t accumulator { 0 }; size_t n { 0 }; bool history_valid() const { From 227719ff1de77635ab5fa34516fbd96f0c264f11 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 28 Nov 2016 12:05:24 -0800 Subject: [PATCH 085/100] Fix static_cast warning. --- firmware/common/tpms_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/common/tpms_packet.cpp b/firmware/common/tpms_packet.cpp index 0bc3064b..8cca23f2 100644 --- a/firmware/common/tpms_packet.cpp +++ b/firmware/common/tpms_packet.cpp @@ -88,7 +88,7 @@ Optional Packet::reading_ook_8k192_schrader() const { reader_.read(3, 24), Pressure { static_cast(reader_.read(27, 8)) * 4 / 3 }, { }, - Flags { (flags << 4) | checksum } + Flags { static_cast((flags << 4) | checksum) } }; } else { return { }; From 4c8550bb7d68f2d0f71749d2fd67282a66ba90b3 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Mon, 28 Nov 2016 12:09:02 -0800 Subject: [PATCH 086/100] Fix narrowing conversion warnings. --- firmware/baseband/ook.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/baseband/ook.hpp b/firmware/baseband/ook.hpp index 2453f2f4..04324fe6 100644 --- a/firmware/baseband/ook.hpp +++ b/firmware/baseband/ook.hpp @@ -70,8 +70,8 @@ public: constexpr OOKClockRecovery( const float samples_per_symbol ) : symbol_phase_inc_nominal { static_cast(std::round((1ULL << 32) / samples_per_symbol)) }, - symbol_phase_inc_k { symbol_phase_inc_nominal * (2.0f / 8.0f) / samples_per_symbol }, - phase_detector { samples_per_symbol }, + symbol_phase_inc_k { static_cast(std::round(symbol_phase_inc_nominal * (2.0f / 8.0f) / samples_per_symbol)) }, + phase_detector { static_cast(std::round(samples_per_symbol)) }, phase_accumulator { symbol_phase_inc_nominal } { } From 3f94591083ce995d2e2a3d14b8a278624e02c24a Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 29 Nov 2016 10:13:56 -0800 Subject: [PATCH 087/100] Remove a lot of static_cast<>s involving UI structs. Also starting to get religion on using unsigned integers only when I want their wrapping/modulus behavior. --- firmware/application/analog_audio_app.cpp | 2 +- firmware/application/capture_app.cpp | 2 +- firmware/application/touch.cpp | 5 +---- firmware/application/ui_audio.cpp | 20 ++++-------------- firmware/application/ui_channel.cpp | 15 +++----------- firmware/application/ui_menu.cpp | 9 +++----- firmware/application/ui_navigation.cpp | 5 +---- firmware/application/ui_rssi.cpp | 25 +++++------------------ firmware/common/lcd_ili9341.cpp | 5 +---- firmware/common/ui_widget.cpp | 8 ++++---- firmware/common/ui_widget.hpp | 8 ++++---- 11 files changed, 28 insertions(+), 76 deletions(-) diff --git a/firmware/application/analog_audio_app.cpp b/firmware/application/analog_audio_app.cpp index 0a6009ca..0f2c41f9 100644 --- a/firmware/application/analog_audio_app.cpp +++ b/firmware/application/analog_audio_app.cpp @@ -161,7 +161,7 @@ void AnalogAudioView::on_hide() { void AnalogAudioView::set_parent_rect(const Rect new_parent_rect) { View::set_parent_rect(new_parent_rect); - const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), static_cast(new_parent_rect.height() - header_height) }; + const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; waterfall.set_parent_rect(waterfall_rect); } diff --git a/firmware/application/capture_app.cpp b/firmware/application/capture_app.cpp index cd2a3824..e5b9a36c 100644 --- a/firmware/application/capture_app.cpp +++ b/firmware/application/capture_app.cpp @@ -98,7 +98,7 @@ void CaptureAppView::on_hide() { void CaptureAppView::set_parent_rect(const Rect new_parent_rect) { View::set_parent_rect(new_parent_rect); - const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), static_cast(new_parent_rect.height() - header_height) }; + const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; waterfall.set_parent_rect(waterfall_rect); } diff --git a/firmware/application/touch.cpp b/firmware/application/touch.cpp index ac48c053..d3fc6c92 100644 --- a/firmware/application/touch.cpp +++ b/firmware/application/touch.cpp @@ -73,10 +73,7 @@ ui::Point Calibration::translate(const DigitizerPoint& p) const { const int32_t y = (d * p.x + e * p.y + f) / k; const auto x_clipped = x_range.clip(x); const auto y_clipped = y_range.clip(y); - return { - static_cast(x_clipped), - static_cast(y_clipped) - }; + return { x_clipped, y_clipped }; } const Calibration default_calibration() { diff --git a/firmware/application/ui_audio.cpp b/firmware/application/ui_audio.cpp index 389a65e1..1f51c3a3 100644 --- a/firmware/application/ui_audio.cpp +++ b/firmware/application/ui_audio.cpp @@ -38,37 +38,25 @@ void Audio::paint(Painter& painter) { const range_t x_max_range { x_rms + 1, r.width() }; const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta); - const Rect r0 { - static_cast(r.left()), r.top(), - static_cast(x_rms), r.height() - }; + const Rect r0 { r.left(), r.top(), x_rms, r.height() }; painter.fill_rectangle( r0, Color::green() ); - const Rect r1 { - static_cast(r.left() + x_rms), r.top(), - 1, r.height() - }; + const Rect r1 { r.left() + x_rms, r.top(), 1, r.height() }; painter.fill_rectangle( r1, Color::black() ); - const Rect r2 { - static_cast(r.left() + x_rms + 1), r.top(), - static_cast(x_max - (x_rms + 1)), r.height() - }; + const Rect r2 { r.left() + x_rms + 1, r.top(), x_max - (x_rms + 1), r.height() }; painter.fill_rectangle( r2, Color::red() ); - const Rect r3 { - static_cast(r.left() + x_max), r.top(), - static_cast(r.width() - x_max), r.height() - }; + const Rect r3 { r.left() + x_max, r.top(), r.width() - x_max, r.height() }; painter.fill_rectangle( r3, Color::black() diff --git a/firmware/application/ui_channel.cpp b/firmware/application/ui_channel.cpp index 3d21e535..227d71fe 100644 --- a/firmware/application/ui_channel.cpp +++ b/firmware/application/ui_channel.cpp @@ -36,28 +36,19 @@ void Channel::paint(Painter& painter) { const range_t x_max_range { 0, r.width() - 1 }; const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta); - const Rect r0 { - static_cast(r.left()), r.top(), - static_cast(x_max), r.height() - }; + const Rect r0 { r.left(), r.top(), x_max, r.height() }; painter.fill_rectangle( r0, Color::blue() ); - const Rect r1 { - static_cast(r.left() + x_max), r.top(), - 1, r.height() - }; + const Rect r1 { r.left() + x_max, r.top(), 1, r.height() }; painter.fill_rectangle( r1, Color::white() ); - const Rect r2 { - static_cast(r.left() + x_max + 1), r.top(), - static_cast(r.width() - (x_max + 1)), r.height() - }; + const Rect r2 { r.left() + x_max + 1, r.top(), r.width() - (x_max + 1), r.height() }; painter.fill_rectangle( r2, Color::black() diff --git a/firmware/application/ui_menu.cpp b/firmware/application/ui_menu.cpp index 7498a611..f7ce7031 100644 --- a/firmware/application/ui_menu.cpp +++ b/firmware/application/ui_menu.cpp @@ -82,13 +82,10 @@ void MenuView::add_items(std::initializer_list items) { void MenuView::set_parent_rect(const Rect new_parent_rect) { View::set_parent_rect(new_parent_rect); - constexpr size_t item_height = 24; - size_t i = 0; + constexpr int item_height = 24; + int i = 0; for(auto child : children_) { - child->set_parent_rect({ - { 0, static_cast(i * item_height) }, - { size().width(), item_height } - }); + child->set_parent_rect({ 0, i * item_height, size().width(), item_height }); i++; } } diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 8572d6d7..d6a35521 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -230,10 +230,7 @@ SystemView::SystemView( }; add_child(&navigation_view); - navigation_view.set_parent_rect({ - { 0, status_view_height }, - { parent_rect.width(), static_cast(parent_rect.height() - status_view_height) } - }); + navigation_view.set_parent_rect({ 0, status_view_height, parent_rect.width(), parent_rect.height() - status_view_height }); navigation_view.on_view_changed = [this](const View& new_view) { this->status_view.set_back_enabled(!this->navigation_view.is_top()); this->status_view.set_title(new_view.title()); diff --git a/firmware/application/ui_rssi.cpp b/firmware/application/ui_rssi.cpp index ab506906..36e8dacf 100644 --- a/firmware/application/ui_rssi.cpp +++ b/firmware/application/ui_rssi.cpp @@ -44,46 +44,31 @@ void RSSI::paint(Painter& painter) { const range_t x_max_range { x_avg + 1, r.width() }; const auto x_max = x_max_range.clip((max_ - raw_min) * r.width() / raw_delta); - const Rect r0 { - static_cast(r.left()), r.top(), - static_cast(x_min), r.height() - }; + const Rect r0 { r.left(), r.top(), x_min, r.height() }; painter.fill_rectangle( r0, Color::blue() ); - const Rect r1 { - static_cast(r.left() + x_min), r.top(), - static_cast(x_avg - x_min), r.height() - }; + const Rect r1 { r.left() + x_min, r.top(), x_avg - x_min, r.height() }; painter.fill_rectangle( r1, Color::red() ); - const Rect r2 { - static_cast(r.left() + x_avg), r.top(), - 1, r.height() - }; + const Rect r2 { r.left() + x_avg, r.top(), 1, r.height() }; painter.fill_rectangle( r2, Color::white() ); - const Rect r3 { - static_cast(r.left() + x_avg + 1), r.top(), - static_cast(x_max - (x_avg + 1)), r.height() - }; + const Rect r3 { r.left() + x_avg + 1, r.top(), x_max - (x_avg + 1), r.height() }; painter.fill_rectangle( r3, Color::red() ); - const Rect r4 { - static_cast(r.left() + x_max), r.top(), - static_cast(r.width() - x_max), r.height() - }; + const Rect r4 { r.left() + x_max, r.top(), r.width() - x_max, r.height() }; painter.fill_rectangle( r4, Color::black() diff --git a/firmware/common/lcd_ili9341.cpp b/firmware/common/lcd_ili9341.cpp index db434d6c..539535dc 100644 --- a/firmware/common/lcd_ili9341.cpp +++ b/firmware/common/lcd_ili9341.cpp @@ -291,10 +291,7 @@ void ILI9341::fill_circle( const uint32_t d2 = x2 + y2; const bool inside = d2 < radius2; const auto color = inside ? foreground : background; - draw_pixel({ - static_cast(x + center.x()), - static_cast(y + center.y()) - }, color); + draw_pixel({ x + center.x(), y + center.y() }, color); } } } diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 61abaecf..6255a0ab 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -523,9 +523,9 @@ bool ImageButton::on_touch(const TouchEvent event) { OptionsField::OptionsField( Point parent_pos, - size_t length, + int length, options_t options -) : Widget { { parent_pos, { static_cast(8 * length), 16 } } }, +) : Widget { { parent_pos, { 8 * length, 16 } } }, length_ { length }, options { options } { @@ -594,11 +594,11 @@ bool OptionsField::on_touch(const TouchEvent event) { NumberField::NumberField( Point parent_pos, - size_t length, + int length, range_t range, int32_t step, char fill_char -) : Widget { { parent_pos, { static_cast(8 * length), 16 } } }, +) : Widget { { parent_pos, { 8 * length, 16 } } }, range { range }, step { step }, length_ { length }, diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 3ec09d26..68a8fdba 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -273,7 +273,7 @@ public: std::function on_change { }; std::function on_show_options { }; - OptionsField(Point parent_pos, size_t length, options_t options); + OptionsField(Point parent_pos, int length, options_t options); size_t selected_index() const; void set_selected_index(const size_t new_index); @@ -287,7 +287,7 @@ public: bool on_touch(const TouchEvent event) override; private: - const size_t length_; + const int length_; options_t options; size_t selected_index_ { 0 }; }; @@ -298,7 +298,7 @@ public: using range_t = std::pair; - NumberField(Point parent_pos, size_t length, range_t range, int32_t step, char fill_char); + NumberField(Point parent_pos, int length, range_t range, int32_t step, char fill_char); NumberField(const NumberField&) = delete; NumberField(NumberField&&) = delete; @@ -314,7 +314,7 @@ public: private: const range_t range; const int32_t step; - const size_t length_; + const int length_; const char fill_char; int32_t value_ { 0 }; From f2dd6827eab132c283f565abe9d087b57df3a5a3 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 6 Dec 2016 09:28:48 -0800 Subject: [PATCH 088/100] Add Widget::parent_rect() accessor, rename member variable. Some day I will settle on a convention for naming members... I think that day is near. --- firmware/common/ui_widget.cpp | 14 +++++++++----- firmware/common/ui_widget.hpp | 7 ++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 6255a0ab..88d5cc16 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -53,15 +53,19 @@ Point Widget::screen_pos() { } Size Widget::size() const { - return parent_rect.size(); + return _parent_rect.size(); } Rect Widget::screen_rect() const { - return parent() ? (parent_rect + parent()->screen_pos()) : parent_rect; + return parent() ? (parent_rect() + parent()->screen_pos()) : parent_rect(); +} + +Rect Widget::parent_rect() const { + return _parent_rect; } void Widget::set_parent_rect(const Rect new_parent_rect) { - parent_rect = new_parent_rect; + _parent_rect = new_parent_rect; set_dirty(); } @@ -106,7 +110,7 @@ void Widget::hidden(bool hide) { if( hide ) { // TODO: Instead of dirtying parent entirely, dirty only children // that overlap with this widget. - parent()->dirty_overlapping_children_in_rect(parent_rect); + parent()->dirty_overlapping_children_in_rect(parent_rect()); /* TODO: Notify self and all non-hidden children that they're * now effectively hidden? */ @@ -214,7 +218,7 @@ void Widget::set_highlighted(const bool value) { void Widget::dirty_overlapping_children_in_rect(const Rect& child_rect) { for(auto child : children()) { - if( !child_rect.intersect(child->parent_rect).is_empty() ) { + if( !child_rect.intersect(child->parent_rect()).is_empty() ) { child->set_dirty(); } } diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 68a8fdba..6a081b6f 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -52,13 +52,13 @@ private: class Widget { public: Widget( - ) : parent_rect { } + ) : _parent_rect { } { } Widget( Rect parent_rect - ) : parent_rect { parent_rect } + ) : _parent_rect { parent_rect } { } @@ -72,6 +72,7 @@ public: Point screen_pos(); Size size() const; Rect screen_rect() const; + Rect parent_rect() const; virtual void set_parent_rect(const Rect new_parent_rect); Widget* parent() const; @@ -119,7 +120,7 @@ protected: private: /* Widget rectangle relative to parent pos(). */ - Rect parent_rect; + Rect _parent_rect; const Style* style_ { nullptr }; Widget* parent_ { nullptr }; From 431aae333a696dda21573ffaa3eed4de263f2e9e Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 6 Dec 2016 09:34:45 -0800 Subject: [PATCH 089/100] Move additional FR_* error values to file.hpp for public use. --- firmware/application/file.cpp | 7 ------- firmware/application/file.hpp | 10 ++++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index ae846892..ef7dc3ed 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -25,13 +25,6 @@ #include #include -/* Values added to FatFs FRESULT enum, values outside the FRESULT data type */ -static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected."); -#define FR_DISK_FULL (0x100) -#define FR_EOF (0x101) -#define FR_BAD_SEEK (0x102) -#define FR_UNEXPECTED (0x103) - Optional File::open_fatfs(const std::filesystem::path& filename, BYTE mode) { auto result = f_open(&f, reinterpret_cast(filename.c_str()), mode); if( result == FR_OK ) { diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index 4c59f7f0..42cce7f5 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -233,6 +233,16 @@ space_info space(const path& p); std::filesystem::path next_filename_stem_matching_pattern(std::filesystem::path filename_stem_pattern); +/* Values added to FatFs FRESULT enum, values outside the FRESULT data type */ +static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected."); + +/* Dangerous to expose these, as FatFs native error values are byte-sized. However, + * my filesystem_error implemetation is fine with it. */ +#define FR_DISK_FULL (0x100) +#define FR_EOF (0x101) +#define FR_BAD_SEEK (0x102) +#define FR_UNEXPECTED (0x103) + class File { public: using Size = uint64_t; From ad9a63a66606001e1047b044cb6924e5e51c321c Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 21 Dec 2016 22:20:28 -0800 Subject: [PATCH 090/100] GPIO: Fix apparent dumb bit-logic bug. Not due to any observable incorrect behavior, but just noticing that the code, as previously written, should not work... --- firmware/common/gpio.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/firmware/common/gpio.hpp b/firmware/common/gpio.hpp index 0f02d6ec..97e69952 100644 --- a/firmware/common/gpio.hpp +++ b/firmware/common/gpio.hpp @@ -37,12 +37,12 @@ struct PinConfig { constexpr operator uint16_t() { return - ((~ifilt) << 7) - | (input << 6) - | (fast << 5) - | ((~pu) << 4) - | (pd << 3) - | (mode << 0); + (((~ifilt) & 1) << 7) + | ((input & 1) << 6) + | ((fast & 1) << 5) + | (((~pu) & 1) << 4) + | ((pd & 1) << 3) + | ((mode & 1) << 0); } /* constexpr operator uint32_t() { From 01cd8c7776d4ab5ebcdeedad504700e2a61acf35 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 21 Dec 2016 22:24:07 -0800 Subject: [PATCH 091/100] GPIO: Fix PinConfig -> uint16_t type conversion signature. --- firmware/common/gpio.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/common/gpio.hpp b/firmware/common/gpio.hpp index 97e69952..69e12c91 100644 --- a/firmware/common/gpio.hpp +++ b/firmware/common/gpio.hpp @@ -35,7 +35,7 @@ struct PinConfig { const uint32_t input; const uint32_t ifilt; - constexpr operator uint16_t() { + constexpr operator uint16_t() const { return (((~ifilt) & 1) << 7) | ((input & 1) << 6) From 0ea2f9650e6b915c285635025e7e1b810482c4c2 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 5 Jan 2017 17:06:44 -0800 Subject: [PATCH 092/100] C++14: const all the methods! --- firmware/application/si5351.hpp | 26 +++++++++++++------------- firmware/common/i2c_pp.hpp | 6 +++--- firmware/common/memory_map.hpp | 6 +++--- firmware/common/spi_image.hpp | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/firmware/application/si5351.hpp b/firmware/application/si5351.hpp index e76a4bbb..58104f82 100644 --- a/firmware/application/si5351.hpp +++ b/firmware/application/si5351.hpp @@ -168,7 +168,7 @@ struct Inputs { const uint32_t f_clkin; const uint32_t clkin_div; - constexpr uint32_t f_clkin_out() { + constexpr uint32_t f_clkin_out() const { return f_clkin / clkin_div; } }; @@ -181,23 +181,23 @@ struct PLL { const uint32_t b; const uint32_t c; - constexpr uint32_t f_vco() { + constexpr uint32_t f_vco() const { return f_in * (a + (float)b / (float)c); } - constexpr uint32_t p1() { + constexpr uint32_t p1() const { return 128 * a + (uint32_t)(128 * (float)b / (float)c) - 512; } - constexpr uint32_t p2() { + constexpr uint32_t p2() const { return 128 * b - c * (uint32_t)(128 * (float)b / (float)c); } - constexpr uint32_t p3() { + constexpr uint32_t p3() const { return c; } - constexpr PLLReg reg(const uint8_t pll_n) { + constexpr PLLReg reg(const uint8_t pll_n) const { return { uint8_t(26 + (pll_n * 8)), uint8_t((p3() >> 8) & 0xff), @@ -224,23 +224,23 @@ struct MultisynthFractional { const uint32_t c; const uint32_t r_div; - constexpr uint32_t p1() { + constexpr uint32_t p1() const { return 128 * a + (uint32_t)(128 * (float)b / (float)c) - 512; } - constexpr uint32_t p2() { + constexpr uint32_t p2() const { return 128 * b - c * (uint32_t)(128 * (float)b / (float)c); } - constexpr uint32_t p3() { + constexpr uint32_t p3() const { return c; } - constexpr uint32_t f_out() { + constexpr uint32_t f_out() const { return f_src / (a + (float)b / (float)c) / (1 << r_div); } - constexpr MultisynthFractionalReg reg(const uint8_t multisynth_n) { + constexpr MultisynthFractionalReg reg(const uint8_t multisynth_n) const { return { uint8_t(42 + (multisynth_n * 8)), uint8_t((p3() >> 8) & 0xFF), @@ -260,11 +260,11 @@ struct MultisynthInteger { const uint32_t a; const uint32_t r_div; - constexpr uint8_t p1() { + constexpr uint8_t p1() const { return a; } - constexpr uint32_t f_out() { + constexpr uint32_t f_out() const { return f_src / a / (1 << r_div); } }; diff --git a/firmware/common/i2c_pp.hpp b/firmware/common/i2c_pp.hpp index 7eabab0b..62f729e7 100644 --- a/firmware/common/i2c_pp.hpp +++ b/firmware/common/i2c_pp.hpp @@ -46,15 +46,15 @@ struct I2CClockConfig { return 1e9 / f; } - constexpr uint32_t i2c_period_count() { + constexpr uint32_t i2c_period_count() const { return period_ns(bus_f) / period_ns(clock_source_f) + 0.5f; } - constexpr uint32_t i2c_high_count() { + constexpr uint32_t i2c_high_count() const { return high_period_ns / period_ns(clock_source_f) + 0.5f; } - constexpr uint32_t i2c_low_count() { + constexpr uint32_t i2c_low_count() const { return i2c_period_count() - i2c_high_count(); } }; diff --git a/firmware/common/memory_map.hpp b/firmware/common/memory_map.hpp index 8b32a0cd..d8624d74 100644 --- a/firmware/common/memory_map.hpp +++ b/firmware/common/memory_map.hpp @@ -43,15 +43,15 @@ public: } - constexpr uint32_t base() { + constexpr uint32_t base() const { return base_; } - constexpr uint32_t end() { + constexpr uint32_t end() const { return base_ + size_; } - constexpr size_t size() { + constexpr size_t size() const { return size_; } diff --git a/firmware/common/spi_image.hpp b/firmware/common/spi_image.hpp index e1143712..7b53fd75 100644 --- a/firmware/common/spi_image.hpp +++ b/firmware/common/spi_image.hpp @@ -86,7 +86,7 @@ struct region_t { const size_t offset; const size_t size; - constexpr const void* base() { + constexpr const void* base() const { return reinterpret_cast(portapack::memory::map::spifi_cached.base() + offset); } }; From a22dc150bc4326295f27c5fa854cb81e56179e95 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 5 Jan 2017 17:10:00 -0800 Subject: [PATCH 093/100] C++14: make some wrapper classes static. Also address GCC 6.2 not allowing constexpr from reinterpret_cast<> values. --- firmware/application/touch_adc.cpp | 10 ++--- firmware/baseband/rssi.cpp | 12 +++--- firmware/common/adc.hpp | 65 ++++++++++++++---------------- firmware/common/hackrf_hal.hpp | 4 +- firmware/common/i2s.hpp | 46 +++++++++++---------- 5 files changed, 68 insertions(+), 69 deletions(-) diff --git a/firmware/application/touch_adc.cpp b/firmware/application/touch_adc.cpp index be731282..91748ff2 100644 --- a/firmware/application/touch_adc.cpp +++ b/firmware/application/touch_adc.cpp @@ -57,14 +57,14 @@ constexpr lpc43xx::adc::Config adc0_config { }; void init() { - adc0.clock_enable(); - adc0.interrupts_disable(); - adc0.power_up(adc0_config); - adc0.interrupts_enable(adc0_interrupt_mask); + adc0::clock_enable(); + adc0::interrupts_disable(); + adc0::power_up(adc0_config); + adc0::interrupts_enable(adc0_interrupt_mask); } void start() { - adc0.start_burst(); + adc0::start_burst(); } // static constexpr bool monitor_overruns_and_not_dones = false; diff --git a/firmware/baseband/rssi.cpp b/firmware/baseband/rssi.cpp index 2e9cfa32..1abf10a1 100644 --- a/firmware/baseband/rssi.cpp +++ b/firmware/baseband/rssi.cpp @@ -52,22 +52,22 @@ constexpr adc::Config adc1_config { }; void init() { - adc1.clock_enable(); - adc1.interrupts_disable(); - adc1.power_up(adc1_config); - adc1.interrupts_enable(adc1_interrupt_mask); + adc1::clock_enable(); + adc1::interrupts_disable(); + adc1::power_up(adc1_config); + adc1::interrupts_enable(adc1_interrupt_mask); dma::init(); } void start() { dma::enable(); - adc1.start_burst(); + adc1::start_burst(); } void stop() { dma::disable(); - adc1.stop_burst(); + adc1::stop_burst(); } } /* namespace rssi */ diff --git a/firmware/common/adc.hpp b/firmware/common/adc.hpp index e419ee22..71414666 100644 --- a/firmware/common/adc.hpp +++ b/firmware/common/adc.hpp @@ -52,84 +52,81 @@ struct Config { uint32_t cr; }; +template class ADC { public: - constexpr ADC( - LPC_ADCx_Type* adcp - ) : adcp(adcp) - { + static void power_up(const Config config) { + adcp().CR = config.cr; } - void power_up(const Config config) const { - adcp->CR = config.cr; - } - - void clock_enable() const { - if( adcp == LPC_ADC0 ) { + static void clock_enable() { + if( &adcp() == LPC_ADC0 ) { LPC_CCU1->CLK_APB3_ADC0_CFG.AUTO = 1; LPC_CCU1->CLK_APB3_ADC0_CFG.RUN = 1; } - if( adcp == LPC_ADC1 ) { + if( &adcp() == LPC_ADC1 ) { LPC_CCU1->CLK_APB3_ADC1_CFG.AUTO = 1; LPC_CCU1->CLK_APB3_ADC1_CFG.RUN = 1; } } - void clock_disable() const { - if( adcp == LPC_ADC0 ) { + static void clock_disable() { + if( &adcp() == LPC_ADC0 ) { LPC_CCU1->CLK_APB3_ADC0_CFG.RUN = 0; } - if( adcp == LPC_ADC1 ) { + if( &adcp() == LPC_ADC1 ) { LPC_CCU1->CLK_APB3_ADC1_CFG.RUN = 0; } } - void disable() const { - adcp->INTEN = 0; - adcp->CR = 0; + static void disable() { + adcp().INTEN = 0; + adcp().CR = 0; clock_disable(); } - void interrupts_disable() const { - adcp->INTEN = 0; + static void interrupts_disable() { + adcp().INTEN = 0; } - void interrupts_enable(const uint32_t mask) const { - adcp->INTEN = mask; + static void interrupts_enable(const uint32_t mask) { + adcp().INTEN = mask; } - void start_burst() const { - adcp->CR |= (1U << 16); + static void start_burst() { + adcp().CR |= (1U << 16); } - void start_once() const { - adcp->CR |= (1U << 24); + static void start_once() { + adcp().CR |= (1U << 24); } - void start_once(size_t n) const { - uint32_t cr = adcp->CR; + static void start_once(size_t n) { + uint32_t cr = adcp().CR; cr &= ~(0xffU); cr |= (1 << 24) | (1 << n); - adcp->CR = cr; + adcp().CR = cr; } - void stop_burst() const { - adcp->CR &= ~(1U << 16); + static void stop_burst() { + adcp().CR &= ~(1U << 16); } - uint32_t convert(size_t n) const { + static uint32_t convert(size_t n) { start_once(n); while(true) { - const uint32_t data = adcp->DR[n]; + const uint32_t data = adcp().DR[n]; if( (data >> 31) & 1 ) { return (data >> 6) & 0x3ff; } } } - + private: - LPC_ADCx_Type* const adcp; + static LPC_ADCx_Type& adcp() { + return *reinterpret_cast(BaseAddress); + } }; } /* namespace adc */ diff --git a/firmware/common/hackrf_hal.hpp b/firmware/common/hackrf_hal.hpp index 8b41f0a3..b7d5e1c0 100644 --- a/firmware/common/hackrf_hal.hpp +++ b/firmware/common/hackrf_hal.hpp @@ -67,11 +67,11 @@ constexpr size_t clock_generator_output_mcu_clkin = 7; /* ADC0 */ -constexpr adc::ADC adc0 { LPC_ADC0 }; +using adc0 = adc::ADC; /* ADC1 */ -constexpr adc::ADC adc1 { LPC_ADC1 }; +using adc1 = adc::ADC; void reset(); diff --git a/firmware/common/i2s.hpp b/firmware/common/i2s.hpp index b7d4d46a..0ddaa966 100644 --- a/firmware/common/i2s.hpp +++ b/firmware/common/i2s.hpp @@ -155,8 +155,6 @@ struct ConfigDMA { template class I2S { public: - static constexpr LPC_I2S_Type* Peripheral = reinterpret_cast(BaseAddress); - static void configure( const ConfigTX& config_tx, const ConfigRX& config_rx @@ -167,28 +165,28 @@ public: /* NOTE: Documentation of CREG6 is quite confusing. Refer to "I2S clocking and * pin connections" and other I2S diagrams for more clarity. */ - if( Peripheral == LPC_I2S0 ) { + if( &p() == LPC_I2S0 ) { LPC_CREG->CREG6 |= (1U << 12) | (1U << 13) ; } - if( Peripheral == LPC_I2S1 ) { + if( &p() == LPC_I2S1 ) { LPC_CREG->CREG6 |= (1U << 14) | (1U << 15) ; } - Peripheral->DAO = config_tx.dao; - Peripheral->TXRATE = config_tx.txrate; - Peripheral->TXBITRATE = config_tx.txbitrate; - Peripheral->TXMODE = config_tx.txmode; + p().DAO = config_tx.dao; + p().TXRATE = config_tx.txrate; + p().TXBITRATE = config_tx.txbitrate; + p().TXMODE = config_tx.txmode; - Peripheral->DAI = config_rx.dai; - Peripheral->RXRATE = config_rx.rxrate; - Peripheral->RXBITRATE = config_rx.rxbitrate; - Peripheral->RXMODE = config_rx.rxmode; + p().DAI = config_rx.dai; + p().RXRATE = config_rx.rxrate; + p().RXBITRATE = config_rx.rxbitrate; + p().RXMODE = config_rx.rxmode; } static void configure( @@ -198,38 +196,42 @@ public: ) { configure(config_tx, config_rx); - Peripheral->DMA1 = config_dma.dma1; - Peripheral->DMA2 = config_dma.dma2; + p().DMA1 = config_dma.dma1; + p().DMA2 = config_dma.dma2; } static void rx_start() { - Peripheral->DAI &= ~(1U << 3); + p().DAI &= ~(1U << 3); } static void rx_stop() { - Peripheral->DAI |= (1U << 3); + p().DAI |= (1U << 3); } static void tx_start() { - Peripheral->DAO &= ~(1U << 3); + p().DAO &= ~(1U << 3); } static void tx_stop() { - Peripheral->DAO |= (1U << 3); + p().DAO |= (1U << 3); } static void tx_mute() { - Peripheral->DAO |= (1U << 15); + p().DAO |= (1U << 15); } static void tx_unmute() { - Peripheral->DAO &= ~(1U << 15); + p().DAO &= ~(1U << 15); } private: static void reset() { - Peripheral->DAO |= (1U << 4); - Peripheral->DAI |= (1U << 4); + p().DAO |= (1U << 4); + p().DAI |= (1U << 4); + } + + static LPC_I2S_Type& p() { + return *reinterpret_cast(BaseAddress); } }; From 0d1e48ae9cb3f29bc201c462f3e738424e8b1239 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 5 Jan 2017 17:13:03 -0800 Subject: [PATCH 094/100] C++14: Add two-argument delete() implementations. Just passing to one-argument versions. --- firmware/common/chibios_cpp.cpp | 8 ++++++++ firmware/common/chibios_cpp.hpp | 2 ++ 2 files changed, 10 insertions(+) diff --git a/firmware/common/chibios_cpp.cpp b/firmware/common/chibios_cpp.cpp index eab2fcb0..d148ca1d 100644 --- a/firmware/common/chibios_cpp.cpp +++ b/firmware/common/chibios_cpp.cpp @@ -41,6 +41,14 @@ void operator delete[](void* p) noexcept { chHeapFree(p); } +void operator delete(void* ptr, std::size_t) noexcept { + ::operator delete(ptr); +} + +void operator delete[](void* ptr, std::size_t) noexcept { + ::operator delete(ptr); +} + extern uint8_t __heap_base__[]; extern uint8_t __heap_end__[]; diff --git a/firmware/common/chibios_cpp.hpp b/firmware/common/chibios_cpp.hpp index 5cb4af2e..fd952aaf 100644 --- a/firmware/common/chibios_cpp.hpp +++ b/firmware/common/chibios_cpp.hpp @@ -30,6 +30,8 @@ void* operator new(size_t size); void* operator new[](size_t size); void operator delete(void* p); void operator delete[](void* p); +void operator delete(void* ptr, std::size_t); +void operator delete[](void* ptr, std::size_t); namespace chibios { From 87383d735cb6de4d179e0933e538775db6bda4e5 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 5 Jan 2017 17:14:07 -0800 Subject: [PATCH 095/100] C++14: Decommission my own make_unique. --- firmware/baseband/baseband_thread.cpp | 2 ++ firmware/common/utility.hpp | 42 --------------------------- 2 files changed, 2 insertions(+), 42 deletions(-) diff --git a/firmware/baseband/baseband_thread.cpp b/firmware/baseband/baseband_thread.cpp index 8901f528..7083e9f4 100644 --- a/firmware/baseband/baseband_thread.cpp +++ b/firmware/baseband/baseband_thread.cpp @@ -33,6 +33,8 @@ using namespace lpc43xx; #include "portapack_shared_memory.hpp" +#include "utility.hpp" + #include static baseband::SGPIO baseband_sgpio; diff --git a/firmware/common/utility.hpp b/firmware/common/utility.hpp index c6cfab94..feab67ec 100644 --- a/firmware/common/utility.hpp +++ b/firmware/common/utility.hpp @@ -113,46 +113,4 @@ struct range_t { } }; -namespace std { - -/*! Stephan T Lavavej (STL!) implementation of make_unique, which has been accepted into the C++14 standard. - * It includes handling for arrays. - * Paper here: http://isocpp.org/files/papers/N3656.txt - */ -template struct _Unique_if { - typedef unique_ptr _Single_object; -}; - -//! Specialization for unbound array -template struct _Unique_if { - typedef unique_ptr _Unknown_bound; -}; - -//! Specialization for array of known size -template struct _Unique_if { - typedef void _Known_bound; -}; - -//! Specialization for normal object type -template -typename _Unique_if::_Single_object -make_unique(Args&& ... args) { - return unique_ptr(new T(std::forward(args)...)); -} - -//! Specialization for unknown bound -template -typename _Unique_if::_Unknown_bound -make_unique(size_t n) { - typedef typename remove_extent::type U; - return unique_ptr(new U[n]()); -} - -//! Deleted specialization -template -typename _Unique_if::_Known_bound -make_unique(Args&& ...) = delete; - -} /* namespace std */ - #endif/*__UTILITY_H__*/ From e763592adbdb2fc8d0a281ba7a3d7e9899760fe6 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 5 Jan 2017 17:15:00 -0800 Subject: [PATCH 096/100] Compile firmware as C++14. --- firmware/application/CMakeLists.txt | 2 +- firmware/baseband/CMakeLists.txt | 2 +- firmware/bootstrap/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index d0860c15..0243f760 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -35,7 +35,7 @@ set(USE_OPT "-Os -g --specs=nano.specs") set(USE_COPT "-std=gnu99") # C++ specific options here (added to USE_OPT). -set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") +set(USE_CPPOPT "-std=c++14 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") # Enable this if you want the linker to remove unused code and data set(USE_LINK_GC yes) diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index faf5c2cb..b6230dfe 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -35,7 +35,7 @@ set(USE_OPT "-O3 -g -falign-functions=16 -fno-math-errno --specs=nano.specs") set(USE_COPT "-std=gnu99") # C++ specific options here (added to USE_OPT). -set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") +set(USE_CPPOPT "-std=c++14 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") # Enable this if you want the linker to remove unused code and data set(USE_LINK_GC yes) diff --git a/firmware/bootstrap/CMakeLists.txt b/firmware/bootstrap/CMakeLists.txt index 6756f5b0..0c5e4a29 100644 --- a/firmware/bootstrap/CMakeLists.txt +++ b/firmware/bootstrap/CMakeLists.txt @@ -35,7 +35,7 @@ set(USE_OPT "-Os -g -falign-functions=16 -fno-math-errno --specs=nano.specs") set(USE_COPT "-std=gnu99") # C++ specific options here (added to USE_OPT). -set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") +set(USE_CPPOPT "-std=c++14 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") # Enable this if you want the linker to remove unused code and data set(USE_LINK_GC yes) From 177d49b769331a1aeb63ec589b1d54c2ebc93dca Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 6 Jan 2017 14:22:27 -0800 Subject: [PATCH 097/100] GPIO: Fix for incorrect pin-funciton bit mask. Caused device to not boot. Oops. --- firmware/common/gpio.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/common/gpio.hpp b/firmware/common/gpio.hpp index 69e12c91..75c729c5 100644 --- a/firmware/common/gpio.hpp +++ b/firmware/common/gpio.hpp @@ -42,7 +42,7 @@ struct PinConfig { | ((fast & 1) << 5) | (((~pu) & 1) << 4) | ((pd & 1) << 3) - | ((mode & 1) << 0); + | ((mode & 7) << 0); } /* constexpr operator uint32_t() { From 1986d1faca61117a4a1de9b286458d57c59fe697 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 6 Jan 2017 14:28:41 -0800 Subject: [PATCH 098/100] Travis-CI: Update compiler to ARM-GCC 6.2. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 363625ea..b3cfb7f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,9 @@ notifications: - "Firmware download : https://portapack-h1-builds.s3.amazonaws.com/%{repository_slug}/%{build_number}/%{build_number}.1/build/firmware/portapack-h1-firmware-%{commit}.tar.bz2" before_script: - - wget https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q3-update/+download/gcc-arm-none-eabi-5_4-2016q3-20160926-linux.tar.bz2 -O /tmp/gcc-arm.tar.bz2 + - wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/6-2016q4/gcc-arm-none-eabi-6_2-2016q4-20161216-linux.tar.bz2 -O /tmp/gcc-arm.tar.bz2 - tar -xf /tmp/gcc-arm.tar.bz2 - - export PATH=$PWD/gcc-arm-none-eabi-5_4-2016q3/bin:$PATH + - export PATH=$PWD/gcc-arm-none-eabi-6_2-2016q4/bin:$PATH - export CC="arm-none-eabi-gcc" - export CXX="arm-none-eabi-g++" From 05eb694c0a9d29e3098e396b53a7e0fb05fe9b93 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 6 Jan 2017 16:57:36 -0800 Subject: [PATCH 099/100] Introduce simd32_t type. Discontinue use of disagreeable __SIMD #define. --- firmware/baseband/channel_stats_collector.hpp | 8 +- firmware/baseband/dsp_decimate.cpp | 78 ++++++++++--------- firmware/baseband/dsp_demodulate.cpp | 27 +++---- .../os/hal/platforms/LPC43xx_M4/lpc43xx_m4.h | 6 -- firmware/common/simd.hpp | 24 ++++++ 5 files changed, 83 insertions(+), 60 deletions(-) diff --git a/firmware/baseband/channel_stats_collector.hpp b/firmware/baseband/channel_stats_collector.hpp index ca7d0eed..44d70753 100644 --- a/firmware/baseband/channel_stats_collector.hpp +++ b/firmware/baseband/channel_stats_collector.hpp @@ -23,6 +23,7 @@ #define __CHANNEL_STATS_COLLECTOR_H__ #include "dsp_types.hpp" +#include "simd.hpp" #include "message.hpp" #include "utility.hpp" @@ -35,9 +36,10 @@ class ChannelStatsCollector { public: template void feed(const buffer_c16_t& src, Callback callback) { - auto src_p = src.p; - while(src_p < &src.p[src.count]) { - const uint32_t sample = *__SIMD32(src_p)++; + auto src_p = simd32_ptr(src.p); + const auto end_p = simd32_ptr(&src.p[src.count]); + while(src_p < end_p) { + const uint32_t sample = *(src_p++); const uint32_t mag_sq = __SMUAD(sample, sample); if( mag_sq > max_squared ) { max_squared = mag_sq; diff --git a/firmware/baseband/dsp_decimate.cpp b/firmware/baseband/dsp_decimate.cpp index 93dbc602..09aa45df 100644 --- a/firmware/baseband/dsp_decimate.cpp +++ b/firmware/baseband/dsp_decimate.cpp @@ -21,6 +21,8 @@ #include "dsp_decimate.hpp" +#include "simd.hpp" + #include namespace dsp { @@ -566,39 +568,39 @@ buffer_c16_t DecimateBy2CIC3::execute( uint32_t t1 = _iq0; uint32_t t2 = _iq1; const uint32_t taps = 0x00000003; - auto s = src.p; - auto d = dst.p; - const auto d_end = &dst.p[src.count / 2]; + auto s = simd32_ptr(&src.p[0]); + auto d = simd32_ptr(&dst.p[0]); + const auto d_end = simd32_ptr(&dst.p[src.count / 2]); while(d < d_end) { uint32_t i = __SXTH(t1, 0); /* 1: I0 */ uint32_t q = __SXTH(t1, 16); /* 1: Q0 */ i = __SMLABB(t2, taps, i); /* 1: I1*3 + I0 */ q = __SMLATB(t2, taps, q); /* 1: Q1*3 + Q0 */ - const uint32_t t3 = *__SIMD32(s)++; /* 3: Q2:I2 */ - const uint32_t t4 = *__SIMD32(s)++; /* Q3:I3 */ + const uint32_t t3 = *(s++); /* 3: Q2:I2 */ + const uint32_t t4 = *(s++); /* Q3:I3 */ i = __SMLABB(t3, taps, i); /* 1: I2*3 + I1*3 + I0 */ q = __SMLATB(t3, taps, q); /* 1: Q2*3 + Q1*3 + Q0 */ int32_t si0 = __SXTAH(i, t4, 0); /* 1: I3 + Q2*3 + Q1*3 + Q0 */ int32_t sq0 = __SXTAH(q, t4, 16); /* 1: Q3 + Q2*3 + Q1*3 + Q0 */ i = __BFI(si0 / 8, sq0 / 8, 16, 16); /* 1: D2_Q0:D2_I0 */ - *__SIMD32(d)++ = i; /* D2_Q0:D2_I0 */ + *(d++) = i; /* D2_Q0:D2_I0 */ i = __SXTH(t3, 0); /* 1: I2 */ q = __SXTH(t3, 16); /* 1: Q2 */ i = __SMLABB(t4, taps, i); /* 1: I3*3 + I2 */ q = __SMLATB(t4, taps, q); /* 1: Q3*3 + Q2 */ - t1 = *__SIMD32(s)++; /* 3: Q4:I4 */ - t2 = *__SIMD32(s)++; /* Q5:I5 */ + t1 = *(s++); /* 3: Q4:I4 */ + t2 = *(s++); /* Q5:I5 */ i = __SMLABB(t1, taps, i); /* 1: I4*3 + I3*3 + I2 */ q = __SMLATB(t1, taps, q); /* 1: Q4*3 + Q3*3 + Q2 */ int32_t si1 = __SXTAH(i, t2, 0) ; /* 1: I5 + Q4*3 + Q3*3 + Q2 */ int32_t sq1 = __SXTAH(q, t2, 16); /* 1: Q5 + Q4*3 + Q3*3 + Q2 */ i = __BFI(si1 / 8, sq1 / 8, 16, 16); /* 1: D2_Q1:D2_I1 */ - *__SIMD32(d)++ = i; /* D2_Q1:D2_I1 */ + *(d++) = i; /* D2_Q1:D2_I1 */ } _iq0 = t1; _iq1 = t2; @@ -665,57 +667,57 @@ buffer_c16_t FIRAndDecimateComplex::execute( const auto output_sampling_rate = src.sampling_rate / decimation_factor_; const size_t output_samples = src.count / decimation_factor_; - sample_t* dst_p = dst.p; + auto dst_p = simd32_ptr(dst.p); const buffer_c16_t result { dst.p, output_samples, output_sampling_rate }; - const sample_t* src_p = src.p; + auto src_p = simd32_ptr(src.p); size_t outer_count = output_samples; while(outer_count > 0) { /* Put new samples into delay buffer */ - auto z_new_p = &samples_[taps_count_ - decimation_factor_]; + auto z_new_p = simd32_ptr(&samples_[taps_count_ - decimation_factor_]); for(size_t i=0; i 0) { - const auto tap0 = *__SIMD32(t_p)++; - const auto sample0 = *__SIMD32(z_p)++; - const auto tap1 = *__SIMD32(t_p)++; - const auto sample1 = *__SIMD32(z_p)++; + const auto tap0 = *(t_p++); + const auto sample0 = *(z_p++); + const auto tap1 = *(t_p++); + const auto sample1 = *(z_p++); t_real = __SMLSLD(sample0, tap0, t_real); t_imag = __SMLALDX(sample0, tap0, t_imag); t_real = __SMLSLD(sample1, tap1, t_real); t_imag = __SMLALDX(sample1, tap1, t_imag); - const auto tap2 = *__SIMD32(t_p)++; - const auto sample2 = *__SIMD32(z_p)++; - const auto tap3 = *__SIMD32(t_p)++; - const auto sample3 = *__SIMD32(z_p)++; + const auto tap2 = *(t_p++); + const auto sample2 = *(z_p++); + const auto tap3 = *(t_p++); + const auto sample3 = *(z_p++); t_real = __SMLSLD(sample2, tap2, t_real); t_imag = __SMLALDX(sample2, tap2, t_imag); t_real = __SMLSLD(sample3, tap3, t_real); t_imag = __SMLALDX(sample3, tap3, t_imag); - const auto tap4 = *__SIMD32(t_p)++; - const auto sample4 = *__SIMD32(z_p)++; - const auto tap5 = *__SIMD32(t_p)++; - const auto sample5 = *__SIMD32(z_p)++; + const auto tap4 = *(t_p++); + const auto sample4 = *(z_p++); + const auto tap5 = *(t_p++); + const auto sample5 = *(z_p++); t_real = __SMLSLD(sample4, tap4, t_real); t_imag = __SMLALDX(sample4, tap4, t_imag); t_real = __SMLSLD(sample5, tap5, t_real); t_imag = __SMLALDX(sample5, tap5, t_imag); - const auto tap6 = *__SIMD32(t_p)++; - const auto sample6 = *__SIMD32(z_p)++; - const auto tap7 = *__SIMD32(t_p)++; - const auto sample7 = *__SIMD32(z_p)++; + const auto tap6 = *(t_p++); + const auto sample6 = *(z_p++); + const auto tap7 = *(t_p++); + const auto sample7 = *(z_p++); t_real = __SMLSLD(sample6, tap6, t_real); t_imag = __SMLALDX(sample6, tap6, t_imag); t_real = __SMLSLD(sample7, tap7, t_real); @@ -731,7 +733,7 @@ buffer_c16_t FIRAndDecimateComplex::execute( const int32_t i = t_imag >> 16; const int32_t r_sat = __SSAT(r, 16); const int32_t i_sat = __SSAT(i, 16); - *__SIMD32(dst_p)++ = __PKHBT( + *(dst_p++) = __PKHBT( r_sat, i_sat, 16 @@ -741,14 +743,14 @@ buffer_c16_t FIRAndDecimateComplex::execute( const size_t unroll_factor = 4; size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor; - sample_t* t = &samples_[0]; - const sample_t* s = &samples_[decimation_factor_]; + auto t = simd32_ptr(&samples_[0]); + auto s = simd32_ptr(&samples_[decimation_factor_]); while(shift_count > 0) { - *__SIMD32(t)++ = *__SIMD32(s)++; - *__SIMD32(t)++ = *__SIMD32(s)++; - *__SIMD32(t)++ = *__SIMD32(s)++; - *__SIMD32(t)++ = *__SIMD32(s)++; + *(t++) = *(s++); + *(t++) = *(s++); + *(t++) = *(s++); + *(t++) = *(s++); shift_count--; } diff --git a/firmware/baseband/dsp_demodulate.cpp b/firmware/baseband/dsp_demodulate.cpp index cf79d0d0..22348a35 100644 --- a/firmware/baseband/dsp_demodulate.cpp +++ b/firmware/baseband/dsp_demodulate.cpp @@ -24,6 +24,7 @@ #include "complex.hpp" #include "fxpt_atan2.hpp" #include "utility_m4.hpp" +#include "simd.hpp" #include @@ -34,12 +35,12 @@ buffer_f32_t AM::execute( const buffer_c16_t& src, const buffer_f32_t& dst ) { - const auto src_p = src.p; - const auto src_end = &src.p[src.count]; + auto src_p = simd32_ptr(src.p); + auto src_end = simd32_ptr(&src.p[src.count]); auto dst_p = dst.p; while(src_p < src_end) { - const uint32_t sample0 = *__SIMD32(src_p)++; - const uint32_t sample1 = *__SIMD32(src_p)++; + const uint32_t sample0 = *(src_p++); + const uint32_t sample1 = *(src_p++); const uint32_t mag_sq0 = __SMUAD(sample0, sample0); const uint32_t mag_sq1 = __SMUAD(sample1, sample1); *(dst_p++) = __builtin_sqrtf(mag_sq0) * k; @@ -90,12 +91,12 @@ buffer_f32_t FM::execute( ) { auto z = z_; - const auto src_p = src.p; - const auto src_end = &src.p[src.count]; + auto src_p = simd32_ptr(src.p); + auto src_end = simd32_ptr(&src.p[src.count]); auto dst_p = dst.p; while(src_p < src_end) { - const auto s0 = *__SIMD32(src_p)++; - const auto s1 = *__SIMD32(src_p)++; + const auto s0 = *(src_p++); + const auto s1 = *(src_p++); const auto t0 = multiply_conjugate_s16_s32(s0, z); const auto t1 = multiply_conjugate_s16_s32(s1, s0); z = s1; @@ -113,12 +114,12 @@ buffer_s16_t FM::execute( ) { auto z = z_; - const auto src_p = src.p; - const auto src_end = &src.p[src.count]; + auto src_p = simd32_ptr(src.p); + auto src_end = simd32_ptr(&src.p[src.count]); auto dst_p = dst.p; while(src_p < src_end) { - const auto s0 = *__SIMD32(src_p)++; - const auto s1 = *__SIMD32(src_p)++; + const auto s0 = *(src_p++); + const auto s1 = *(src_p++); const auto t0 = multiply_conjugate_s16_s32(s0, z); const auto t1 = multiply_conjugate_s16_s32(s1, s0); z = s1; @@ -126,7 +127,7 @@ buffer_s16_t FM::execute( const int32_t theta0_sat = __SSAT(theta0_int, 16); const int32_t theta1_int = angle_approx_0deg27(t1) * ks16; const int32_t theta1_sat = __SSAT(theta1_int, 16); - *__SIMD32(dst_p)++ = __PKHBT( + *(dst_p++) = __PKHBT( theta0_sat, theta1_sat, 16 diff --git a/firmware/chibios-portapack/os/hal/platforms/LPC43xx_M4/lpc43xx_m4.h b/firmware/chibios-portapack/os/hal/platforms/LPC43xx_M4/lpc43xx_m4.h index d56457ae..4a80e82a 100644 --- a/firmware/chibios-portapack/os/hal/platforms/LPC43xx_M4/lpc43xx_m4.h +++ b/firmware/chibios-portapack/os/hal/platforms/LPC43xx_M4/lpc43xx_m4.h @@ -125,12 +125,6 @@ typedef enum IRQn { #ifdef __cplusplus -/* NOTE: Override old, misbehaving SIMD #defines */ - -#define __SIMD32_TYPE int32_t -#define __SIMD32(addr) (*(__SIMD32_TYPE **) & (addr)) -#define _SIMD32_OFFSET(addr) (*(__SIMD32_TYPE *) (addr)) - /* Overload of __SXTB16() to add ROR argument, since using __ROR() as an * argument to the existing __SXTB16() doesn't produce optimum/sane code. */ diff --git a/firmware/common/simd.hpp b/firmware/common/simd.hpp index c2e9be95..1724066b 100644 --- a/firmware/common/simd.hpp +++ b/firmware/common/simd.hpp @@ -28,6 +28,30 @@ #include +template +struct simd32_t { + union { + uint32_t raw; + T vec; + }; + + operator uint32_t() const { + return raw; + } + + simd32_t& operator=(uint32_t v) { + raw = v; + return *this; + } + + static_assert(sizeof(raw) == sizeof(vec), "simd32_t types are not the same size."); +}; + +template +simd32_t* simd32_ptr(T* const p) { + return reinterpret_cast*>(p); +} + struct vec4_s8 { union { int8_t v[4]; From 052fd1c407e506fd3e4a4593b06193cc7d31c2c3 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 6 Jan 2017 16:59:57 -0800 Subject: [PATCH 100/100] Extract loop into static method. Performance boost as compiler is no longer updating member variable every pass through the loop. --- firmware/baseband/channel_stats_collector.hpp | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/firmware/baseband/channel_stats_collector.hpp b/firmware/baseband/channel_stats_collector.hpp index 44d70753..8e69448e 100644 --- a/firmware/baseband/channel_stats_collector.hpp +++ b/firmware/baseband/channel_stats_collector.hpp @@ -36,15 +36,7 @@ class ChannelStatsCollector { public: template void feed(const buffer_c16_t& src, Callback callback) { - auto src_p = simd32_ptr(src.p); - const auto end_p = simd32_ptr(&src.p[src.count]); - while(src_p < end_p) { - const uint32_t sample = *(src_p++); - const uint32_t mag_sq = __SMUAD(sample, sample); - if( mag_sq > max_squared ) { - max_squared = mag_sq; - } - } + max_squared = compute_max_squared(src, max_squared); count += src.count; const size_t samples_per_update = src.sampling_rate * update_interval; @@ -63,6 +55,24 @@ private: static constexpr float update_interval { 0.1f }; uint32_t max_squared { 0 }; size_t count { 0 }; + + static uint32_t compute_max_squared( + const buffer_c16_t& src, + uint32_t max_squared + ) { + auto p = simd32_ptr(src.p); + const auto end_p = simd32_ptr(&src.p[src.count]); + + while(p < end_p) { + const uint32_t sample = *(p++); + const uint32_t mag_sq = __SMUAD(sample, sample); + if( mag_sq > max_squared ) { + max_squared = mag_sq; + } + } + + return max_squared; + } }; #endif/*__CHANNEL_STATS_COLLECTOR_H__*/

    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%0D#Hwe+ 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