Browse Source

implement USB MSC, add FatFs library for debug mass storage functionality.

Thomas Buck 1 year ago
parent
commit
1ee1d58773

+ 3
- 0
.gitmodules View File

@@ -1,3 +1,6 @@
1 1
 [submodule "firmware/pico-sdk"]
2 2
 	path = firmware/pico-sdk
3 3
 	url = https://github.com/raspberrypi/pico-sdk
4
+[submodule "firmware/fatfs"]
5
+	path = firmware/fatfs
6
+	url = https://github.com/abbrev/fatfs

+ 30
- 0
firmware/CMakeLists.txt View File

@@ -9,6 +9,28 @@ project(trackball)
9 9
 # initialize the Raspberry Pi Pico SDK
10 10
 pico_sdk_init()
11 11
 
12
+# copy FatFS source files to build dir, so we can use our own ffconf.h
13
+configure_file(
14
+    ${CMAKE_CURRENT_SOURCE_DIR}/fatfs/source/ff.c
15
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ff.c
16
+    COPYONLY
17
+)
18
+configure_file(
19
+    ${CMAKE_CURRENT_SOURCE_DIR}/fatfs/source/ff.h
20
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ff.h
21
+    COPYONLY
22
+)
23
+configure_file(
24
+    ${CMAKE_CURRENT_SOURCE_DIR}/fatfs/source/diskio.h
25
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/diskio.h
26
+    COPYONLY
27
+)
28
+configure_file(
29
+    ${CMAKE_CURRENT_SOURCE_DIR}/fatfs/source/ffunicode.c
30
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ffunicode.c
31
+    COPYONLY
32
+)
33
+
12 34
 add_executable(trackball)
13 35
 
14 36
 target_sources(trackball PUBLIC
@@ -21,11 +43,19 @@ target_sources(trackball PUBLIC
21 43
     src/usb_cdc.c
22 44
     src/usb_descriptors.c
23 45
     src/usb_hid.c
46
+    src/usb_msc.c
47
+    src/fat_disk.c
48
+    src/debug.c
49
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ff.c
50
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ffunicode.c
24 51
 )
25 52
 
26 53
 # Make sure TinyUSB can find tusb_config.h
27 54
 target_include_directories(trackball PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
28 55
 
56
+# Make sure FatFS can find ffconf.h
57
+target_include_directories(trackball PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/fatfs)
58
+
29 59
 target_compile_options(trackball PUBLIC
30 60
     -Wall
31 61
     -Werror

+ 1
- 0
firmware/fatfs

@@ -0,0 +1 @@
1
+Subproject commit 010b83715f3896827af14b811c77cc1d2edee8c5

+ 14
- 0
firmware/include/debug.h View File

@@ -0,0 +1,14 @@
1
+/*
2
+ * debug.h
3
+ */
4
+
5
+#ifndef __DEBUG_H__
6
+#define __DEBUG_H__
7
+
8
+int debug_msc_mount(void);
9
+int debug_msc_unmount(void);
10
+
11
+void debug_msc_stats(void);
12
+void debug_msc_pmw3360(void);
13
+
14
+#endif // __DEBUG_H__

+ 10
- 0
firmware/include/fat_disk.h View File

@@ -0,0 +1,10 @@
1
+/* 
2
+ * fat_disk.h
3
+ */
4
+
5
+#define DISK_BLOCK_COUNT 256
6
+#define DISK_BLOCK_SIZE 512
7
+
8
+void fat_disk_init(void);
9
+
10
+uint8_t *fat_disk_get_sector(uint32_t sector);

+ 296
- 0
firmware/include/ffconf.h View File

@@ -0,0 +1,296 @@
1
+/*---------------------------------------------------------------------------/
2
+/  Configurations of FatFs Module
3
+/---------------------------------------------------------------------------*/
4
+
5
+#define FFCONF_DEF	80286	/* Revision ID */
6
+
7
+/*---------------------------------------------------------------------------/
8
+/ Function Configurations
9
+/---------------------------------------------------------------------------*/
10
+
11
+#define FF_FS_READONLY	0
12
+/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
13
+/  Read-only configuration removes writing API functions, f_write(), f_sync(),
14
+/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
15
+/  and optional writing functions as well. */
16
+
17
+
18
+#define FF_FS_MINIMIZE	0
19
+/* This option defines minimization level to remove some basic API functions.
20
+/
21
+/   0: Basic functions are fully enabled.
22
+/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
23
+/      are removed.
24
+/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
25
+/   3: f_lseek() function is removed in addition to 2. */
26
+
27
+
28
+#define FF_USE_FIND		0
29
+/* This option switches filtered directory read functions, f_findfirst() and
30
+/  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
31
+
32
+
33
+#define FF_USE_MKFS		1
34
+/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
35
+
36
+
37
+#define FF_USE_FASTSEEK	0
38
+/* This option switches fast seek function. (0:Disable or 1:Enable) */
39
+
40
+
41
+#define FF_USE_EXPAND	0
42
+/* This option switches f_expand function. (0:Disable or 1:Enable) */
43
+
44
+
45
+#define FF_USE_CHMOD	0
46
+/* This option switches attribute manipulation functions, f_chmod() and f_utime().
47
+/  (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
48
+
49
+
50
+#define FF_USE_LABEL	1
51
+/* This option switches volume label functions, f_getlabel() and f_setlabel().
52
+/  (0:Disable or 1:Enable) */
53
+
54
+
55
+#define FF_USE_FORWARD	0
56
+/* This option switches f_forward() function. (0:Disable or 1:Enable) */
57
+
58
+
59
+#define FF_USE_STRFUNC	0
60
+#define FF_PRINT_LLI	1
61
+#define FF_PRINT_FLOAT	1
62
+#define FF_STRF_ENCODE	3
63
+/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
64
+/  f_printf().
65
+/
66
+/   0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
67
+/   1: Enable without LF-CRLF conversion.
68
+/   2: Enable with LF-CRLF conversion.
69
+/
70
+/  FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
71
+/  makes f_printf() support floating point argument. These features want C99 or later.
72
+/  When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
73
+/  encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
74
+/  to be read/written via those functions.
75
+/
76
+/   0: ANSI/OEM in current CP
77
+/   1: Unicode in UTF-16LE
78
+/   2: Unicode in UTF-16BE
79
+/   3: Unicode in UTF-8
80
+*/
81
+
82
+
83
+/*---------------------------------------------------------------------------/
84
+/ Locale and Namespace Configurations
85
+/---------------------------------------------------------------------------*/
86
+
87
+#define FF_CODE_PAGE	850
88
+/* This option specifies the OEM code page to be used on the target system.
89
+/  Incorrect code page setting can cause a file open failure.
90
+/
91
+/   437 - U.S.
92
+/   720 - Arabic
93
+/   737 - Greek
94
+/   771 - KBL
95
+/   775 - Baltic
96
+/   850 - Latin 1
97
+/   852 - Latin 2
98
+/   855 - Cyrillic
99
+/   857 - Turkish
100
+/   860 - Portuguese
101
+/   861 - Icelandic
102
+/   862 - Hebrew
103
+/   863 - Canadian French
104
+/   864 - Arabic
105
+/   865 - Nordic
106
+/   866 - Russian
107
+/   869 - Greek 2
108
+/   932 - Japanese (DBCS)
109
+/   936 - Simplified Chinese (DBCS)
110
+/   949 - Korean (DBCS)
111
+/   950 - Traditional Chinese (DBCS)
112
+/     0 - Include all code pages above and configured by f_setcp()
113
+*/
114
+
115
+
116
+#define FF_USE_LFN		3
117
+#define FF_MAX_LFN		255
118
+/* The FF_USE_LFN switches the support for LFN (long file name).
119
+/
120
+/   0: Disable LFN. FF_MAX_LFN has no effect.
121
+/   1: Enable LFN with static  working buffer on the BSS. Always NOT thread-safe.
122
+/   2: Enable LFN with dynamic working buffer on the STACK.
123
+/   3: Enable LFN with dynamic working buffer on the HEAP.
124
+/
125
+/  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
126
+/  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
127
+/  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
128
+/  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
129
+/  be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
130
+/  specification.
131
+/  When use stack for the working buffer, take care on stack overflow. When use heap
132
+/  memory for the working buffer, memory management functions, ff_memalloc() and
133
+/  ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
134
+
135
+
136
+#define FF_LFN_UNICODE	2
137
+/* This option switches the character encoding on the API when LFN is enabled.
138
+/
139
+/   0: ANSI/OEM in current CP (TCHAR = char)
140
+/   1: Unicode in UTF-16 (TCHAR = WCHAR)
141
+/   2: Unicode in UTF-8 (TCHAR = char)
142
+/   3: Unicode in UTF-32 (TCHAR = DWORD)
143
+/
144
+/  Also behavior of string I/O functions will be affected by this option.
145
+/  When LFN is not enabled, this option has no effect. */
146
+
147
+
148
+#define FF_LFN_BUF		255
149
+#define FF_SFN_BUF		12
150
+/* This set of options defines size of file name members in the FILINFO structure
151
+/  which is used to read out directory items. These values should be suffcient for
152
+/  the file names to read. The maximum possible length of the read file name depends
153
+/  on character encoding. When LFN is not enabled, these options have no effect. */
154
+
155
+
156
+#define FF_FS_RPATH		0
157
+/* This option configures support for relative path.
158
+/
159
+/   0: Disable relative path and remove related functions.
160
+/   1: Enable relative path. f_chdir() and f_chdrive() are available.
161
+/   2: f_getcwd() function is available in addition to 1.
162
+*/
163
+
164
+
165
+/*---------------------------------------------------------------------------/
166
+/ Drive/Volume Configurations
167
+/---------------------------------------------------------------------------*/
168
+
169
+#define FF_VOLUMES		1
170
+/* Number of volumes (logical drives) to be used. (1-10) */
171
+
172
+
173
+#define FF_STR_VOLUME_ID	0
174
+#define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
175
+/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
176
+/  When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
177
+/  number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
178
+/  logical drives. Number of items must not be less than FF_VOLUMES. Valid
179
+/  characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
180
+/  compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
181
+/  not defined, a user defined volume string table is needed as:
182
+/
183
+/  const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
184
+*/
185
+
186
+
187
+#define FF_MULTI_PARTITION	0
188
+/* This option switches support for multiple volumes on the physical drive.
189
+/  By default (0), each logical drive number is bound to the same physical drive
190
+/  number and only an FAT volume found on the physical drive will be mounted.
191
+/  When this function is enabled (1), each logical drive number can be bound to
192
+/  arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
193
+/  function will be available. */
194
+
195
+
196
+#define FF_MIN_SS		512
197
+#define FF_MAX_SS		512
198
+/* This set of options configures the range of sector size to be supported. (512,
199
+/  1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
200
+/  harddisk, but a larger value may be required for on-board flash memory and some
201
+/  type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
202
+/  for variable sector size mode and disk_ioctl() function needs to implement
203
+/  GET_SECTOR_SIZE command. */
204
+
205
+
206
+#define FF_LBA64		0
207
+/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
208
+/  To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
209
+
210
+
211
+#define FF_MIN_GPT		0x10000000
212
+/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
213
+/  f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
214
+
215
+
216
+#define FF_USE_TRIM		0
217
+/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
218
+/  To enable Trim function, also CTRL_TRIM command should be implemented to the
219
+/  disk_ioctl() function. */
220
+
221
+
222
+
223
+/*---------------------------------------------------------------------------/
224
+/ System Configurations
225
+/---------------------------------------------------------------------------*/
226
+
227
+#define FF_FS_TINY		0
228
+/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
229
+/  At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
230
+/  Instead of private sector buffer eliminated from the file object, common sector
231
+/  buffer in the filesystem object (FATFS) is used for the file data transfer. */
232
+
233
+
234
+#define FF_FS_EXFAT		0
235
+/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
236
+/  To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
237
+/  Note that enabling exFAT discards ANSI C (C89) compatibility. */
238
+
239
+
240
+#define FF_FS_NORTC		1
241
+#define FF_NORTC_MON	1
242
+#define FF_NORTC_MDAY	1
243
+#define FF_NORTC_YEAR	2022
244
+/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
245
+/  an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
246
+/  timestamp feature. Every object modified by FatFs will have a fixed timestamp
247
+/  defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
248
+/  To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
249
+/  added to the project to read current time form real-time clock. FF_NORTC_MON,
250
+/  FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
251
+/  These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
252
+
253
+
254
+#define FF_FS_NOFSINFO	0
255
+/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
256
+/  option, and f_getfree() function at the first time after volume mount will force
257
+/  a full FAT scan. Bit 1 controls the use of last allocated cluster number.
258
+/
259
+/  bit0=0: Use free cluster count in the FSINFO if available.
260
+/  bit0=1: Do not trust free cluster count in the FSINFO.
261
+/  bit1=0: Use last allocated cluster number in the FSINFO if available.
262
+/  bit1=1: Do not trust last allocated cluster number in the FSINFO.
263
+*/
264
+
265
+
266
+#define FF_FS_LOCK		0
267
+/* The option FF_FS_LOCK switches file lock function to control duplicated file open
268
+/  and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
269
+/  is 1.
270
+/
271
+/  0:  Disable file lock function. To avoid volume corruption, application program
272
+/      should avoid illegal open, remove and rename to the open objects.
273
+/  >0: Enable file lock function. The value defines how many files/sub-directories
274
+/      can be opened simultaneously under file lock control. Note that the file
275
+/      lock control is independent of re-entrancy. */
276
+
277
+
278
+#define FF_FS_REENTRANT	0
279
+#define FF_FS_TIMEOUT	1000
280
+/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
281
+/  module itself. Note that regardless of this option, file access to different
282
+/  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
283
+/  and f_fdisk() function, are always not re-entrant. Only file/directory access
284
+/  to the same volume is under control of this featuer.
285
+/
286
+/   0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
287
+/   1: Enable re-entrancy. Also user provided synchronization handlers,
288
+/      ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give()
289
+/      function, must be added to the project. Samples are available in ffsystem.c.
290
+/
291
+/  The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
292
+*/
293
+
294
+
295
+
296
+/*--- End of configuration options ---*/

+ 2
- 0
firmware/include/log.h View File

@@ -18,6 +18,8 @@
18 18
 #define println(fmt, ...) debug_log(false, fmt "\r\n", ##__VA_ARGS__)
19 19
 
20 20
 void debug_log(bool log, const char *format, ...) __attribute__((format(printf, 2, 3)));
21
+
21 22
 void log_dump_to_usb(void);
23
+void log_dump_to_disk(void);
22 24
 
23 25
 #endif // __LOG_H__

+ 2
- 2
firmware/include/pmw3360.h View File

@@ -33,8 +33,8 @@ uint8_t pmw_get_sensitivity(void);
33 33
 #define PMW_SENSE_TO_CPI(sense) (100 + (sense * 100))
34 34
 #define PMW_CPI_TO_SENSE(cpi) ((cpi / 100) - 1)
35 35
 
36
-void pmw_print_status(void);
37
-void pmw_dump_data(void);
36
+void pmw_print_status(char *buff, size_t len);
37
+void pmw_dump_data(bool serial);
38 38
 
39 39
 ssize_t pmw_frame_capture(uint8_t *buff, size_t buffsize);
40 40
 #define PMW_FRAME_CAPTURE_LEN 1296

+ 4
- 1
firmware/include/tusb_config.h View File

@@ -97,7 +97,7 @@
97 97
 //------------- CLASS -------------//
98 98
 #define CFG_TUD_HID               1
99 99
 #define CFG_TUD_CDC               1
100
-#define CFG_TUD_MSC               0
100
+#define CFG_TUD_MSC               1
101 101
 #define CFG_TUD_MIDI              0
102 102
 #define CFG_TUD_VENDOR            0
103 103
 
@@ -111,6 +111,9 @@
111 111
 // CDC Endpoint transfer buffer size, more is faster
112 112
 #define CFG_TUD_CDC_EP_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
113 113
 
114
+// MSC Buffer size of Device Mass storage
115
+#define CFG_TUD_MSC_EP_BUFSIZE   512
116
+
114 117
 #ifdef __cplusplus
115 118
  }
116 119
 #endif

+ 11
- 0
firmware/include/usb_msc.h View File

@@ -0,0 +1,11 @@
1
+/*
2
+ * usb_msc.h
3
+ */
4
+
5
+#ifndef __USB_MSC_H__
6
+#define __USB_MSC_H__
7
+
8
+bool msc_is_medium_available(void);
9
+void msc_set_medium_available(bool state);
10
+
11
+#endif // __USB_MSC_H__

+ 41
- 3
firmware/src/console.c View File

@@ -10,6 +10,8 @@
10 10
 #include "log.h"
11 11
 #include "pmw3360.h"
12 12
 #include "util.h"
13
+#include "usb_msc.h"
14
+#include "debug.h"
13 15
 #include "console.h"
14 16
 
15 17
 #define CNSL_BUFF_SIZE 1024
@@ -59,13 +61,18 @@ static void cnsl_interpret(const char *line) {
59 61
         println("   \\x18 - reset to bootloader");
60 62
         println(" repeat - repeat last command every %d milliseconds", CNSL_REPEAT_MS);
61 63
         println("   help - print this message");
64
+        println("  stats - put statistics on mass storage medium");
65
+        println("   data - put PMW3360 data on mass storage medium");
66
+        println("  mount - make mass storage medium (un)available");
62 67
         println("Press Enter with no input to repeat last command.");
63 68
         println("Use repeat to continuously execute last command.");
64 69
         println("Stop this by calling repeat again.");
65 70
     } else if (strcmp(line, "pmws") == 0) {
66
-        pmw_print_status();
71
+        char status_buff[1024];
72
+        pmw_print_status(status_buff, sizeof(status_buff));
73
+        print("%s", status_buff);
67 74
     } else if (strcmp(line, "pmwd") == 0) {
68
-        pmw_dump_data();
75
+        pmw_dump_data(true);
69 76
     } else if (strcmp(line, "pmwf") == 0) {
70 77
         uint8_t frame[PMW_FRAME_CAPTURE_LEN];
71 78
         ssize_t r = pmw_frame_capture(frame, PMW_FRAME_CAPTURE_LEN);
@@ -102,9 +109,40 @@ static void cnsl_interpret(const char *line) {
102 109
         }
103 110
     } else if (strcmp(line, "reset") == 0) {
104 111
         reset_to_main();
112
+    } else if ((strcmp(line, "stats") == 0) || (strcmp(line, "data") == 0)) {
113
+        if (msc_is_medium_available()) {
114
+            println("Currently mounted. Unplugging now.");
115
+            msc_set_medium_available(false);
116
+        }
117
+
118
+        if (debug_msc_mount() != 0) {
119
+            println("Error mounting file system.");
120
+            println();
121
+            return;
122
+        }
123
+
124
+        println("Writing data to file system");
125
+
126
+        if (strcmp(line, "data") == 0) {
127
+            debug_msc_pmw3360();
128
+        }
129
+
130
+        debug_msc_stats();
131
+
132
+        if (debug_msc_unmount() != 0) {
133
+            println("Error unmounting file system.");
134
+        }
135
+
136
+        println("Done. Plugging in now.");
137
+        msc_set_medium_available(true);
138
+    } else if (strcmp(line, "mount") == 0) {
139
+        bool state = msc_is_medium_available();
140
+        println("Currently %s. %s now.",
141
+                state ? "mounted" : "unmounted",
142
+                state ? "Unplugging" : "Plugging in");
143
+        msc_set_medium_available(!state);
105 144
     } else {
106 145
         println("unknown command \"%s\"", line);
107
-        return;
108 146
     }
109 147
 
110 148
     println();

+ 108
- 0
firmware/src/debug.c View File

@@ -0,0 +1,108 @@
1
+/*
2
+ * debug.c
3
+ */
4
+
5
+#include <string.h>
6
+#include "pico/stdlib.h"
7
+#include "ff.h"
8
+
9
+#include "config.h"
10
+#include "log.h"
11
+#include "pmw3360.h"
12
+#include "debug.h"
13
+
14
+static FATFS fs;
15
+static bool mounted = false;
16
+
17
+int debug_msc_mount(void) {
18
+    if (mounted) {
19
+        debug("already mounted");
20
+        return 0;
21
+    }
22
+
23
+    FRESULT res = f_mount(&fs, "", 0);
24
+    if (res != FR_OK) {
25
+        debug("error: f_mount returned %d", res);
26
+        mounted = false;
27
+        return -1;
28
+    }
29
+
30
+    mounted = true;
31
+    return 0;
32
+}
33
+
34
+int debug_msc_unmount(void) {
35
+    if (!mounted) {
36
+        debug("already unmounted");
37
+        return 0;
38
+    }
39
+
40
+    FRESULT res = f_mount(0, "", 0);
41
+    if (res != FR_OK) {
42
+        debug("error: f_mount returned %d", res);
43
+        return -1;
44
+    }
45
+
46
+    mounted = false;
47
+    return 0;
48
+}
49
+
50
+static void debug_msc_pmw_stats(void) {
51
+    FIL file;
52
+    FRESULT res = f_open(&file, "pmw_stats.txt", FA_CREATE_ALWAYS | FA_WRITE);
53
+    if (res != FR_OK) {
54
+        debug("error: f_open returned %d", res);
55
+        return;
56
+    }
57
+
58
+    char status_buff[1024];
59
+    pmw_print_status(status_buff, sizeof(status_buff));
60
+    size_t len = strlen(status_buff);
61
+
62
+    UINT bw;
63
+    res = f_write(&file, status_buff, len, &bw);
64
+    if ((res != FR_OK) || (bw != len)) {
65
+        debug("error: f_write returned %d", res);
66
+    }
67
+
68
+    res = f_close(&file);
69
+    if (res != FR_OK) {
70
+        debug("error: f_close returned %d", res);
71
+    }
72
+}
73
+
74
+void debug_msc_stats(void) {
75
+    debug_msc_pmw_stats();
76
+    log_dump_to_disk();
77
+}
78
+
79
+static void debug_msc_pmw3360_frame(void) {
80
+    FIL file;
81
+    FRESULT res = f_open(&file, "pmw_frame.bin", FA_CREATE_ALWAYS | FA_WRITE);
82
+    if (res != FR_OK) {
83
+        debug("error: f_open returned %d", res);
84
+        return;
85
+    }
86
+
87
+    uint8_t frame[PMW_FRAME_CAPTURE_LEN];
88
+    ssize_t r = pmw_frame_capture(frame, PMW_FRAME_CAPTURE_LEN);
89
+    if (r != PMW_FRAME_CAPTURE_LEN) {
90
+        debug("error: pmw_frame_capture %d != %d", r, PMW_FRAME_CAPTURE_LEN);
91
+    } else {
92
+        UINT bw;
93
+        res = f_write(&file, frame, r, &bw);
94
+        if ((res != FR_OK) || (bw != r)) {
95
+            debug("error: f_write returned %d", res);
96
+        }
97
+    }
98
+
99
+    res = f_close(&file);
100
+    if (res != FR_OK) {
101
+        debug("error: f_close returned %d", res);
102
+    }
103
+}
104
+
105
+void debug_msc_pmw3360(void) {
106
+    pmw_dump_data(false);
107
+    debug_msc_pmw3360_frame();
108
+}

+ 116
- 0
firmware/src/fat_disk.c View File

@@ -0,0 +1,116 @@
1
+/* 
2
+ * fat_disk.c
3
+ */
4
+
5
+#include <string.h>
6
+#include <stdlib.h>
7
+
8
+#include "pico/stdlib.h"
9
+#include "ff.h"
10
+#include "diskio.h"
11
+
12
+#include "config.h"
13
+#include "log.h"
14
+#include "fat_disk.h"
15
+
16
+static uint8_t disk[DISK_BLOCK_COUNT * DISK_BLOCK_SIZE];
17
+
18
+void fat_disk_init(void) {
19
+    BYTE work[FF_MAX_SS];
20
+    FRESULT res = f_mkfs("", 0, work, sizeof(work));
21
+    if (res != FR_OK) {
22
+        debug("error: f_mkfs returned %d", res);
23
+        return;
24
+    }
25
+}
26
+
27
+uint8_t *fat_disk_get_sector(uint32_t sector) {
28
+    return disk + (sector * DISK_BLOCK_SIZE);
29
+}
30
+
31
+/*
32
+ * FatFS ffsystem.c
33
+ */
34
+
35
+void* ff_memalloc(UINT msize) {
36
+    return malloc((size_t)msize);
37
+}
38
+
39
+void ff_memfree(void* mblock) {
40
+    free(mblock);
41
+}
42
+
43
+/*
44
+ * FatFS diskio.c
45
+ */
46
+
47
+DSTATUS disk_status(BYTE pdrv) {
48
+    if (pdrv != 0) {
49
+        debug("invalid drive number %d", pdrv);
50
+        return STA_NODISK;
51
+    }
52
+
53
+    return 0;
54
+}
55
+
56
+DSTATUS disk_initialize(BYTE pdrv) {
57
+    if (pdrv != 0) {
58
+        debug("invalid drive number %d", pdrv);
59
+        return STA_NODISK;
60
+    }
61
+
62
+    return 0;
63
+}
64
+
65
+DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
66
+    if (pdrv != 0) {
67
+        debug("invalid drive number %d", pdrv);
68
+        return RES_PARERR;
69
+    }
70
+
71
+    if ((sector + count) > DISK_BLOCK_COUNT) {
72
+        debug("invalid read ((%lu + %u) > %u)", sector, count, DISK_BLOCK_COUNT);
73
+        return RES_ERROR;
74
+    }
75
+
76
+    memcpy(buff, disk + (sector * DISK_BLOCK_SIZE), count * DISK_BLOCK_SIZE);
77
+    return RES_OK;
78
+}
79
+
80
+DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
81
+    if (pdrv != 0) {
82
+        debug("invalid drive number %d", pdrv);
83
+        return RES_PARERR;
84
+    }
85
+
86
+    if ((sector + count) > DISK_BLOCK_COUNT) {
87
+        debug("invalid read ((%lu + %u) > %u)", sector, count, DISK_BLOCK_COUNT);
88
+        return RES_ERROR;
89
+    }
90
+
91
+    memcpy(disk + (sector * DISK_BLOCK_SIZE), buff, count * DISK_BLOCK_SIZE);
92
+    return RES_OK;
93
+}
94
+
95
+DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
96
+    if (pdrv != 0) {
97
+        debug("invalid drive number %d", pdrv);
98
+        return RES_PARERR;
99
+    }
100
+
101
+    switch (cmd) {
102
+        case GET_SECTOR_COUNT:
103
+            *((LBA_t *)buff) = DISK_BLOCK_COUNT;
104
+            break;
105
+
106
+        case GET_SECTOR_SIZE:
107
+            *((WORD *)buff) = DISK_BLOCK_SIZE;
108
+            break;
109
+
110
+        case GET_BLOCK_SIZE:
111
+            *((DWORD *)buff) = 1; // non flash memory media
112
+            break;
113
+    }
114
+
115
+    return RES_OK;
116
+}

+ 34
- 0
firmware/src/log.c View File

@@ -5,6 +5,7 @@
5 5
 #include <stdarg.h>
6 6
 #include <stdio.h>
7 7
 #include "pico/stdlib.h"
8
+#include "ff.h"
8 9
 
9 10
 #include "config.h"
10 11
 #include "usb_cdc.h"
@@ -54,6 +55,39 @@ void log_dump_to_usb(void) {
54 55
     }
55 56
 }
56 57
 
58
+void log_dump_to_disk(void) {
59
+    FIL file;
60
+    FRESULT res = f_open(&file, "log.txt", FA_CREATE_ALWAYS | FA_WRITE);
61
+    if (res != FR_OK) {
62
+        debug("error: f_open returned %d", res);
63
+        return;
64
+    }
65
+
66
+    UINT bw;
67
+
68
+    if (head > tail) {
69
+        res = f_write(&file, log_buff + tail, head - tail, &bw);
70
+        if ((res != FR_OK) || (bw != head - tail)) {
71
+            debug("error: f_write (A) returned %d", res);
72
+        }
73
+    } else if (head < tail) {
74
+        res = f_write(&file, log_buff + tail, sizeof(log_buff) - tail, &bw);
75
+        if ((res != FR_OK) || (bw != sizeof(log_buff) - tail)) {
76
+            debug("error: f_write (B) returned %d", res);
77
+        } else {
78
+            res = f_write(&file, log_buff, head, &bw);
79
+            if ((res != FR_OK) || (bw != head)) {
80
+                debug("error: f_write (C) returned %d", res);
81
+            }
82
+        }
83
+    }
84
+
85
+    res = f_close(&file);
86
+    if (res != FR_OK) {
87
+        debug("error: f_close returned %d", res);
88
+    }
89
+}
90
+
57 91
 static int format_debug_log(char *buff, size_t len, const char *format, va_list args) {
58 92
     int l = vsnprintf(buff, len, format, args);
59 93
 

+ 5
- 0
firmware/src/main.c View File

@@ -11,6 +11,7 @@
11 11
 #include "log.h"
12 12
 #include "usb.h"
13 13
 #include "pmw3360.h"
14
+#include "fat_disk.h"
14 15
 
15 16
 int main(void) {
16 17
     heartbeat_init();
@@ -22,6 +23,10 @@ int main(void) {
22 23
         debug("reset by watchdog");
23 24
     }
24 25
 
26
+    debug("fat_disk_init");
27
+    fat_disk_init();
28
+
29
+    debug("pmw_init");
25 30
     if (pmw_init() != 0) {
26 31
         debug("error initializing PMW3360");
27 32
     }

+ 68
- 28
firmware/src/pmw3360.c View File

@@ -17,10 +17,12 @@
17 17
  * The Pico GPIO (and therefore SPI) cannot be used at 5v.
18 18
  */
19 19
 
20
+#include <stdio.h>
20 21
 #include "pico/stdlib.h"
21 22
 #include "pico/binary_info.h"
22 23
 #include "hardware/spi.h"
23 24
 #include "hardware/watchdog.h"
25
+#include "ff.h"
24 26
 
25 27
 #include "config.h"
26 28
 #include "log.h"
@@ -51,25 +53,27 @@ static uint64_t pmw_irq_count_rest2 = 0;
51 53
 static uint64_t pmw_irq_count_rest3 = 0;
52 54
 #endif // PMW_IRQ_COUNTERS
53 55
 
54
-void pmw_print_status(void) {
56
+void pmw_print_status(char *buff, size_t len) {
57
+    size_t pos = 0;
58
+
55 59
     bool com = pmw_is_alive();
56 60
     if (com) {
57
-        println("Communication to PMW3360 is working");
61
+        pos += snprintf(buff + pos, len - pos, "Communication to PMW3360 is working\r\n");
58 62
     } else {
59
-        println("ERROR: can not communicate to PMW3360");
63
+        pos += snprintf(buff + pos, len - pos, "ERROR: can not communicate to PMW3360\r\n");
60 64
     }
61 65
 
62 66
 #ifdef PMW_IRQ_COUNTERS
63
-    println("Interrupt statistics:");
64
-    println("    pmw_irq_cnt_all = %llu", pmw_irq_count_all);
65
-    println(" pmw_irq_cnt_motion = %llu", pmw_irq_count_motion);
66
-    println("pmw_irq_cnt_no_move = %llu", pmw_irq_count_no_motion);
67
-    println("pmw_irq_cnt_surface = %llu", pmw_irq_count_on_surface);
68
-    println(" pmw_irq_cnt_lifted = %llu", pmw_irq_count_lifted);
69
-    println("    pmw_irq_cnt_run = %llu", pmw_irq_count_run);
70
-    println("  pmw_irq_cnt_rest1 = %llu", pmw_irq_count_rest1);
71
-    println("  pmw_irq_cnt_rest2 = %llu", pmw_irq_count_rest2);
72
-    println("  pmw_irq_cnt_rest3 = %llu", pmw_irq_count_rest3);
67
+    pos += snprintf(buff + pos, len - pos, "Interrupt statistics:\r\n");
68
+    pos += snprintf(buff + pos, len - pos, "    pmw_irq_cnt_all = %llu\r\n", pmw_irq_count_all);
69
+    pos += snprintf(buff + pos, len - pos, " pmw_irq_cnt_motion = %llu\r\n", pmw_irq_count_motion);
70
+    pos += snprintf(buff + pos, len - pos, "pmw_irq_cnt_no_move = %llu\r\n", pmw_irq_count_no_motion);
71
+    pos += snprintf(buff + pos, len - pos, "pmw_irq_cnt_surface = %llu\r\n", pmw_irq_count_on_surface);
72
+    pos += snprintf(buff + pos, len - pos, " pmw_irq_cnt_lifted = %llu\r\n", pmw_irq_count_lifted);
73
+    pos += snprintf(buff + pos, len - pos, "    pmw_irq_cnt_run = %llu\r\n", pmw_irq_count_run);
74
+    pos += snprintf(buff + pos, len - pos, "  pmw_irq_cnt_rest1 = %llu\r\n", pmw_irq_count_rest1);
75
+    pos += snprintf(buff + pos, len - pos, "  pmw_irq_cnt_rest2 = %llu\r\n", pmw_irq_count_rest2);
76
+    pos += snprintf(buff + pos, len - pos, "  pmw_irq_cnt_rest3 = %llu\r\n", pmw_irq_count_rest3);
73 77
 #endif // PMW_IRQ_COUNTERS
74 78
 }
75 79
 
@@ -384,9 +388,29 @@ ssize_t pmw_frame_capture(uint8_t *buff, size_t buffsize) {
384 388
     return PMW_FRAME_CAPTURE_LEN;
385 389
 }
386 390
 
387
-#define PMW_DATA_DUMP_SAMPLES 5000
391
+#define PMW_DATA_DUMP_SAMPLES 1000
392
+
393
+#define PRINTRAW(fmt, ...) {                             \
394
+    if (serial) {                                        \
395
+        print(fmt, ##__VA_ARGS__);                       \
396
+    } else {                                             \
397
+        int n = snprintf(line, 100, fmt, ##__VA_ARGS__); \
398
+        UINT bw;                                         \
399
+        f_write(&file, line, n, &bw);                    \
400
+    }                                                    \
401
+}
402
+
403
+void pmw_dump_data(bool serial) {
404
+    char line[100];
405
+    FIL file;
406
+    if (!serial) {
407
+        FRESULT res = f_open(&file, "pmw_data.csv", FA_CREATE_ALWAYS | FA_WRITE);
408
+        if (res != FR_OK) {
409
+            debug("error: f_open returned %d", res);
410
+            return;
411
+        }
412
+    }
388 413
 
389
-void pmw_dump_data(void) {
390 414
     struct pmw_motion_report buff[PMW_DATA_DUMP_SAMPLES];
391 415
 
392 416
     println("Will now capture %u data samples from PMW3360", PMW_DATA_DUMP_SAMPLES);
@@ -403,8 +427,14 @@ void pmw_dump_data(void) {
403 427
         buff[i] = pmw_motion_read();
404 428
     }
405 429
 
406
-    println();
407
-    println("time,motion,observation,delta_x,delta_y,squal,raw_sum,raw_max,raw_min,shutter");
430
+    if (serial) {
431
+        println();
432
+    } else {
433
+        println("Now writing data");
434
+    }
435
+
436
+    PRINTRAW("time,motion,observation,delta_x,delta_y,squal,raw_sum,raw_max,raw_min,shutter\r\n");
437
+
408 438
     for (size_t i = 0; i < PMW_DATA_DUMP_SAMPLES; i++) {
409 439
         watchdog_update();
410 440
 
@@ -412,20 +442,30 @@ void pmw_dump_data(void) {
412 442
         uint16_t delta_y_raw = buff[i].delta_y_l | (buff[i].delta_y_h << 8);
413 443
         uint16_t shutter_raw = buff[i].shutter_lower | (buff[i].shutter_upper << 8);
414 444
 
415
-        print("%llu,", to_us_since_boot(get_absolute_time()));
416
-        print("%u,", buff[i].motion);
417
-        print("%u,", buff[i].observation);
418
-        print("%ld,", convert_two_complement(delta_x_raw));
419
-        print("%ld,", convert_two_complement(delta_y_raw));
420
-        print("%u,", buff[i].squal);
421
-        print("%u,", buff[i].raw_data_sum);
422
-        print("%u,", buff[i].maximum_raw_data);
423
-        print("%u,", buff[i].minimum_raw_data);
424
-        println("%u", shutter_raw);
445
+        PRINTRAW("%llu,", to_us_since_boot(get_absolute_time()));
446
+        PRINTRAW("%u,", buff[i].motion);
447
+        PRINTRAW("%u,", buff[i].observation);
448
+        PRINTRAW("%ld,", convert_two_complement(delta_x_raw));
449
+        PRINTRAW("%ld,", convert_two_complement(delta_y_raw));
450
+        PRINTRAW("%u,", buff[i].squal);
451
+        PRINTRAW("%u,", buff[i].raw_data_sum);
452
+        PRINTRAW("%u,", buff[i].maximum_raw_data);
453
+        PRINTRAW("%u,", buff[i].minimum_raw_data);
454
+        PRINTRAW("%u\r\n", shutter_raw);
455
+    }
456
+
457
+    if (serial) {
458
+        println();
425 459
     }
426
-    println();
427 460
 
428 461
     pmw_irq_start();
462
+
463
+    if (!serial) {
464
+        FRESULT res = f_close(&file);
465
+        if (res != FR_OK) {
466
+            debug("error: f_close returned %d", res);
467
+        }
468
+    }
429 469
 }
430 470
 
431 471
 int pmw_init(void) {

+ 5
- 1
firmware/src/usb.c View File

@@ -27,6 +27,7 @@
27 27
 #include "tusb.h"
28 28
 
29 29
 #include "config.h"
30
+#include "log.h"
30 31
 #include "usb_descriptors.h"
31 32
 #include "usb_cdc.h"
32 33
 #include "usb_hid.h"
@@ -46,19 +47,22 @@ void usb_run(void) {
46 47
 
47 48
 // Invoked when device is mounted
48 49
 void tud_mount_cb(void) {
50
+    debug("device mounted");
49 51
 }
50 52
 
51 53
 // Invoked when device is unmounted
52 54
 void tud_umount_cb(void) {
55
+    debug("device unmounted");
53 56
 }
54 57
 
55 58
 // Invoked when usb bus is suspended
56 59
 // remote_wakeup_en : if host allow us  to perform remote wakeup
57 60
 // Within 7ms, device must draw an average of current less than 2.5 mA from bus
58 61
 void tud_suspend_cb(bool remote_wakeup_en) {
59
-    (void) remote_wakeup_en;
62
+    debug("device suspended wakeup=%d", remote_wakeup_en);
60 63
 }
61 64
 
62 65
 // Invoked when usb bus is resumed
63 66
 void tud_resume_cb(void) {
67
+    debug("device resumed");
64 68
 }

+ 10
- 2
firmware/src/usb_descriptors.c View File

@@ -110,15 +110,18 @@ enum {
110 110
     ITF_NUM_CDC = 0,
111 111
     ITF_NUM_CDC_DATA,
112 112
     ITF_NUM_HID,
113
+    ITF_NUM_MSC,
113 114
     ITF_NUM_TOTAL
114 115
 };
115 116
 
116
-#define  CONFIG_TOTAL_LEN  (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_DESC_LEN)
117
+#define  CONFIG_TOTAL_LEN  (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_DESC_LEN + TUD_MSC_DESC_LEN)
117 118
 
118 119
 #define EPNUM_HID       0x81
119 120
 #define EPNUM_CDC_NOTIF 0x82
120 121
 #define EPNUM_CDC_OUT   0x02
121 122
 #define EPNUM_CDC_IN    0x83
123
+#define EPNUM_MSC_OUT   0x03
124
+#define EPNUM_MSC_IN    0x84
122 125
 
123 126
 uint8_t const desc_configuration[] = {
124 127
     // Config number, interface count, string index, total length, attribute, power in mA
@@ -128,7 +131,10 @@ uint8_t const desc_configuration[] = {
128 131
     TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
129 132
 
130 133
     // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
131
-    TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5)
134
+    TUD_HID_DESCRIPTOR(ITF_NUM_HID, 5, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5),
135
+
136
+    // Interface number, string index, EP Out & EP In address, EP size
137
+    TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 6, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
132 138
 };
133 139
 
134 140
 #if TUD_OPT_HIGH_SPEED
@@ -203,6 +209,8 @@ char const* string_desc_arr [] = {
203 209
     "Trackball",                   // 2: Product
204 210
     string_pico_serial,            // 3: Serials, should use chip ID
205 211
     "Debug Serial",                // 4: CDC Interface
212
+    "Input Device",                // 5: HID Interface
213
+    "Debug Memory",                // 6: MSC Interface
206 214
 };
207 215
 
208 216
 static uint16_t _desc_str[32];

+ 184
- 0
firmware/src/usb_msc.c View File

@@ -0,0 +1,184 @@
1
+/* 
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ *
24
+ */
25
+
26
+#include "bsp/board.h"
27
+#include "tusb.h"
28
+
29
+#include "config.h"
30
+#include "fat_disk.h"
31
+#include "log.h"
32
+
33
+static bool ejected = false;
34
+static bool medium_available = false;
35
+
36
+bool msc_is_medium_available(void) {
37
+    return medium_available;
38
+}
39
+
40
+void msc_set_medium_available(bool state) {
41
+    medium_available = state;
42
+    if (state) {
43
+        ejected = false;
44
+    }
45
+}
46
+
47
+// Invoked when received SCSI_CMD_INQUIRY
48
+// Application fill vendor id, product id and revision
49
+// with string up to 8, 16, 4 characters respectively
50
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8],
51
+        uint8_t product_id[16], uint8_t product_rev[4]) {
52
+    (void) lun;
53
+
54
+    const char vid[] = "xythobuz";
55
+    const char pid[] = "Debug Storage";
56
+    const char rev[] = "0.01";
57
+
58
+    memcpy(vendor_id  , vid, strlen(vid));
59
+    memcpy(product_id , pid, strlen(pid));
60
+    memcpy(product_rev, rev, strlen(rev));
61
+}
62
+
63
+// Invoked when received Test Unit Ready command.
64
+// return true allowing host to read/write this LUN e.g SD card inserted
65
+bool tud_msc_test_unit_ready_cb(uint8_t lun) {
66
+    if (ejected || !medium_available) {
67
+        // Additional Sense 3A-00 is NOT_FOUND
68
+        tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
69
+        return false;
70
+    }
71
+
72
+    return true;
73
+}
74
+
75
+// Invoked when received SCSI_CMD_READ_CAPACITY_10 and
76
+// SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
77
+// Application update block count and block size
78
+void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) {
79
+    (void) lun;
80
+
81
+    *block_count = DISK_BLOCK_COUNT;
82
+    *block_size  = DISK_BLOCK_SIZE;
83
+}
84
+
85
+// Invoked when received Start Stop Unit command
86
+// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
87
+// - Start = 1 : active mode, if load_eject = 1 : load disk storage
88
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition,
89
+        bool start, bool load_eject) {
90
+    (void) lun;
91
+    (void) power_condition;
92
+
93
+    if (start) {
94
+        // load disk storage
95
+        debug("load disk storage %d", load_eject);
96
+    } else {
97
+        // unload disk storage
98
+        debug("unload disk storage %d", load_eject);
99
+        ejected = true;
100
+    }
101
+
102
+    return true;
103
+}
104
+
105
+// Callback invoked when received READ10 command.
106
+// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
107
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba,
108
+        uint32_t offset, void* buffer, uint32_t bufsize) {
109
+    (void) lun;
110
+
111
+    // out of ramdisk
112
+    if (lba >= DISK_BLOCK_COUNT) {
113
+        return -1;
114
+    }
115
+
116
+    // TODO better range checking and length calculation?
117
+
118
+    uint8_t const* addr = fat_disk_get_sector(lba) + offset;
119
+    memcpy(buffer, addr, bufsize);
120
+
121
+    return (int32_t) bufsize;
122
+}
123
+
124
+bool tud_msc_is_writable_cb (uint8_t lun) {
125
+    (void) lun;
126
+
127
+    return true;
128
+}
129
+
130
+// Callback invoked when received WRITE10 command.
131
+// Process data in buffer to disk's storage and return number of written bytes
132
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba,
133
+        uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
134
+    (void) lun;
135
+
136
+    // out of ramdisk
137
+    if (lba >= DISK_BLOCK_COUNT) {
138
+        return -1;
139
+    }
140
+
141
+    // TODO better range checking and length calculation?
142
+
143
+    uint8_t* addr = fat_disk_get_sector(lba) + offset;
144
+    memcpy(addr, buffer, bufsize);
145
+
146
+    return (int32_t) bufsize;
147
+}
148
+
149
+// Callback invoked when received an SCSI command not in built-in list below
150
+// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
151
+// - READ10 and WRITE10 has their own callbacks and MUST not be handled here
152
+int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16],
153
+        void* buffer, uint16_t bufsize) {
154
+    void const* response = NULL;
155
+    int32_t resplen = 0;
156
+
157
+    // most scsi handled is input
158
+    bool in_xfer = true;
159
+
160
+    switch (scsi_cmd[0]) {
161
+    default:
162
+        // Set Sense = Invalid Command Operation
163
+        tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
164
+
165
+        // negative means error -> tinyusb could stall and/or response with failed status
166
+        resplen = -1;
167
+        break;
168
+    }
169
+
170
+    // return resplen must not larger than bufsize
171
+    if (resplen > bufsize) {
172
+        resplen = bufsize;
173
+    }
174
+
175
+    if (response && (resplen > 0)) {
176
+        if (in_xfer) {
177
+            memcpy(buffer, response, (size_t) resplen);
178
+        } else {
179
+            // SCSI output
180
+        }
181
+    }
182
+
183
+    return (int32_t) resplen;
184
+}

Loading…
Cancel
Save