├── QUICKSTART.pdf ├── image ├── 1-1.png ├── 1-2.png ├── 1-3.png ├── 1-4.png ├── 2-1.png ├── 2-2.png ├── 2-3.png ├── 3-1.png ├── 3-2.png ├── 3-3.png ├── 3-4.png ├── 4-1.png └── 4-2.png ├── .gitignore ├── iconv ├── Makefile ├── iconv_mini.h ├── createtable.py ├── iconv_mini.c └── createtable.c ├── driver ├── clrconfig.py ├── fixupsys.py ├── createrescue.py ├── bindump.py ├── settinguisub.h ├── Makefile ├── bootloader.S ├── hdsboot.S ├── rescueboot.S ├── scsiremote.c ├── settinguipat.h └── settingui.c ├── .gitmodules ├── Makefile ├── config.tmpl.txt ├── include ├── config.h └── vd_command.h ├── src ├── config_file.h ├── main.h ├── virtual_disk.h ├── diskmap.txt ├── tusb_config.h ├── hdscache.c ├── main.c ├── msc_disk.c ├── FreeRTOSConfig.h ├── usb_descriptors.c ├── smb2connect.c ├── connect.c ├── fileop.h ├── config_file.c ├── vd_command.c └── virtual_disk.c ├── CMakeLists.txt ├── README.md ├── QUICKSTART.md └── md2txtconv.py /QUICKSTART.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/QUICKSTART.pdf -------------------------------------------------------------------------------- /image/1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/1-1.png -------------------------------------------------------------------------------- /image/1-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/1-2.png -------------------------------------------------------------------------------- /image/1-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/1-3.png -------------------------------------------------------------------------------- /image/1-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/1-4.png -------------------------------------------------------------------------------- /image/2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/2-1.png -------------------------------------------------------------------------------- /image/2-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/2-2.png -------------------------------------------------------------------------------- /image/2-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/2-3.png -------------------------------------------------------------------------------- /image/3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/3-1.png -------------------------------------------------------------------------------- /image/3-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/3-2.png -------------------------------------------------------------------------------- /image/3-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/3-3.png -------------------------------------------------------------------------------- /image/3-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/3-4.png -------------------------------------------------------------------------------- /image/4-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/4-1.png -------------------------------------------------------------------------------- /image/4-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunkya2/x68kzremotedrv/HEAD/image/4-2.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.o 3 | *.x 4 | *.zip 5 | *.sys 6 | *.elf* 7 | *.bin 8 | *.inc 9 | *.uf2 10 | *.HDS 11 | *.xdf -------------------------------------------------------------------------------- /iconv/Makefile: -------------------------------------------------------------------------------- 1 | all: createtable 2 | 3 | createtable: createtable.c 4 | $(CC) -o $@ $^ 5 | 6 | clean: 7 | -rm -f createtable 8 | -------------------------------------------------------------------------------- /driver/clrconfig.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | from struct import unpack, pack 4 | 5 | with open(sys.argv[1], 'wb') as f: 6 | data = bytearray(512) 7 | data[0:16] = pack('<4L', 0x0a324655, 0x9e5d5157, 0x00002000, 0x101f0000) 8 | data[16:32] = pack('<4L', 256, 0, 1, 0xe48bff56) 9 | data[508:512] = pack('4L', f.read(16)) 9 | data[0x5a:0x5a + 12] = pack('>3L', textsz + datasz, relsz, bsssz) 10 | with open(sys.argv[2], 'wb') as g: 11 | g.write(data[0x40:]) 12 | -------------------------------------------------------------------------------- /driver/createrescue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | from struct import unpack, pack 4 | 5 | with open(sys.argv[1], 'rb') as f1, open(sys.argv[2], 'rb') as f2: 6 | boot = f1.read() 7 | settingui = f2.read() 8 | 9 | xdf = bytearray(1024 * 2 * 8 * 77) 10 | xdf[0:len(boot)] = boot 11 | xdf[1024:1024 + len(settingui)] = settingui 12 | 13 | with open(sys.argv[3], 'wb') as g: 14 | g.write(xdf) 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pico-sdk"] 2 | path = pico-sdk 3 | url = https://github.com/raspberrypi/pico-sdk.git 4 | [submodule "libsmb2"] 5 | path = libsmb2 6 | url = https://github.com/yunkya2/libsmb2.git 7 | [submodule "FreeRTOS-Kernel"] 8 | path = FreeRTOS-Kernel 9 | url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git 10 | [submodule "x68kserremote"] 11 | path = x68kserremote 12 | url = https://github.com/yunkya2/x68kserremote.git 13 | -------------------------------------------------------------------------------- /driver/bindump.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | 4 | with open(sys.argv[1], 'rb') as f: 5 | data = bytearray(f.read()) 6 | print("/* automatically created by bindump.py */") 7 | print("#include ") 8 | print("static const uint8_t "+sys.argv[2]+"[] = {") 9 | i = 0 10 | for a in data: 11 | if (i % 16) == 0: 12 | print(" ", end='') 13 | print("0x{:02x},".format(a), end='') 14 | if (i % 16) == 15: 15 | print("") 16 | i += 1 17 | print("") 18 | print("};") 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | (cd build; make -j) 3 | 4 | build: 5 | -rm -rf build 6 | mkdir build 7 | (cd build; cmake ..) 8 | 9 | build-debug: 10 | -rm -rf build 11 | mkdir build 12 | (cd build; cmake -DCMAKE_BUILD_TYPE=Debug ..) 13 | 14 | clean: 15 | (cd driver; make clean) 16 | -rm -rf build 17 | 18 | build: FreeRTOS-Kernel/.git libsmb2/.git pico-sdk/.git 19 | 20 | FreeRTOS-Kernel/.git libsmb2/.git pico-sdk/.git update: 21 | git submodule update --init --recursive 22 | 23 | RELFILE := x68kzremotedrv-$(shell date +%Y%m%d) 24 | 25 | release: all 26 | ./md2txtconv.py README.md 27 | mv README.txt build 28 | cp QUICKSTART.pdf build 29 | cp driver/*.uf2 build 30 | cp driver/*.xdf build 31 | (cd build; zip -r ../$(RELFILE).zip *.uf2 README.txt *.pdf *.xdf) 32 | 33 | .PHONY: all clean update release 34 | -------------------------------------------------------------------------------- /config.tmpl.txt: -------------------------------------------------------------------------------- 1 | [X68000Z Remote Drive Service Configuration] 2 | 3 | # WiFi 接続先の SSID、パスワード 4 | WIFI_SSID: %s 5 | WIFI_PASSWORD: ******** 6 | 7 | # Windows ファイル共有のユーザ名、パスワード、ワークグループ名、サーバ名 8 | SMB2_USERNAME: %s 9 | SMB2_PASSWORD: ******** 10 | SMB2_WORKGROUP: %s 11 | SMB2_SERVER: %s 12 | 13 | # X68000Z に見せるHDSファイルの場所 14 | HDS0: %s 15 | HDS1: %s 16 | HDS2: %s 17 | HDS3: %s 18 | 19 | # リモートドライブからの起動を行うかどうか (0=行わない/1=行う) 20 | REMOTE_BOOT: %s 21 | # リモートドライブのユニット数 (0-4) 0ならリモートドライブは使用しない 22 | REMOTE_UNIT: %s 23 | 24 | # X68000Z に見せるリモートドライブの場所 25 | REMOTE0: %s 26 | REMOTE1: %s 27 | REMOTE2: %s 28 | REMOTE3: %s 29 | REMOTE4: %s 30 | REMOTE5: %s 31 | REMOTE6: %s 32 | REMOTE7: %s 33 | 34 | # タイムゾーン設定 35 | TZ: %s 36 | # 起動時の日時補正値 (サーバから取得した日時からのオフセット/空欄なら補正しない) 37 | TADJUST: %s 38 | # リモートドライブサービスの接続を高速化するか (0=高速化しない/1=高速化する) 39 | # 1に設定した場合はHDSファイルのイメージサイズが正しく取得できない副作用がある 40 | # 通常は問題ないがformat.xで装置初期化を行う際は0を設定しておく必要がある 41 | FASTCONNECT: %s 42 | -------------------------------------------------------------------------------- /iconv/iconv_mini.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | #ifndef _ICONV_MINI_H_ 26 | #define _ICONV_MINI_H_ 27 | 28 | #include 29 | 30 | int iconv_s2u(char **src_buf, size_t *src_len, 31 | char **dst_buf, size_t *dst_len); 32 | int iconv_u2s(char **src_buf, size_t *src_len, 33 | char **dst_buf, size_t *dst_len); 34 | 35 | #endif /* _ICONV_MINI_H_ */ 36 | -------------------------------------------------------------------------------- /include/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | #ifndef _CONFIG_H_ 26 | #define _CONFIG_H_ 27 | 28 | #define CONFIG_ALIGNED 29 | #define CONFIG_NFILEINFO 10 30 | #define CONFIG_DATASIZE (1024 * 15) 31 | 32 | /* Remote drive config */ 33 | 34 | #define CONFIG_BOOTDRIVER 35 | #define CONFIG_DEVNAME "\x01ZREMOTE" 36 | #define CONFIG_NDCACHE 4 37 | #define CONFIG_NFCACHE 4 38 | 39 | /* Remote service config */ 40 | 41 | #endif /* _CONFIG_H_ */ 42 | -------------------------------------------------------------------------------- /src/config_file.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023 Yuichi Nakamura 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 | #ifndef _CONFIG_FILE_H 27 | #define _CONFIG_FILE_H 28 | 29 | #include 30 | #include "vd_command.h" 31 | 32 | /* configuration data */ 33 | 34 | extern char configtxt[2048]; 35 | extern struct config_data config; 36 | 37 | /* configuration functions */ 38 | 39 | void config_read(void); 40 | void config_write(void); 41 | void config_erase(void); 42 | void config_parse(uint8_t *buf); 43 | 44 | #endif /* _CONFIG_FILE_H */ 45 | -------------------------------------------------------------------------------- /iconv/createtable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | from struct import unpack, pack 4 | 5 | def s2u(s): 6 | try: 7 | enc = 'B' if s < 0x100 else 'H' 8 | (d,) = unpack('>H', pack('>'+enc, s).decode('ms932').encode('utf-16be')) 9 | except: 10 | return -1 11 | return d 12 | 13 | def u2s(u): 14 | try: 15 | s = pack('>H', u).decode('utf-16be').encode('ms932') 16 | enc = 'B' if len(s) == 1 else 'H' 17 | (d,) = unpack('>'+enc, s) 18 | except: 19 | return -1 20 | return d 21 | 22 | def getindex(f): 23 | index = [] 24 | p = 0 25 | for h in range(0, 0x100): 26 | for c in range(h * 256, (h * 256) + 0x100): 27 | if f(c) >= 0: 28 | index.append(p) 29 | p += 1 30 | break 31 | else: 32 | index.append(-1) 33 | return index 34 | 35 | def dump(name): 36 | f = eval(name) 37 | print('static int8_t '+name+'_upper[] = {') 38 | 39 | index = getindex(f) 40 | for h in range(0, 0x100): 41 | if h % 16 == 0: 42 | print(' ', end='') 43 | print('{:3d}, '.format(index[h]), end='') 44 | if h % 16 == 15: 45 | print(' /* 0x{:04x} */'.format((h * 256) & 0xf000)) 46 | 47 | print('};') 48 | print() 49 | 50 | print('static uint16_t '+name+'_lower[][256] = {') 51 | for h in range(0, 0x100): 52 | if index[h] < 0: 53 | continue 54 | print(" {") 55 | for c in range(h * 256, (h * 256) + 0x100): 56 | if c % 16 == 0: 57 | print(' ', end='') 58 | x = f(c) 59 | if x < 0: 60 | print(' {:4d}, '.format(0), end='') 61 | else: 62 | print('0x{:04x}, '.format(x), end='') 63 | if c % 16 == 15: 64 | print(' /* 0x{:04x} */'.format(c & 0xfff0)) 65 | print(" },") 66 | print('};') 67 | 68 | if __name__ == '__main__': 69 | print('/* automatically created by createtable.py */') 70 | print('#include ') 71 | print() 72 | dump('s2u') 73 | dump('u2s') 74 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023 Yuichi Nakamura 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 | #ifndef _MAIN_H_ 27 | #define _MAIN_H_ 28 | 29 | #include "smb2.h" 30 | #include "libsmb2.h" 31 | #include "FreeRTOS.h" 32 | #include "task.h" 33 | #include "semphr.h" 34 | 35 | #define LOGSIZE 1024 36 | extern char log_txt[LOGSIZE]; 37 | 38 | extern TaskHandle_t main_th; 39 | extern TaskHandle_t connect_th; 40 | extern TaskHandle_t keepalive_th; 41 | extern SemaphoreHandle_t remote_sem; 42 | 43 | extern uint64_t boottime; 44 | extern volatile int sysstatus; 45 | void connect_task(void *params); 46 | void keepalive_task(void *params); 47 | 48 | struct smb2_context *connect_smb2(const char *share); 49 | void disconnect_smb2(struct smb2_context *smb2); 50 | struct smb2_context *path2smb2(const char *path, const char **shpath); 51 | struct smb2_context *connect_smb2_path(const char *path, const char **shpath); 52 | void disconnect_smb2_path(const char *path); 53 | void disconnect_smb2_all(void); 54 | void keepalive_smb2_all(void); 55 | 56 | void hds_cache_init(void); 57 | int hds_cache_read(struct smb2_context *smb2, struct smb2fh *sfh, uint32_t lba, uint8_t *buf); 58 | int hds_cache_write(struct smb2_context *smb2, struct smb2fh *sfh, uint32_t lba, uint8_t *buf); 59 | 60 | #endif /* _MAIN_H_ */ 61 | -------------------------------------------------------------------------------- /src/virtual_disk.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023 Yuichi Nakamura 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 | #ifndef _VIRTUAL_DISK_H 27 | #define _VIRTUAL_DISK_H 28 | 29 | #include 30 | 31 | /* virtual disk volume contstants */ 32 | 33 | #define SECTOR_SIZE 512 34 | #define CLUSTER_SIZE 32768 35 | 36 | #define MAX_CLUSTER 0x100000 37 | 38 | #define CLUS_PER_SECT (CLUSTER_SIZE / SECTOR_SIZE) // 64 39 | #define FATENTS_SECT (SECTOR_SIZE / sizeof(uint32_t)) // 128 40 | #define FAT_SECTORS (MAX_CLUSTER / FATENTS_SECT) // 0x2000 41 | #define VOLUME_SECTOR_COUNT (0x20 + FAT_SECTORS * 2 + (MAX_CLUSTER - 2) * CLUS_PER_SECT) 42 | // 0x4003fa0 43 | 44 | /* virtual disk function prototypes */ 45 | 46 | int vd_init(void); 47 | int vd_read_block(uint32_t lba, uint8_t *buf); 48 | int vd_write_block(uint32_t lba, uint8_t *buf); 49 | 50 | /* remote disk information */ 51 | 52 | #define DTYPE_NOTUSED 0 53 | #define DTYPE_HDS 1 54 | #define DTYPE_REMOTEBOOT 2 55 | #define DTYPE_REMOTECOMM 3 56 | 57 | struct diskinfo { 58 | int type; 59 | struct smb2fh *sfh; 60 | struct smb2_context *smb2; 61 | uint32_t size; 62 | int sects; 63 | }; 64 | 65 | extern struct diskinfo diskinfo[7]; 66 | 67 | #endif /* _VIRTUAL_DISK_H */ 68 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.24) 2 | 3 | # Get the Pico SDK from https://github.com/raspberrypi/pico-sdk.git tested on TAG 1.4.0 4 | # Get FreeRTOS from https://github.com/FreeRTOS/FreeRTOS-Kernel.git tested on TAG V10.5.0 5 | set(PICO_SDK_PATH ${CMAKE_CURRENT_LIST_DIR}/pico-sdk) 6 | set(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/FreeRTOS-Kernel) 7 | set(LIBSMB2_PATH ${CMAKE_CURRENT_LIST_DIR}/libsmb2) 8 | 9 | # Name the project and prepare it to be a pico_w project, using FreeRTOS and lwip 10 | set(PROJECT_NAME x68kzremotedrv) 11 | set(PICO_BOARD pico_w) 12 | include(${PICO_SDK_PATH}/pico_sdk_init.cmake) 13 | include(${FREERTOS_KERNEL_PATH}/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_import.cmake) 14 | project(${PROJECT_NAME} C CXX ASM) 15 | pico_sdk_init() 16 | 17 | # Add a couple of definitions that all projects/libraries can/should use 18 | add_definitions(-DPICO_PLATFORM=${PICO_PLATFORM}) 19 | add_definitions(-DHAVE_CONFIG_H) 20 | 21 | # Build libsmb2 as a library 22 | add_subdirectory(${LIBSMB2_PATH} libsmb2) 23 | 24 | # Application, including in the FreeRTOS-Kernel 25 | add_executable( 26 | ${PROJECT_NAME} 27 | src/main.c 28 | src/connect.c 29 | src/msc_disk.c 30 | src/virtual_disk.c 31 | src/vd_command.c 32 | src/hdscache.c 33 | src/smb2connect.c 34 | src/config_file.c 35 | src/usb_descriptors.c 36 | x68kserremote/service/remoteserv.c 37 | iconv/iconv_mini.c 38 | ) 39 | 40 | add_custom_target(driver make 41 | WORKING_DIRECTORY ../driver 42 | ) 43 | add_dependencies(${PROJECT_NAME} driver) 44 | 45 | # Build the app 46 | include_directories( 47 | ${CMAKE_CURRENT_LIST_DIR} 48 | ${CMAKE_CURRENT_LIST_DIR}/src 49 | ${CMAKE_CURRENT_LIST_DIR}/include 50 | ${CMAKE_CURRENT_LIST_DIR}/driver 51 | ${CMAKE_CURRENT_LIST_DIR}/iconv 52 | ${CMAKE_CURRENT_LIST_DIR}/x68kserremote/include 53 | ${CMAKE_CURRENT_LIST_DIR}/x68kserremote/service 54 | ${FREERTOS_KERNEL_PATH}/include 55 | ${LIBSMB2_PATH}/include 56 | ${LIBSMB2_PATH}/include/smb2 57 | ${LIBSMB2_PATH}/include/picow 58 | ) 59 | 60 | pico_enable_stdio_usb(${PROJECT_NAME} 0) 61 | pico_enable_stdio_uart(${PROJECT_NAME} 1) 62 | pico_enable_stdio_semihosting(${PROJECT_NAME} 0) 63 | 64 | execute_process(COMMAND git describe --tags --always 65 | OUTPUT_VARIABLE GIT_REPO_VERSION 66 | OUTPUT_STRIP_TRAILING_WHITESPACE 67 | ) 68 | add_compile_definitions(-DGIT_REPO_VERSION="${GIT_REPO_VERSION}") 69 | 70 | target_compile_definitions(${PROJECT_NAME} PRIVATE 71 | PICO_HEAP_SIZE=0x18000 72 | NO_SYS=0 # don't want NO_SYS (generally this would be in your lwipopts.h) 73 | ) 74 | 75 | target_compile_options(${PROJECT_NAME} PRIVATE -g) 76 | 77 | target_link_libraries(${PROJECT_NAME} 78 | pico_cyw43_arch_lwip_sys_freertos 79 | FreeRTOS-Kernel 80 | pico_stdlib 81 | libsmb2 82 | FreeRTOS-Kernel-Heap3 83 | tinyusb_device 84 | tinyusb_board 85 | ) 86 | 87 | pico_add_extra_outputs(${PROJECT_NAME}) 88 | -------------------------------------------------------------------------------- /driver/settinguisub.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | #include 26 | #include 27 | 28 | #ifndef _SETTINGUISUB_H_ 29 | #define _SETTINGUISUB_H_ 30 | 31 | //**************************************************************************** 32 | // Definition 33 | //**************************************************************************** 34 | 35 | #define min(a, b) (((a) < (b)) ? (a) : (b)) 36 | #define max(a, b) (((a) > (b)) ? (a) : (b)) 37 | 38 | struct itemtbl { 39 | int stat; 40 | int x; 41 | int y; 42 | int xn; 43 | const char *msg; 44 | const char *help1; 45 | const char *help2; 46 | const char *help3; 47 | int xd; 48 | int wd; 49 | char *value; 50 | int valuesz; 51 | int (*func)(struct itemtbl *, void *v); 52 | void *opt; 53 | }; 54 | 55 | struct numlist_opt { 56 | int min; 57 | int max; 58 | }; 59 | 60 | //**************************************************************************** 61 | // Function prototype 62 | //**************************************************************************** 63 | 64 | /* Communication */ 65 | void com_init(void); 66 | void com_cmdres(void *wbuf, size_t wsize, void *rbuf, size_t rsize); 67 | 68 | /* Drawing */ 69 | void drawframe(int x, int y, int w, int h, int c, int h2); 70 | void drawframe2(int x, int y, int w, int h, int c, int h2); 71 | void drawframe3(int x, int y, int w, int h, int c, int h2); 72 | void drawhline(int x, int y, int w, int c); 73 | void drawmsg(int x, int y, int c, const char *msg); 74 | void drawvalue(int c, struct itemtbl *it, const char *s, int mask); 75 | void drawhelp(int c, int x, int y, int w, const char *s); 76 | 77 | /* Input */ 78 | int keyinp(int timeout); 79 | int input_entry(struct itemtbl *it, void *v); 80 | int input_passwd(struct itemtbl *it, void *v); 81 | int input_numlist(struct itemtbl *it, void *v); 82 | int input_wifiap(struct itemtbl *it, void *v); 83 | int input_dirfile(struct itemtbl *it, void *v); 84 | 85 | int topview(void); 86 | 87 | #endif /* _SETTINGUISUB_H_ */ 88 | -------------------------------------------------------------------------------- /src/diskmap.txt: -------------------------------------------------------------------------------- 1 | [BPB] 2 | JmpBoot 3 3 | OEMName 8 4 | BytePerSec 2 0x0200 512bytes/sector 5 | SecPerClus 1 0x40 32768/512=64 6 | RsvdSecCnt 2 0x20 32sectors (0x4000) 7 | NumFATs 1 0x02 8 | RootEntCnt 2 0x0000 9 | TotSec16 2 0x0000 10 | Media 1 0xf8 11 | FATSz16 2 0x0000 12 | SecPerTrk 2 0x003f 13 | NumHeads 2 0x00ff 14 | HiddSec 4 0x00000000 15 | TotSec32 4 0x04003fa0 (32GB disk) 16 | FATSz32 4 0x00002000 8192*(512/4)*32kB = 32GB 17 | ExtFlags 2 0x0000 18 | FSVer 2 0x0000 19 | RootClus 4 0x00000002 20 | FSInfo 2 0x0001 21 | BkBootSec 2 0x0006 22 | Reserved 12 23 | DrvNum 1 0x80 24 | Reserved 1 0x00 25 | BootSig 1 0x29 26 | VolID 4 0xXXXXXXXX 27 | VolLab 11 "NO NAME " 28 | FilSysType 8 "FAT32 " 29 | BootCode32 420 30 | BootSign 2 0xaa55 31 | 32 | 33 | [DISK] 34 | off sect clus 35 | 0x000000000 0x0000000 BPB 36 | 0x000000200 0x0000001 FSINFO 37 | 0x000000c00 0x0000006 BPB2 38 | 0x000000e00 0x0000007 FSINFO2 39 | 40 | (Reserved) 41 | 0x000004000 0x0000020 FAT1 (for cluster# 2~) 42 | 0x000084000 0x0000420 (for cluster# 0x020000~) 43 | 0x000104000 0x0000820 (for cluster# 0x040000~) 44 | 0x000184000 0x0000c20 (for cluster# 0x060000~) 45 | 0x000204000 0x0001020 (for cluster# 0x080000~) 46 | 0x000284000 0x0001420 (for cluster# 0x0a0000~) 47 | 0x000304000 0x0001820 (for cluster# 0x0c0000~) 48 | 0x000384000 0x0001c20 (for cluster# 0x0e0000~) 49 | 0x000404000 0x0002020 FAT2 50 | : 51 | (0x007f4000 0x0003fa0 0x000000) 52 | 0x000804000 0x0004020 0x000002 RootDir 53 | 0x00080c000 0x0004060 0x000003 "X68000Z" subdir 54 | 0x000814000 0x00040a0 0x000004 pscsi.ini 55 | 0x00081c000 0x00040e0 0x000005 log.txt 56 | 0x000824000 0x0004120 0x000006 config.txt 57 | (update時: write 0x4020~0x4027 -> 0x4120) 58 | 0x00082c000 0x0004160 0x000007 "X68000Z/image" subdir 59 | 60 | 0x1007f4000 0x0803fa0 0x020000 image-0 61 | 0x2007f4000 0x1003fa0 0x040000 image-1 62 | 0x3007f4000 0x1803fa0 0x060000 image-2 63 | 0x4007f4000 0x2003fa0 0x080000 image-3 64 | 0x5007f4000 0x2803fa0 0x0a0000 image-4 65 | 0x6007f4000 0x3003fa0 0x0c0000 image-5 66 | 0x7007f4000 0x3803fa0 0x0e0000 image-6 67 | 0x8007f4000 0x4003fa0 0x100000 max 68 | 69 | 70 | 4GB file = 131072 clusters (0x20000) = 0x800000 sectors 71 | FAT 1sector = 128 entries (4MB分) 72 | pscsi.ini 0x00000- 73 | image-0 0x20000 cluster (1024sector) 74 | image-1 0x40000 75 | image-2 0x60000 76 | image-3 0x80000 77 | image-4 0xa0000 78 | image-5 0xc0000 79 | image-6 0xe0000 80 | max 0x100000 約32GB分のHDD image 81 | 82 | [Remote image] 83 | 0x000000000 0x0000000 signature 84 | 0x000000400 0x0000002 boot loader 85 | 0x000000800 0x0000004 partition table 86 | 0x000000c00 0x0000006 SCSI device driver 87 | 0x000004000 0x0000020 remote command area 88 | 0x000008000 0x0000040 HUMAN.SYS 89 | 0x000020000 0x0000100 settingui 90 | 0x000400000 0x0200000 remote response area 91 | 0x000800000 0x0400000 92 | -------------------------------------------------------------------------------- /driver/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | # 4 | # The MIT License (MIT) 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 | CROSS = m68k-xelf- 25 | CC = $(CROSS)gcc 26 | AS = $(CROSS)gcc 27 | LD = $(CROSS)gcc 28 | AR = $(CROSS)ar 29 | RANLIB = $(CROSS)ranlib 30 | OBJCOPY = $(CROSS)objcopy 31 | 32 | GIT_REPO_VERSION=$(shell git describe --tags --always) 33 | 34 | RMTINC = ../x68kserremote/include 35 | RMTSRC = ../x68kserremote/driver 36 | 37 | vpath %.h $(RMTINC):$(RMTSRC) 38 | vpath %.c $(RMTSRC) 39 | vpath %.S $(RMTSRC) 40 | 41 | CFLAGS = -g -m68000 42 | CFLAGS += -I. -I../include 43 | CFLAGS += -I $(RMTINC) 44 | CFLAGS += -I $(RMTSRC) 45 | CFLAGS += -Os -DGIT_REPO_VERSION=\"$(GIT_REPO_VERSION)\" 46 | CFLAGS += -finput-charset=utf-8 -fexec-charset=cp932 47 | CFLAGS += $(CFLAGS_XTEST) 48 | ASFLAGS = -m68000 -I. -I../include 49 | 50 | ifneq ($(DEBUG),) 51 | CFLAGS += -DDEBUG 52 | endif 53 | 54 | all: scsiremote.inc bootloader.inc hdsboot.inc settingui.inc clrconfig.uf2 zrmtrescue.xdf 55 | 56 | uitest: 57 | $(MAKE) CFLAGS_XTEST=-DXTEST clean settingui.x 58 | 59 | scsiremote.bin: scsiremote.sys 60 | ./fixupsys.py $< $@ 61 | 62 | scsiremote.sys: head.o remotedrv.o scsiremote.o 63 | $(LD) -o $@ $^ -nostartfiles -s 64 | 65 | zrmtrescue.xdf: rescueboot.bin settingui.bin 66 | ./createrescue.py $^ $@ 67 | 68 | head.o: ../include/config.h 69 | remotedrv.o: ../include/config.h remotedrv.h x68kremote.h 70 | scsiremote.o: ../include/config.h remotedrv.h x68kremote.h ../include/vd_command.h 71 | settingui.o: ../include/config.h ../include/vd_command.h settinguipat.h settinguisub.h 72 | settinguisub.o: ../include/config.h ../include/vd_command.h settinguipat.h settinguisub.h 73 | 74 | bootloader.bin: bootloader.o 75 | $(OBJCOPY) -O binary $< $@ 76 | 77 | hdsboot.bin: hdsboot.o 78 | $(OBJCOPY) -O binary $< $@ 79 | 80 | rescueboot.bin: rescueboot.o 81 | $(OBJCOPY) -O binary $< $@ 82 | 83 | settingui.bin: settingui.o settinguisub.o 84 | $(LD) -o $@ $^ -Ttext=0x6800 -s -specs=x68knodos.specs 85 | 86 | settingui.x: settingui.o settinguisub.o 87 | $(LD) -o $@ $^ 88 | 89 | clrconfig.uf2: clrconfig.py 90 | ./clrconfig.py $@ 91 | 92 | %.o: %.c 93 | $(CC) $(CFLAGS) -c -o $@ $< 94 | 95 | %.o: %.S 96 | $(AS) $(ASFLAGS) -c -o $@ $< 97 | 98 | %.inc: %.bin 99 | ./bindump.py $< $(basename $@) > $@ 100 | 101 | clean: 102 | -rm -f *.o *.x *.elf* *.sys *.bin *.inc *.uf2 *.xdf 103 | 104 | .PHONY: all clean uitest 105 | -------------------------------------------------------------------------------- /src/tusb_config.h: -------------------------------------------------------------------------------- 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 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //-------------------------------------------------------------------- 34 | // COMMON CONFIGURATION 35 | //-------------------------------------------------------------------- 36 | 37 | // defined by board.mk 38 | #ifndef CFG_TUSB_MCU 39 | #error CFG_TUSB_MCU must be defined 40 | #endif 41 | 42 | // RHPort number used for device can be defined by board.mk, default to port 0 43 | #ifndef BOARD_DEVICE_RHPORT_NUM 44 | #define BOARD_DEVICE_RHPORT_NUM 0 45 | #endif 46 | 47 | // RHPort max operational speed can defined by board.mk 48 | #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED 49 | 50 | // Device mode with rhport and speed defined by board.mk 51 | #if BOARD_DEVICE_RHPORT_NUM == 0 52 | #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) 53 | #elif BOARD_DEVICE_RHPORT_NUM == 1 54 | #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) 55 | #else 56 | #error "Incorrect RHPort configuration" 57 | #endif 58 | 59 | // can be defined by compiler in DEBUG build 60 | #ifndef CFG_TUSB_DEBUG 61 | #define CFG_TUSB_DEBUG 0 62 | #endif 63 | 64 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 65 | * Tinyusb use follows macros to declare transferring memory so that they can be put 66 | * into those specific section. 67 | * e.g 68 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 69 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 70 | */ 71 | #ifndef CFG_TUSB_MEM_SECTION 72 | #define CFG_TUSB_MEM_SECTION 73 | #endif 74 | 75 | #ifndef CFG_TUSB_MEM_ALIGN 76 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 77 | #endif 78 | 79 | //-------------------------------------------------------------------- 80 | // DEVICE CONFIGURATION 81 | //-------------------------------------------------------------------- 82 | 83 | #ifndef CFG_TUD_ENDPOINT0_SIZE 84 | #define CFG_TUD_ENDPOINT0_SIZE 64 85 | #endif 86 | 87 | //------------- CLASS -------------// 88 | #define CFG_TUD_CDC 0 89 | #define CFG_TUD_MSC 1 90 | #define CFG_TUD_HID 0 91 | #define CFG_TUD_MIDI 0 92 | #define CFG_TUD_VENDOR 0 93 | 94 | // MSC Buffer size of Device Mass storage 95 | #define CFG_TUD_MSC_EP_BUFSIZE 512 96 | 97 | #ifdef __cplusplus 98 | } 99 | #endif 100 | 101 | #endif /* _TUSB_CONFIG_H_ */ 102 | -------------------------------------------------------------------------------- /driver/bootloader.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | .text 26 | 27 | /* HUMAN.SYS simple bootloader */ 28 | 29 | entry: 30 | bra.w start 31 | 32 | sysstat: 33 | .word 0 /* system status */ 34 | 35 | start: 36 | move.w %pc@(sysstat),%d0 37 | cmpi.w #5,%d0 /* STAT_CONFIGURED */ 38 | blt loadsetting /* not configured -> setting UI */ 39 | 40 | moveq.l #0x04,%d0 /* B_BITSNS */ 41 | moveq.l #0x0c,%d1 42 | trap #15 43 | andi.b #0x08,%d0 /* check F1 key */ 44 | bne loadsetting 45 | 46 | loadhuman: 47 | move.l #(0x8000/512),%d2 /* HUMAN.SYS */ 48 | bra 1f 49 | 50 | loadsetting: 51 | move.l #(0x20000/512),%d2 /* Setting UI menu */ 52 | moveq.l #6,%d4 /* SCSI ID 6 */ 53 | 54 | 1: 55 | moveq.l #1,%d3 56 | moveq.l #1,%d5 57 | movea.w #0x67c0.w,%a1 58 | move.l #0xf5,%d0 /* SCSIDRV */ 59 | moveq.l #0x26,%d1 /* S_READEXT */ 60 | trap #15 /* Read the first sector */ 61 | 62 | cmpi.w #0x4855,0x67c0.w 63 | bne error 64 | 65 | move.l 0x67cc.w,%d3 /* text size */ 66 | add.l 0x67d0.w,%d3 /* + data size */ 67 | add.l #(0x40+511),%d3 /* round up to the sector size */ 68 | lsr.l #8,%d3 69 | lsr.l #1,%d3 /* # of sectors */ 70 | movea.w #0x67c0.w,%a1 71 | move.l #0xf5,%d0 /* SCSIDRV */ 72 | moveq.l #0x26,%d1 /* S_READEXT */ 73 | trap #15 /* Read HUMAN.SYS */ 74 | 75 | movea.l 0x67c4.w,%a0 /* start address (0x6800) */ 76 | adda.l 0x67cc.w,%a0 /* + text size */ 77 | adda.l 0x67d0.w,%a0 /* + data size -> bss address */ 78 | move.l 0x67d4.w,%d0 /* bss size */ 79 | addq.l #3,%d0 80 | lsr.l #2,%d0 /* long word count */ 81 | bra 3f 82 | 2: 83 | clr.l %a0@+ /* clear bss */ 84 | 3: 85 | dbra %d0,2b 86 | 87 | movea.l 0x67c8.w,%a0 /* entry address */ 88 | jmp %a0@ /* HUMAN.SYS entry */ 89 | 90 | error: 91 | lea.l %pc@(errormsg),%a1 92 | moveq.l #0x21,%d0 93 | trap #15 94 | 4: 95 | bra 4b 96 | 97 | errormsg: 98 | .byte 0x1b 99 | .ascii "[47m" 100 | .byte 0x1b 101 | .ascii "[13;26H " 102 | .byte 0x1b 103 | .ascii "[14;26H " 104 | .byte 0x1b 105 | .ascii "[14;35HHuman.sys " 106 | .byte 0x82,0xaa,0x20 107 | .byte 0x8c,0xa9,0x82,0xc2,0x82,0xa9,0x82,0xe8,0x82,0xdc,0x82,0xb9,0x82,0xf1 108 | .byte 0 109 | 110 | .end 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # x68kzremotedrv - X68000Z Remote Drive service with Raspberry Pi Pico W 2 | 3 | ## 概要 4 | 5 | [X68000Z](https://www.zuiki.co.jp/x68000z/) の ver.1.3.1 エミュレータでサポートされた Pseudo SCSI 機能を用いて、ネットワーク上の Windows PC 内のファイルを SCSI ディスクイメージやリモートドライブとして利用します。 6 | 7 | X68000Z にはネットワーク機能がありませんが、無線 LAN を搭載したボードコンピュータ [Raspberry Pi Pico W](https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html) がネットワーク共有されたファイルを USB メモリ上の HDS ファイルに見せかけることで、X68000Z から間接的にネットワーク上のリソースを利用できるようにしています。 8 | 9 | * SCSI ディスクイメージファイル (HDSファイル)\ 10 | Windows PC 内にある HDS ファイルを SCSI ディスクイメージとして X68000Z から参照でき、ここから起動することもできます。 11 | 既存の X68k エミュレータで使用している HDS ファイルを PC 上に置いたまま利用することができます。 12 | * リモートドライブ\ 13 | Windows PC 内の特定のフォルダ以下のファイルをリモートドライブとして X68000Z から参照できるようにします。 14 | PC 上のファイルを直接 X68000Z から利用できます。HUMAN.SYS や CONFIG.SYS など、起動に必要なファイルがあればリモートドライブからの起動も可能です。 15 | 16 | 更に、X68000Z の起動時に時刻を Windows PC の時刻に合わせる機能を追加しました。 17 | 18 | (従来 x68kzrmthds として公開していたアプリを、リモートドライブ対応に伴い名称を変更しました) 19 | 20 | ## 必要なもの 21 | 22 | X68000Z 本体に加えて以下が必要です。 23 | 24 | * Raspberry Pi Pico W (以下、ラズパイ Pico W) 25 | * ラズパイ Pico にはいくつか製品バリエーションがありますが、W が付かないものは無線 LAN 機能を持たないため使用できません 26 | * Pico W (ヘッダピンなし)、Pico WH (ヘッダピン付き) のどちらでも使用できます 27 | * USB micro-B ケーブル 28 | * ラズパイ Pico W を X68000Z に接続したり、ファームウェア書き込みのため PC に接続したりするために必要です 29 | 30 | ## 使用方法 31 | 32 | * ラズパイ Pico W のファームウェア書き込み後、設定はすべて X68000Z の画面上で行えるようになりました。 33 | [クイックスタートガイド](QUICKSTART.md) を参照してください。 34 | * HDS ファイルやリモートドライブの設定によって、SCSI IDは以下のように割り当てられます。 35 | * リモートドライブからの起動を行わない場合 (`RMTBOOT=0`) 36 | * ID 0 : HDS0 に設定した HDS ファイル (起動時の A ドライブになる) 37 | * ID 1 : HDS1 に設定した HDS ファイル (起動時の B ドライブになる) 38 | * ... (最大で ID 3 まで使用される) 39 | * ID 6 : (リモートドライブとの通信用に使用) 40 | * ID 7 : (本体ID) 41 | * リモートドライブからの起動を行う場合 (`RMTBOOT=1`) 42 | * ID 0 : (リモートドライブの起動用に使用) 43 | * リモートドライブの台数によって、起動時の A ~ D ドライブまでが割り当てられます 44 | * ID 1 : HDS0 に設定した HDS ファイル (リモートドライブの最後のドライブ名の次) 45 | * ID 2 : HDS1 に設定した HDS ファイル (HDS0 のドライブ名の次) 46 | * ... (最大で ID 4 まで使用される) 47 | * ID 6 : (リモートドライブとの通信用に使用) 48 | * ID 7 : (本体ID) 49 | 50 | ## 注意と制約事項 51 | 52 | * ネットワーク接続のパスワード情報などはラズパイ Pico W の中に平文で記録されます。接続情報を設定した Pico W の管理にはご注意ください。 53 | * 設定画面で `設定クリア` を選択することで、ラズパイ Pico W 内の設定データをクリアできます。ラズパイ Pico W を別の用途に使用する際などに利用してください。 54 | * ラズパイ Pico W の USB 機能がフルスピード (12Mbps) までと遅いため、通常の USB メモリからの起動に比べるともっさりします(FD イメージと比べれば十分速いですが…)。 55 | * リモートドライブには現状、以下の制約事項があります。 56 | * リモートドライブ上ではファイルアトリビュートの隠しファイルやシステム属性、書き込み禁止属性などは無視されます 57 | * Human68k の DSKFRE が 2GB 以上のディスクサイズを想定していないため、ドライブの残容量表示は不正確です 58 | 59 | ## ビルド方法 60 | 61 | ソースコードからのビルドを行う際には、事前に Raspberry Pi Pico SDK のセットアップが必要です。 62 | 63 | 1. Pico SDK をセットアップした PC に本リポジトリを clone します。 64 | 2. `make` を実行すると、追加で必要なリポジトリを clone してビルドを開始します。 65 | 3. ビルドが完了すると生成されるファイル、`build/x68kremotedrv.uf2` がラズパイ Pico W へ書き込むファイルとなります。 66 | 67 | ## 謝辞 68 | 69 | Human68k のリモートドライブの実装は以下を参考にしています。開発者の皆様に感謝します。 70 | 71 | * [ぷにぐらま~ずまにゅある](https://github.com/kg68k/puni) by 立花@桑島技研 氏 72 | * [filesystem.txt](https://github.com/kg68k/puni/blob/main/filesystem.txt) 73 | * [XEiJ (X68000 Emulator in Java)](https://stdkmd.net/xeij/) by Makoto Kamada 氏 74 | * ソースコード [HFS.java](https://stdkmd.net/xeij/source/HFS.htm) 75 | * [XM6 TypeG](http://retropc.net/pi/xm6/index.html) by PI. 氏 & GIMONS 氏 76 | * XM6 version 2.06 ソースコード 77 | 78 | リモートドライブ起動機能は、X68k エミュレータ XEiJ のホストファイルシステム機能 (HFS) に大きく触発されて開発されました。 79 | ホストマシンのファイルシステムからの直接起動という素晴らしい機能を実装された Makoto Kamada 氏に感謝します。 80 | 81 | Windows ファイル共有のアクセスについては、ライブラリ libsmb2 の存在に大きく助けられました。 82 | 当初ラズパイ Zero W を用いて Linux ベースでの開発を検討していましたが、起動速度や消費電力の点で現実的でないことが分かり、ターゲットをラズパイ Pico W に変更して FreeRTOS ベースでの開発に切り替えました。これが可能だったのも libsmb2 の存在あってのことでした。開発者の Ronnie Sahlberg 氏に感謝します。 83 | 84 | ## ライセンス 85 | 86 | 本プログラムは、オリジナルで開発したソースコードについては MIT ライセンスとします。その他利用されている以下のソフトウェアについてはそれぞれ開発元のライセンス条件に従います。 87 | 88 | * Pico SDK (3-clause BSD) 89 | * TinyUSB (MIT) 90 | * lwIP (3-clause BSD) 91 | * FreeRTOS kernel (MIT) 92 | * libsmb2 (LGPL-2.1) 93 | 94 | 本プログラムは LGPL を採用している libsmb2 を静的リンクしているため、ビルド生成物のバイナリ配布の際には LGPL が適用されます。独自の修正を加えてビルドしたバイナリを配布する際にはソースコードの開示が必要となります。 95 | -------------------------------------------------------------------------------- /src/hdscache.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023 Yuichi Nakamura 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 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "smb2.h" 34 | #include "libsmb2.h" 35 | 36 | #include "main.h" 37 | #include "virtual_disk.h" 38 | 39 | //**************************************************************************** 40 | // Static variables 41 | //**************************************************************************** 42 | 43 | #define DISK_CACHE_SECTS 8 44 | #define DISK_CACHE_SIZE (DISK_CACHE_SECTS * SECTOR_SIZE) 45 | #define DISK_CACHE_SETS 4 46 | 47 | static struct cache { 48 | uint8_t data[DISK_CACHE_SIZE]; 49 | struct smb2_context *smb2; 50 | struct smb2fh *sfh; 51 | uint32_t lba; 52 | size_t sects; 53 | } cache[DISK_CACHE_SETS]; 54 | static int cache_next = 0; 55 | 56 | //**************************************************************************** 57 | // HDS Disk cache 58 | //**************************************************************************** 59 | 60 | void hds_cache_init(void) 61 | { 62 | for (int i = 0; i < DISK_CACHE_SETS; i++) { 63 | cache[i].sfh = NULL; 64 | cache[i].lba = 0xffffffff; 65 | cache[i].sects = 0; 66 | } 67 | } 68 | 69 | int hds_cache_read(struct smb2_context *smb2, struct smb2fh *sfh, uint32_t lba, uint8_t *buf) 70 | { 71 | for (int i = 0; i < DISK_CACHE_SETS; i++) { 72 | struct cache *c = &cache[i]; 73 | if (c->sfh == sfh && c->smb2 == smb2 && lba >= c->lba && lba < c->lba + c->sects) { 74 | memcpy(buf, &c->data[(lba - c->lba) * SECTOR_SIZE], SECTOR_SIZE); 75 | return 0; 76 | } 77 | } 78 | 79 | struct cache *c = &cache[cache_next]; 80 | uint64_t cur; 81 | if (smb2_lseek(smb2, sfh, lba * SECTOR_SIZE, SEEK_SET, &cur) < 0) 82 | return -1; 83 | c->sects = 0; 84 | int sz = smb2_read(smb2, sfh, c->data, DISK_CACHE_SIZE); 85 | if (sz < 0) 86 | return -1; 87 | c->sfh = sfh; 88 | c->smb2 = smb2; 89 | c->lba = lba; 90 | c->sects = sz / SECTOR_SIZE; 91 | cache_next =(cache_next + 1) % DISK_CACHE_SETS; 92 | memcpy(buf, c->data, SECTOR_SIZE); 93 | return 0; 94 | } 95 | 96 | int hds_cache_write(struct smb2_context *smb2, struct smb2fh *sfh, uint32_t lba, uint8_t *buf) 97 | { 98 | for (int i = 0; i < DISK_CACHE_SETS; i++) { 99 | struct cache *c = &cache[i]; 100 | if (c->sfh == sfh && c->smb2 == smb2 && lba >= c->lba && lba < c->lba + c->sects) { 101 | memcpy(&c->data[(lba - c->lba) * SECTOR_SIZE], buf, SECTOR_SIZE); 102 | break; 103 | } 104 | } 105 | 106 | uint64_t cur; 107 | if (smb2_lseek(smb2, sfh, lba * SECTOR_SIZE, SEEK_SET, &cur) < 0) 108 | return -1; 109 | int sz = smb2_write(smb2, sfh, buf, SECTOR_SIZE); 110 | if (sz < 0) 111 | return -1; 112 | return 0; 113 | } 114 | -------------------------------------------------------------------------------- /iconv/iconv_mini.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | #include 26 | #include 27 | #include "iconv_table.h" 28 | 29 | int iconv_s2u(char **src_buf, size_t *src_len, 30 | char **dst_buf, size_t *dst_len) 31 | { 32 | while ((*src_len) > 0) { 33 | uint16_t s; 34 | uint8_t c = *(*src_buf)++; 35 | (*src_len)--; 36 | if ((c >= 0x80 && c <= 0x9f) || (c >= 0xe0 && c <= 0xff)) { 37 | if ((*src_len) <= 0) 38 | return -1; 39 | uint8_t c2 = *(*src_buf)++; 40 | (*src_len)--; 41 | s = c << 8 | c2; 42 | } else { 43 | s = c; 44 | } 45 | 46 | if (s2u_upper[s >> 8] < 0) 47 | return -1; 48 | uint16_t u = s2u_lower[s2u_upper[s >> 8]][s & 0xff]; 49 | if (s != 0 && u == 0) 50 | return -1; 51 | 52 | if (u <= 0x7f) { 53 | if ((*dst_len) < 1) 54 | return -1; 55 | *(*dst_buf)++ = u; 56 | (*dst_len)--; 57 | } else if (u <= 0x7ff) { 58 | if ((*dst_len) < 2) 59 | return -1; 60 | *(*dst_buf)++ = ((u >> 6) & 0x1f) | 0xc0; 61 | *(*dst_buf)++ = (u & 0x3f) | 0x80; 62 | (*dst_len) -= 2; 63 | } else { 64 | if ((*dst_len) < 3) 65 | return -1; 66 | *(*dst_buf)++ = ((u >> 12) & 0x0f) | 0xe0; 67 | *(*dst_buf)++ = ((u >> 6) & 0x3f) | 0x80; 68 | *(*dst_buf)++ = (u & 0x3f) | 0x80; 69 | (*dst_len) -= 3; 70 | } 71 | } 72 | return 0; 73 | } 74 | 75 | int iconv_u2s(char **src_buf, size_t *src_len, 76 | char **dst_buf, size_t *dst_len) 77 | { 78 | while ((*src_len) > 0) { 79 | uint16_t u; 80 | uint8_t c = *(*src_buf)++; 81 | (*src_len)--; 82 | if (c < 0x80) { 83 | u = c; 84 | } else if (c >= 0xc2 && c < 0xe0) { 85 | if ((*src_len) < 1) 86 | return -1; 87 | uint8_t c2 = *(*src_buf)++; 88 | (*src_len)--; 89 | if (c2 >= 0x80 && c2 < 0xc0) { 90 | u = ((c & 0x1f) << 6) | (c2 & 0x3f); 91 | } else { 92 | return -1; 93 | } 94 | } else if (c >= 0xe0 && c < 0xf0) { 95 | if ((*src_len) < 2) 96 | return -1; 97 | uint8_t c2 = *(*src_buf)++; 98 | uint8_t c3 = *(*src_buf)++; 99 | (*src_len) -= 2; 100 | if ((c2 >= 0x80 && c2 < 0xc0) && (c3 >= 0x80 && c3 < 0xc0)) { 101 | u = ((c & 0x0f) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f); 102 | } else { 103 | return -1; 104 | } 105 | } else { 106 | return -1; 107 | } 108 | 109 | if (u2s_upper[u >> 8] < 0) 110 | return -1; 111 | uint16_t s = u2s_lower[u2s_upper[u >> 8]][u & 0xff]; 112 | 113 | if (u != 0 && s == 0) 114 | return -1; 115 | if (s < 0x100) { 116 | if ((*dst_len) < 1) 117 | return -1; 118 | *(*dst_buf)++ = s; 119 | (*dst_len)--; 120 | } else { 121 | if ((*dst_len) < 2) 122 | return -1; 123 | *(*dst_buf)++ = s >> 8; 124 | *(*dst_buf)++ = s & 0xff; 125 | (*dst_len) -= 2; 126 | } 127 | } 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /driver/hdsboot.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | .text 26 | 27 | /* HDS boot 1st bootloader */ 28 | 29 | entry: 30 | bra.w start 31 | .space 4*15 32 | 33 | start: 34 | moveq.l #0x04,%d0 /* B_BITSNS */ 35 | moveq.l #0x0c,%d1 36 | trap #15 37 | andi.b #0x08,%d0 /* check F1 key */ 38 | bne loadsetting 39 | 40 | load1stboot: 41 | lea.l 0x2400.w,%a0 42 | lea.l %pc@(load1stboot_main),%a1 43 | lea.l %pc@(load1stboot_end),%a2 44 | 0: 45 | move.w %a1@+,%a0@+ 46 | cmpa.l %a1,%a2 47 | bne 0b 48 | jmp 0x2400.w 49 | 50 | load1stboot_main: 51 | moveq.l #(0x4000/512),%d2 /* original SCSI HDD 1st boot */ 52 | moveq.l #1,%d3 53 | moveq.l #1,%d5 54 | movea.w #0x2000.w,%a1 55 | move.l #0xf5,%d0 /* SCSIDRV */ 56 | moveq.l #0x26,%d1 /* S_READEXT */ 57 | trap #15 /* Read the first sector */ 58 | jmp 0x2000.w 59 | load1stboot_end: 60 | 61 | 62 | loadsetting: 63 | move.l #(0x20000/512),%d2 /* Setting UI menu */ 64 | 65 | 1: 66 | moveq.l #6,%d4 /* SCSI ID 6 */ 67 | 68 | moveq.l #1,%d3 69 | moveq.l #1,%d5 70 | movea.w #0x67c0.w,%a1 71 | move.l #0xf5,%d0 /* SCSIDRV */ 72 | moveq.l #0x26,%d1 /* S_READEXT */ 73 | trap #15 /* Read the first sector */ 74 | 75 | cmpi.w #0x4855,0x67c0.w 76 | bne error 77 | 78 | move.l 0x67cc.w,%d3 /* text size */ 79 | add.l 0x67d0.w,%d3 /* + data size */ 80 | add.l #(0x40+511),%d3 /* round up to the sector size */ 81 | lsr.l #8,%d3 82 | lsr.l #1,%d3 /* # of sectors */ 83 | movea.w #0x67c0.w,%a1 84 | move.l #0xf5,%d0 /* SCSIDRV */ 85 | moveq.l #0x26,%d1 /* S_READEXT */ 86 | trap #15 /* Read HUMAN.SYS */ 87 | 88 | movea.l 0x67c4.w,%a0 /* start address (0x6800) */ 89 | adda.l 0x67cc.w,%a0 /* + text size */ 90 | adda.l 0x67d0.w,%a0 /* + data size -> bss address */ 91 | move.l 0x67d4.w,%d0 /* bss size */ 92 | addq.l #3,%d0 93 | lsr.l #2,%d0 /* long word count */ 94 | bra 3f 95 | 2: 96 | clr.l %a0@+ /* clear bss */ 97 | 3: 98 | dbra %d0,2b 99 | 100 | movea.l 0x67c8.w,%a0 /* entry address */ 101 | jmp %a0@ /* HUMAN.SYS entry */ 102 | 103 | error: 104 | lea.l %pc@(errormsg),%a1 105 | moveq.l #0x21,%d0 106 | trap #15 107 | 4: 108 | bra 4b 109 | 110 | errormsg: 111 | .byte 0x1b 112 | .ascii "[47m" 113 | .byte 0x1b 114 | .ascii "[13;26H " 115 | .byte 0x1b 116 | .ascii "[14;26H " 117 | .byte 0x1b 118 | .ascii "[14;35HHuman.sys " 119 | .byte 0x82,0xaa,0x20 120 | .byte 0x8c,0xa9,0x82,0xc2,0x82,0xa9,0x82,0xe8,0x82,0xdc,0x82,0xb9,0x82,0xf1 121 | .byte 0 122 | 123 | .end 124 | -------------------------------------------------------------------------------- /driver/rescueboot.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | .text 26 | 27 | /* FD boot 1st bootloader for rescue disk */ 28 | 29 | entry: 30 | bra.w start 31 | .space 4*15 32 | 33 | start: 34 | lea.l 0xe8e000,%a0 35 | move.b #'Z',%a0@ 36 | cmp.b #'X',%a0@ 37 | bne error_noz 38 | 39 | lea.l 0xea0020,%a0 40 | movea.l %a0@,%a0 41 | movea.l %a0@(-12),%a1 42 | move.l #0x80,%d0 /* B_INTVCS */ 43 | move.l #0x1f5,%d1 /* 0x100 + _SCSIDRV */ 44 | trap #15 45 | move.l #0xf5,d0 /* SCSIDRV */ 46 | moveq.l #0,%d1 /* S_RESET */ 47 | trap #15 48 | 49 | move.l #0x8e,%d0 /* BOOTINF */ 50 | trap #15 51 | lsl.w #8,%d0 52 | moveq.l #0x70,%d7 53 | or.w %d0,%d7 54 | 55 | move.l %d7,%d1 56 | move.l #0x03000002,%d2 /* 1024bytes/sector, track #0, side #0, sector #2 */ 57 | move.l #0x400,%d3 58 | movea.w #0x67c0.w,%a1 59 | moveq.l #0x46,%d0 /* B_READ */ 60 | trap #15 /* Read the first sector */ 61 | 62 | cmpi.w #0x4855,0x67c0.w 63 | bne error 64 | 65 | move.l 0x67cc.w,%d3 /* text size */ 66 | add.l 0x67d0.w,%d3 /* + data size */ 67 | add.l #0x40,%d3 /* + header size */ 68 | movea.w #0x67c0.w,%a1 69 | move.l %d7,%d1 70 | move.l #0x03000002,%d2 /* 1024bytes/sector, track #0, side #0, sector #2 */ 71 | moveq.l #0x46,%d0 /* B_READ */ 72 | trap #15 /* Read Setting UI */ 73 | 74 | movea.l 0x67c4.w,%a0 /* start address (0x6800) */ 75 | adda.l 0x67cc.w,%a0 /* + text size */ 76 | adda.l 0x67d0.w,%a0 /* + data size -> bss address */ 77 | move.l 0x67d4.w,%d0 /* bss size */ 78 | addq.l #3,%d0 79 | lsr.l #2,%d0 /* long word count */ 80 | bra 3f 81 | 2: 82 | clr.l %a0@+ /* clear bss */ 83 | 3: 84 | dbra %d0,2b 85 | 86 | movea.l 0x67c8.w,%a0 /* entry address */ 87 | jmp %a0@ /* Setting UI entry */ 88 | 89 | error: 90 | lea.l %pc@(errormsg),%a1 91 | moveq.l #0x21,%d0 92 | trap #15 93 | 4: 94 | bra 4b 95 | 96 | error_noz: 97 | lea.l %pc@(error_nozmsg),%a1 98 | moveq.l #0x21,%d0 99 | trap #15 100 | 5: 101 | bra 5b 102 | 103 | errormsg: 104 | .byte 0x1b 105 | .ascii "[47m" 106 | .byte 0x1b 107 | .ascii "[13;26H " 108 | .byte 0x1b 109 | .ascii "[14;26H " 110 | .byte 0x1b 111 | .ascii "[14;35Hsettingui " 112 | .byte 0x82,0xaa,0x20 113 | .byte 0x8c,0xa9,0x82,0xc2,0x82,0xa9,0x82,0xe8,0x82,0xdc,0x82,0xb9,0x82,0xf1 114 | .byte 0 115 | 116 | error_nozmsg: 117 | .byte 0x1b 118 | .ascii "[47m" 119 | .byte 0x1b 120 | .ascii "[13;26H " 121 | .byte 0x1b 122 | .ascii "[14;26H " 123 | .byte 0x1b 124 | .ascii "[14;33HX68000 Z " 125 | .byte 0x88,0xC8,0x8A,0x4F,0x82,0xC5,0x82,0xCD,0x8B,0x4E,0x93,0xAE,0x82,0xC5 126 | .byte 0x82,0xAB,0x82,0xDC,0x82,0xB9,0x82,0xF1 127 | .byte 0 128 | 129 | .end 130 | -------------------------------------------------------------------------------- /iconv/createtable.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | #include 26 | #include 27 | #include 28 | 29 | /* ShiftJIS -> UTF-16 conversion */ 30 | static int s2u(int s) 31 | { 32 | uint8_t inbuf[2]; 33 | uint8_t outbuf[2]; 34 | char *src_buf = inbuf; 35 | char *dst_buf = outbuf; 36 | size_t src_len; 37 | size_t dst_len = sizeof(outbuf); 38 | 39 | if (s < 0x100) { 40 | inbuf[0] = s; 41 | src_len = 1; 42 | } else { 43 | inbuf[0] = s >> 8; 44 | inbuf[1] = s; 45 | src_len = 2; 46 | } 47 | 48 | int res; 49 | iconv_t cd = iconv_open("UTF-16BE", "CP932"); 50 | res = iconv(cd, &src_buf, &src_len, &dst_buf, &dst_len); 51 | iconv_close(cd); 52 | 53 | if (res < 0) 54 | return -1; 55 | return (outbuf[0] << 8) | outbuf[1]; 56 | } 57 | 58 | /* UTF-16 -> ShiftJIS conversion */ 59 | static int u2s(int u) 60 | { 61 | uint8_t inbuf[2]; 62 | uint8_t outbuf[2]; 63 | char *src_buf = inbuf; 64 | char *dst_buf = outbuf; 65 | size_t src_len; 66 | size_t dst_len = sizeof(outbuf); 67 | 68 | inbuf[0] = u >> 8; 69 | inbuf[1] = u; 70 | src_len = 2; 71 | 72 | int res; 73 | iconv_t cd = iconv_open("CP932", "UTF-16BE"); 74 | res = iconv(cd, &src_buf, &src_len, &dst_buf, &dst_len); 75 | iconv_close(cd); 76 | 77 | if (res < 0) 78 | return -1; 79 | if (dst_len == sizeof(outbuf) - 1) 80 | return outbuf[0]; 81 | else 82 | return (outbuf[0] << 8) | outbuf[1]; 83 | } 84 | 85 | static void create_upper(FILE *fp, int8_t *ix, int (*conv)(int)) 86 | { 87 | int p = 0; 88 | for (int i = 0; i < 0x100; i++) { 89 | int j; 90 | ix[i] = -1; 91 | for (j = 0; j < 0x100; j++) { 92 | if (conv(i * 256 + j) >= 0) 93 | break; 94 | } 95 | if (j < 0x100) 96 | ix[i] = p++; 97 | if ((i % 16) == 0) fprintf(fp, " "); 98 | fprintf(fp, "%3d, ", ix[i]); 99 | if ((i % 16) == 15) fprintf(fp, " /* 0x%02x00 */\n", i & 0xf0); 100 | } 101 | } 102 | 103 | static void create_lower(FILE *fp, int8_t *ix, int (*conv)(int)) 104 | { 105 | for (int i = 0; i < 0x100; i++) { 106 | if (ix[i] < 0) 107 | continue; 108 | fprintf(fp, " {\n"); 109 | for (int j = 0; j < 0x100; j++) { 110 | int x; 111 | if ((j % 16) == 0) fprintf(fp, " "); 112 | if ((x = conv(i * 256 + j)) >= 0) { 113 | fprintf(fp, "0x%04x, ", x); 114 | } else { 115 | fprintf(fp, " 0, "); 116 | } 117 | if ((j % 16) == 15) fprintf(fp, " /* 0x%04x */\n", i * 256 + (j & 0xf0)); 118 | } 119 | fprintf(fp, " },\n"); 120 | } 121 | } 122 | 123 | int main() 124 | { 125 | FILE *fp; 126 | int8_t ix[256]; 127 | 128 | fp = fopen("iconv_table.h", "w"); 129 | fprintf(fp, "/* automatically created by createtable.c */\n"); 130 | fprintf(fp, "#include \n\n"); 131 | 132 | /* create ShiftJIS to UTF-16 table */ 133 | 134 | fprintf(fp, "static int8_t s2u_upper[] = {\n"); 135 | create_upper(fp, ix, s2u); 136 | fprintf(fp, "};\n\n"); 137 | fprintf(fp, "static uint16_t s2u_lower[][256] = {\n"); 138 | create_lower(fp, ix, s2u); 139 | fprintf(fp, "};\n\n"); 140 | 141 | /* create UTF-16 to ShiftJIS table */ 142 | 143 | fprintf(fp, "static int8_t u2s_upper[] = {\n"); 144 | create_upper(fp, ix, u2s); 145 | fprintf(fp, "};\n\n"); 146 | fprintf(fp, "static uint16_t u2s_lower[][256] = {\n"); 147 | create_lower(fp, ix, u2s); 148 | fprintf(fp, "};\n\n"); 149 | 150 | fclose(fp); 151 | 152 | return 0; 153 | } 154 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023 Yuichi Nakamura 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 27 | 28 | #include "pico/stdio.h" 29 | #include "pico/stdlib.h" 30 | #include "pico/stdio/driver.h" 31 | #include "pico/cyw43_arch.h" 32 | #include "FreeRTOS.h" 33 | #include "task.h" 34 | #include "semphr.h" 35 | #include "bsp/board_api.h" 36 | #include "tusb.h" 37 | 38 | #include "main.h" 39 | #include "vd_command.h" 40 | #include "virtual_disk.h" 41 | #include "config_file.h" 42 | 43 | //**************************************************************************** 44 | // Global variables 45 | //**************************************************************************** 46 | 47 | char log_txt[LOGSIZE]; 48 | 49 | TaskHandle_t main_th; 50 | TaskHandle_t connect_th; 51 | TaskHandle_t keepalive_th; 52 | SemaphoreHandle_t remote_sem; 53 | 54 | //**************************************************************************** 55 | // for debug log 56 | //**************************************************************************** 57 | 58 | static char *log_txtp = log_txt; 59 | 60 | static void log_out_chars(const char *buffer, int length) 61 | { 62 | int remain = 1024 - (log_txtp - log_txt); 63 | length = length > remain ? remain : length; 64 | memcpy(log_txtp, buffer, length); 65 | log_txtp += length; 66 | } 67 | 68 | static stdio_driver_t stdio_log = { 69 | .out_chars = log_out_chars, 70 | }; 71 | 72 | static void log_out_init(void) 73 | { 74 | memset(log_txt, ' ', sizeof(log_txt)); 75 | stdio_set_driver_enabled(&stdio_log, true); 76 | } 77 | 78 | //**************************************************************************** 79 | // Main task 80 | //**************************************************************************** 81 | 82 | static void main_task(void *params) 83 | { 84 | if (cyw43_arch_init()) { 85 | printf("Failed to initialize Pico W\n"); 86 | while (1) 87 | taskYIELD(); 88 | } 89 | 90 | cyw43_arch_enable_sta_mode(); 91 | 92 | remote_sem = xSemaphoreCreateBinary(); 93 | xSemaphoreGive(remote_sem); 94 | xTaskCreate(connect_task, "ConnectThread", 2048, NULL, 1, &connect_th); 95 | xTaskCreate(keepalive_task, "KeepAliveThread", 2048, NULL, 1, &keepalive_th); 96 | 97 | vd_init(); 98 | 99 | printf("Start USB MSC device.\n"); 100 | 101 | /* USB MSC main loop */ 102 | 103 | tusb_init(); 104 | while (1) { 105 | tud_task(); 106 | taskYIELD(); 107 | } 108 | } 109 | 110 | //**************************************************************************** 111 | // MSC device callbacks 112 | //**************************************************************************** 113 | 114 | // Invoked when device is mounted 115 | void tud_mount_cb(void) 116 | { 117 | } 118 | // Invoked when device is unmounted 119 | void tud_umount_cb(void) 120 | { 121 | } 122 | 123 | // Invoked when usb bus is suspended 124 | void tud_suspend_cb(bool remote_wakeup_en) 125 | { 126 | } 127 | // Invoked when usb bus is resumed 128 | void tud_resume_cb(void) 129 | { 130 | } 131 | 132 | //**************************************************************************** 133 | // main 134 | //**************************************************************************** 135 | 136 | int main(void) 137 | { 138 | board_init(); 139 | stdio_init_all(); 140 | log_out_init(); 141 | config_read(); 142 | 143 | printf("\nX68000Z Remote Drive Service (version %s)\n", GIT_REPO_VERSION); 144 | 145 | xTaskCreate(main_task, "MainThread", 2048, NULL, 1, &main_th); 146 | vTaskStartScheduler(); 147 | 148 | return 0; 149 | } 150 | -------------------------------------------------------------------------------- /include/vd_command.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023 Yuichi Nakamura 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 | #ifndef _VD_COMMAND_H_ 27 | #define _VD_COMMAND_H_ 28 | 29 | #include 30 | 31 | /* virtual disk buffer definition */ 32 | 33 | struct vdbuf_header { 34 | uint32_t signature; // "X68Z" signature 35 | uint32_t session; // session ID 36 | uint32_t seqno; // sequence count 37 | uint8_t page; // page number 38 | uint8_t maxpage; // max page 39 | uint8_t reserved[2]; 40 | }; 41 | 42 | struct vdbuf { 43 | struct vdbuf_header header; 44 | uint8_t buf[512 - sizeof(struct vdbuf_header)]; 45 | }; 46 | 47 | int vd_command(uint8_t *cbuf, uint8_t *rbuf); 48 | 49 | /* configuration data structure */ 50 | 51 | struct config_data { 52 | char wifi_ssid[32]; 53 | char wifi_passwd[16]; 54 | 55 | char smb2_user[16]; 56 | char smb2_passwd[16]; 57 | char smb2_workgroup[16]; 58 | char smb2_server[32]; 59 | 60 | char remoteboot[4]; 61 | char remoteunit[4]; 62 | char remote[8][128]; 63 | char hds[4][128]; 64 | 65 | char tz[16]; 66 | char tadjust[4]; 67 | char fastconnect[4]; 68 | }; 69 | 70 | /* scsiremote.sys communication protocol definition */ 71 | 72 | #define PROTO_VERSION 1 73 | 74 | #define CMD_GETINFO 0xff00 75 | #define CMD_GETCONFIG 0xff01 76 | #define CMD_SETCONFIG 0xff02 77 | #define CMD_GETSTATUS 0xff03 78 | #define CMD_WIFI_SCAN 0xff04 79 | #define CMD_SMB2_ENUM 0xff05 80 | #define CMD_SMB2_LIST 0xff06 81 | #define CMD_FLASHCONFIG 0xff07 82 | #define CMD_FLASHCLEAR 0xff08 83 | #define CMD_REBOOT 0xff09 84 | 85 | #define STAT_WIFI_DISCONNECTED 0 86 | #define STAT_WIFI_CONNECTING 1 87 | #define STAT_WIFI_CONNECTED 2 88 | #define STAT_SMB2_CONNECTING 3 89 | #define STAT_SMB2_CONNECTED 4 90 | #define STAT_CONFIGURED 5 91 | 92 | #define CONNECT_WIFI 0 93 | #define CONNECT_SMB2 1 94 | #define CONNECT_NONE 2 95 | #define CONNECT_MASK 0x0f 96 | #define CONNECT_WAIT 0x10 97 | 98 | struct cmd_getinfo { 99 | uint16_t command; 100 | }; 101 | struct res_getinfo { 102 | uint16_t year; 103 | uint8_t mon; 104 | uint8_t day; 105 | uint8_t hour; 106 | uint8_t min; 107 | uint8_t sec; 108 | uint8_t unit; 109 | uint8_t version; 110 | uint8_t verstr[16]; 111 | }; 112 | 113 | struct cmd_getconfig { 114 | uint16_t command; 115 | }; 116 | struct res_getconfig { 117 | struct config_data data; 118 | }; 119 | 120 | struct cmd_setconfig { 121 | uint16_t command; 122 | uint8_t mode; 123 | struct config_data data; 124 | }; 125 | struct res_setconfig { 126 | uint8_t status; 127 | }; 128 | 129 | struct cmd_getstatus { 130 | uint16_t command; 131 | }; 132 | struct res_getstatus { 133 | uint8_t status; 134 | }; 135 | 136 | struct cmd_wifi_scan { 137 | uint16_t command; 138 | uint8_t clear; 139 | }; 140 | struct res_wifi_scan { 141 | uint8_t status; 142 | uint8_t n_items; 143 | uint8_t ssid[16][32]; 144 | }; 145 | 146 | struct cmd_smb2_enum { 147 | uint16_t command; 148 | }; 149 | struct res_smb2_enum { 150 | uint8_t status; 151 | uint8_t n_items; 152 | uint8_t share[16][64]; 153 | }; 154 | 155 | struct cmd_smb2_list { 156 | uint16_t command; 157 | uint8_t share[64]; 158 | uint8_t path[256]; 159 | }; 160 | struct res_smb2_list { 161 | uint8_t status; 162 | uint8_t list[1024]; 163 | }; 164 | 165 | struct cmd_flashconfig { 166 | uint16_t command; 167 | }; 168 | struct res_flashconfig { 169 | uint8_t status; 170 | }; 171 | 172 | struct cmd_flashclear { 173 | uint16_t command; 174 | }; 175 | struct res_flashclear { 176 | uint8_t status; 177 | }; 178 | 179 | struct cmd_reboot { 180 | uint16_t command; 181 | }; 182 | struct res_reboot { 183 | uint8_t status; 184 | }; 185 | 186 | #define countof(array) (sizeof(array) / sizeof(array[0])) 187 | 188 | #endif /* _VD_COMMAND_H_ */ 189 | -------------------------------------------------------------------------------- /src/msc_disk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * Copyright (c) 2023 Yuichi Nakamura 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | #include "bsp/board_api.h" 28 | #include "tusb.h" 29 | #include "virtual_disk.h" 30 | 31 | enum 32 | { 33 | DISK_BLOCK_NUM = VOLUME_SECTOR_COUNT, 34 | DISK_BLOCK_SIZE = SECTOR_SIZE 35 | }; 36 | 37 | // Invoked when received SCSI_CMD_INQUIRY 38 | // Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively 39 | void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) 40 | { 41 | (void) lun; 42 | 43 | const char vid[] = "X68000Z"; 44 | const char pid[] = "Remote Drive Mass Storage"; 45 | const char rev[] = "1.0"; 46 | 47 | memcpy(vendor_id , vid, strlen(vid)); 48 | memcpy(product_id , pid, strlen(pid)); 49 | memcpy(product_rev, rev, strlen(rev)); 50 | } 51 | 52 | // Invoked when received Test Unit Ready command. 53 | // return true allowing host to read/write this LUN e.g SD card inserted 54 | bool tud_msc_test_unit_ready_cb(uint8_t lun) 55 | { 56 | (void) lun; 57 | 58 | return true; // RAM disk is always ready 59 | } 60 | 61 | // Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size 62 | // Application update block count and block size 63 | void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) 64 | { 65 | (void) lun; 66 | 67 | *block_count = DISK_BLOCK_NUM; 68 | *block_size = DISK_BLOCK_SIZE; 69 | } 70 | 71 | // Invoked when received Start Stop Unit command 72 | // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage 73 | // - Start = 1 : active mode, if load_eject = 1 : load disk storage 74 | bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) 75 | { 76 | (void) lun; 77 | (void) power_condition; 78 | 79 | if ( load_eject ) 80 | { 81 | if (start) 82 | { 83 | // load disk storage 84 | }else 85 | { 86 | // unload disk storage 87 | } 88 | } 89 | 90 | return true; 91 | } 92 | 93 | // Callback invoked when received READ10 command. 94 | // Copy disk's data to buffer (up to bufsize) and return number of copied bytes. 95 | int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) 96 | { 97 | // out of ramdisk 98 | if ( lba >= DISK_BLOCK_NUM ) return -1; 99 | 100 | vd_read_block(lba, buffer); 101 | return bufsize; 102 | } 103 | 104 | // Callback invoked when received WRITE10 command. 105 | // Process data in buffer to disk's storage and return number of written bytes 106 | int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) 107 | { 108 | // out of ramdisk 109 | if ( lba >= DISK_BLOCK_NUM ) return -1; 110 | 111 | vd_write_block(lba, buffer); 112 | return bufsize; 113 | } 114 | 115 | // Callback invoked when received an SCSI command not in built-in list below 116 | // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE 117 | // - READ10 and WRITE10 has their own callbacks 118 | int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) 119 | { 120 | // read10 & write10 has their own callback and MUST not be handled here 121 | 122 | void const* response = NULL; 123 | int32_t resplen = 0; 124 | 125 | // most scsi handled is input 126 | bool in_xfer = true; 127 | 128 | switch (scsi_cmd[0]) 129 | { 130 | case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: 131 | // Host is about to read/write etc ... better not to disconnect disk 132 | resplen = 0; 133 | break; 134 | 135 | default: 136 | // Set Sense = Invalid Command Operation 137 | tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); 138 | 139 | // negative means error -> tinyusb could stall and/or response with failed status 140 | resplen = -1; 141 | break; 142 | } 143 | 144 | // return resplen must not larger than bufsize 145 | if ( resplen > bufsize ) resplen = bufsize; 146 | 147 | if ( response && (resplen > 0) ) 148 | { 149 | if(in_xfer) 150 | { 151 | memcpy(buffer, response, resplen); 152 | }else 153 | { 154 | // SCSI output 155 | } 156 | } 157 | 158 | return resplen; 159 | } 160 | -------------------------------------------------------------------------------- /src/FreeRTOSConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeRTOS V202111.00 3 | * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | * the Software, and to permit persons to whom the Software is furnished to do so, 10 | * subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | * http://www.FreeRTOS.org 23 | * http://aws.amazon.com/freertos 24 | * 25 | * 1 tab == 4 spaces! 26 | */ 27 | 28 | #ifndef FREERTOS_CONFIG_H 29 | #define FREERTOS_CONFIG_H 30 | 31 | /*----------------------------------------------------------- 32 | * Application specific definitions. 33 | * 34 | * These definitions should be adjusted for your particular hardware and 35 | * application requirements. 36 | * 37 | * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE 38 | * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. 39 | * 40 | * See http://www.freertos.org/a00110.html 41 | *----------------------------------------------------------*/ 42 | 43 | /* Scheduler Related */ 44 | #define configUSE_PREEMPTION 1 45 | #define configUSE_TICKLESS_IDLE 0 46 | #define configUSE_IDLE_HOOK 0 47 | #define configUSE_TICK_HOOK 0 48 | #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) 49 | #define configMAX_PRIORITIES 32 50 | #define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 512 51 | #define configUSE_16_BIT_TICKS 0 52 | 53 | #define configIDLE_SHOULD_YIELD 1 54 | 55 | /* Synchronization Related */ 56 | #define configUSE_MUTEXES 1 57 | #define configUSE_RECURSIVE_MUTEXES 1 58 | #define configUSE_APPLICATION_TASK_TAG 0 59 | #define configUSE_COUNTING_SEMAPHORES 1 60 | #define configQUEUE_REGISTRY_SIZE 8 61 | #define configUSE_QUEUE_SETS 1 62 | #define configUSE_TIME_SLICING 1 63 | #define configUSE_NEWLIB_REENTRANT 0 64 | // todo need this for lwip FreeRTOS sys_arch to compile 65 | #define configENABLE_BACKWARD_COMPATIBILITY 1 66 | #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 67 | 68 | /* System */ 69 | #define configSTACK_DEPTH_TYPE uint32_t 70 | #define configMESSAGE_BUFFER_LENGTH_TYPE size_t 71 | 72 | /* Memory allocation related definitions. */ 73 | #define configSUPPORT_STATIC_ALLOCATION 0 74 | #define configSUPPORT_DYNAMIC_ALLOCATION 1 75 | #define configAPPLICATION_ALLOCATED_HEAP 0 76 | 77 | /* Hook function related definitions. */ 78 | #define configCHECK_FOR_STACK_OVERFLOW 0 79 | #define configUSE_MALLOC_FAILED_HOOK 0 80 | #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 81 | 82 | /* Run time and task stats gathering related definitions. */ 83 | #define configGENERATE_RUN_TIME_STATS 0 84 | #define configUSE_TRACE_FACILITY 1 85 | #define configUSE_STATS_FORMATTING_FUNCTIONS 0 86 | 87 | /* Co-routine related definitions. */ 88 | #define configUSE_CO_ROUTINES 0 89 | #define configMAX_CO_ROUTINE_PRIORITIES 1 90 | 91 | /* Software timer related definitions. */ 92 | #define configUSE_TIMERS 1 93 | #define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) 94 | #define configTIMER_QUEUE_LENGTH 10 95 | #define configTIMER_TASK_STACK_DEPTH 1024 96 | 97 | /* Interrupt nesting behaviour configuration. */ 98 | /* 99 | #define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] 100 | #define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] 101 | #define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application] 102 | */ 103 | 104 | #if FREE_RTOS_KERNEL_SMP // set by the RP2040 SMP port of FreeRTOS 105 | /* SMP port only */ 106 | #define configNUM_CORES 2 107 | #define configTICK_CORE 0 108 | #define configRUN_MULTIPLE_PRIORITIES 1 109 | #define configUSE_CORE_AFFINITY 1 110 | #endif 111 | 112 | /* RP2040 specific */ 113 | #define configSUPPORT_PICO_SYNC_INTEROP 1 114 | #define configSUPPORT_PICO_TIME_INTEROP 1 115 | 116 | #include 117 | /* Define to trap errors during development. */ 118 | #define configASSERT(x) assert(x) 119 | 120 | /* Set the following definitions to 1 to include the API function, or zero 121 | to exclude the API function. */ 122 | #define INCLUDE_vTaskPrioritySet 1 123 | #define INCLUDE_uxTaskPriorityGet 1 124 | #define INCLUDE_vTaskDelete 1 125 | #define INCLUDE_vTaskSuspend 1 126 | #define INCLUDE_vTaskDelayUntil 1 127 | #define INCLUDE_vTaskDelay 1 128 | #define INCLUDE_xTaskGetSchedulerState 1 129 | #define INCLUDE_xTaskGetCurrentTaskHandle 1 130 | #define INCLUDE_uxTaskGetStackHighWaterMark 1 131 | #define INCLUDE_xTaskGetIdleTaskHandle 1 132 | #define INCLUDE_eTaskGetState 1 133 | #define INCLUDE_xTimerPendFunctionCall 1 134 | #define INCLUDE_xTaskAbortDelay 1 135 | #define INCLUDE_xTaskGetHandle 1 136 | #define INCLUDE_xTaskResumeFromISR 1 137 | #define INCLUDE_xQueueGetMutexHolder 1 138 | 139 | /* A header file that defines trace macro can be included here. */ 140 | 141 | #endif /* FREERTOS_CONFIG_H */ 142 | 143 | -------------------------------------------------------------------------------- /src/usb_descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * Copyright (c) 2023 Yuichi Nakamura 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | #include "tusb.h" 28 | 29 | /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. 30 | * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. 31 | * 32 | * Auto ProductID layout's Bitmap: 33 | * [MSB] HID | MSC | CDC [LSB] 34 | */ 35 | #define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) 36 | #define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ 37 | _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) 38 | 39 | #define USB_VID 0xCafe 40 | #define USB_BCD 0x0200 41 | 42 | //--------------------------------------------------------------------+ 43 | // Device Descriptors 44 | //--------------------------------------------------------------------+ 45 | tusb_desc_device_t const desc_device = 46 | { 47 | .bLength = sizeof(tusb_desc_device_t), 48 | .bDescriptorType = TUSB_DESC_DEVICE, 49 | .bcdUSB = USB_BCD, 50 | .bcdUSB = 0x0200, 51 | .bDeviceClass = 0x00, 52 | .bDeviceSubClass = 0x00, 53 | .bDeviceProtocol = 0x00, 54 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 55 | 56 | .idVendor = USB_VID, 57 | .idProduct = USB_PID, 58 | .bcdDevice = 0x0100, 59 | 60 | .iManufacturer = 0x01, 61 | .iProduct = 0x02, 62 | .iSerialNumber = 0x03, 63 | 64 | .bNumConfigurations = 0x01 65 | }; 66 | 67 | // Invoked when received GET DEVICE DESCRIPTOR 68 | // Application return pointer to descriptor 69 | uint8_t const * tud_descriptor_device_cb(void) 70 | { 71 | return (uint8_t const *) &desc_device; 72 | } 73 | 74 | //--------------------------------------------------------------------+ 75 | // Configuration Descriptor 76 | //--------------------------------------------------------------------+ 77 | 78 | enum 79 | { 80 | ITF_NUM_MSC, 81 | ITF_NUM_TOTAL 82 | }; 83 | 84 | #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN) 85 | 86 | #define EPNUM_MSC_OUT 0x01 87 | #define EPNUM_MSC_IN 0x81 88 | 89 | uint8_t const desc_fs_configuration[] = 90 | { 91 | // Config number, interface count, string index, total length, attribute, power in mA 92 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), 93 | 94 | // Interface number, string index, EP Out & EP In address, EP size 95 | TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), 96 | }; 97 | 98 | #if TUD_OPT_HIGH_SPEED 99 | uint8_t const desc_hs_configuration[] = 100 | { 101 | // Config number, interface count, string index, total length, attribute, power in mA 102 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), 103 | 104 | // Interface number, string index, EP Out & EP In address, EP size 105 | TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512), 106 | }; 107 | #endif // highspeed 108 | 109 | // Invoked when received GET CONFIGURATION DESCRIPTOR 110 | // Application return pointer to descriptor 111 | // Descriptor contents must exist long enough for transfer to complete 112 | uint8_t const * tud_descriptor_configuration_cb(uint8_t index) 113 | { 114 | (void) index; // for multiple configurations 115 | 116 | #if TUD_OPT_HIGH_SPEED 117 | // Although we are highspeed, host may be fullspeed. 118 | return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; 119 | #else 120 | return desc_fs_configuration; 121 | #endif 122 | } 123 | 124 | //--------------------------------------------------------------------+ 125 | // String Descriptors 126 | //--------------------------------------------------------------------+ 127 | 128 | // array of pointer to string descriptors 129 | char const* string_desc_arr [] = 130 | { 131 | (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) 132 | "X68000Z", // 1: Manufacturer 133 | "Remote Drive Mass Storage", // 2: Product 134 | "123456789012", // 3: Serials, should use chip ID 135 | }; 136 | 137 | static uint16_t _desc_str[32]; 138 | 139 | // Invoked when received GET STRING DESCRIPTOR request 140 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 141 | uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) 142 | { 143 | (void) langid; 144 | 145 | uint8_t chr_count; 146 | 147 | if ( index == 0) 148 | { 149 | memcpy(&_desc_str[1], string_desc_arr[0], 2); 150 | chr_count = 1; 151 | }else 152 | { 153 | // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. 154 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors 155 | 156 | if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; 157 | 158 | const char* str = string_desc_arr[index]; 159 | 160 | // Cap at max char 161 | chr_count = strlen(str); 162 | if ( chr_count > 31 ) chr_count = 31; 163 | 164 | // Convert ASCII string into UTF-16 165 | for(uint8_t i=0; i 27 | #include 28 | #include 29 | #include 30 | 31 | #include "smb2.h" 32 | #include "libsmb2.h" 33 | 34 | #include "config_file.h" 35 | 36 | //**************************************************************************** 37 | // Smb2 connection functions 38 | //**************************************************************************** 39 | 40 | struct smb2_context *connect_smb2(const char *share) 41 | { 42 | struct smb2_context *smb2; 43 | if ((smb2 = smb2_init_context()) == NULL) 44 | return NULL; 45 | 46 | if (strlen(config.smb2_user)) 47 | smb2_set_user(smb2, config.smb2_user); 48 | if (strlen(config.smb2_passwd)) 49 | smb2_set_password(smb2, config.smb2_passwd); 50 | if (strlen(config.smb2_workgroup)) 51 | smb2_set_workstation(smb2, config.smb2_workgroup); 52 | 53 | smb2_set_security_mode(smb2, SMB2_NEGOTIATE_SIGNING_ENABLED); 54 | 55 | printf("SMB2 connection server:%s share:%s\n", config.smb2_server, share); 56 | 57 | if (smb2_connect_share(smb2, config.smb2_server, share, config.smb2_user) < 0) { 58 | printf("smb2_connect_share failed. %s\n", smb2_get_error(smb2)); 59 | smb2_destroy_context(smb2); 60 | return NULL; 61 | } 62 | printf("SMB2 connection established.\n"); 63 | 64 | return smb2; 65 | } 66 | 67 | void disconnect_smb2(struct smb2_context *smb2) 68 | { 69 | smb2_disconnect_share(smb2); 70 | smb2_destroy_context(smb2); 71 | } 72 | 73 | //**************************************************************************** 74 | // Smb2 share connection functions 75 | //**************************************************************************** 76 | 77 | //---------------------------------------------------------------------------- 78 | // Private data and functions 79 | //---------------------------------------------------------------------------- 80 | 81 | static struct smb2share { 82 | struct smb2share *next; 83 | char *share; 84 | struct smb2_context *smb2; 85 | int refcnt; 86 | } *smb2share; 87 | 88 | static void disconnect_smb2_internal(struct smb2share **s) 89 | { 90 | disconnect_smb2((*s)->smb2); 91 | free((char *)(*s)->share); 92 | struct smb2share *next = (*s)->next; 93 | free(*s); 94 | *s = next; 95 | } 96 | 97 | static int path2share(const char *path, const char **shpath) 98 | { 99 | const char *p = strchr(path, '/'); 100 | if (p == NULL) { 101 | *shpath = NULL; 102 | return 0; // invalid path 103 | } 104 | 105 | for (*shpath = p + 1; **shpath == '/'; *shpath++) 106 | ; // skip leading slashes 107 | 108 | return p - path; // share length 109 | } 110 | 111 | static struct smb2share **findshare(const char *share, int len) 112 | { 113 | for (struct smb2share **s = &smb2share; *s != NULL; s = &(*s)->next) { 114 | if (strlen((*s)->share) == len && strncmp((*s)->share, share, len) == 0) { 115 | return s; 116 | } 117 | } 118 | return NULL; 119 | } 120 | 121 | //---------------------------------------------------------------------------- 122 | // Public functions 123 | //---------------------------------------------------------------------------- 124 | 125 | struct smb2_context *path2smb2(const char *path, const char **shpath) 126 | { 127 | int len = path2share(path, shpath); 128 | if (len <= 0) { 129 | return NULL; // invalid path 130 | } 131 | 132 | struct smb2share **s; 133 | if (s = findshare(path, len)) { 134 | return (*s)->smb2; // found connected share 135 | } 136 | 137 | return NULL; // no such share 138 | } 139 | 140 | struct smb2_context *connect_smb2_path(const char *path, const char **shpath) 141 | { 142 | int len = path2share(path, shpath); 143 | if (len <= 0) { 144 | return NULL; // invalid path 145 | } 146 | 147 | struct smb2share **s; 148 | if (s = findshare(path, len)) { 149 | (*s)->refcnt++; 150 | return (*s)->smb2; // found connected share 151 | } 152 | 153 | struct smb2_context *smb2; 154 | struct smb2share *t; 155 | t = malloc(sizeof(struct smb2share)); 156 | t->next = NULL; 157 | t->share = calloc(1, len + 1); 158 | memcpy(t->share, path, len); 159 | 160 | if ((smb2 = connect_smb2(t->share)) == NULL) { 161 | free(t->share); 162 | free(t); 163 | return NULL; // connection failed 164 | } 165 | 166 | t->refcnt = 1; 167 | t->smb2 = smb2; 168 | t->next = smb2share; 169 | smb2share = t; 170 | 171 | return smb2; // new connection 172 | } 173 | 174 | void disconnect_smb2_path(const char *path) 175 | { 176 | const char *shpath; 177 | int len = path2share(path, &shpath); 178 | if (len <= 0) { 179 | return; 180 | } 181 | 182 | struct smb2share **s; 183 | if (s = findshare(path, len)) { 184 | if (--(*s)->refcnt == 0) { 185 | disconnect_smb2_internal(s); 186 | } 187 | } 188 | } 189 | 190 | void disconnect_smb2_all(void) 191 | { 192 | struct smb2share **s = &smb2share; 193 | while (*s != NULL) { 194 | disconnect_smb2_internal(s); 195 | } 196 | } 197 | 198 | void keepalive_smb2_all(void) 199 | { 200 | struct smb2share **s = &smb2share; 201 | struct smb2_stat_64 st; 202 | printf("Keepalive:"); 203 | while (*s != NULL) { 204 | int r = smb2_stat((*s)->smb2, "", &st); 205 | printf(" %s->%d", (*s)->share, r); 206 | s = &(*s)->next; 207 | } 208 | printf("\n"); 209 | } 210 | -------------------------------------------------------------------------------- /QUICKSTART.md: -------------------------------------------------------------------------------- 1 | # x68kzremotedrv クイックスタートガイド 2 | 3 | x68kzremotedrv の最初の導入のために、Windows PC に X68000Z とのファイル共有用フォルダを作成してそこから X68000Z をネットワークブートできるようになるまでの手順を説明します。 4 | 5 | ## ハードウェア構成 6 | 7 | 以下の構成のハードウェアが用意されていることを前提とします。 8 | 9 | * X68000Z 本体、キーボード、ディスプレイ 10 | * Raspberry Pi Pico W (以下 ラズパイ Pico W) 11 | * USB micro-B ケーブル 12 | * WiFi ルータ (2.4GHz 帯対応) 13 | * Windows PC 14 | * WiFi ルータを介して WiFi デバイスからのネットワーク接続が可能になっている必要があります 15 | * 動作確認は Windows 11 Pro 22H2 で行っています 16 | 17 | ## X68000Z 側の準備 18 | 19 | X68000Z は本体アップデート更新データによって ver.1.3.1 以降のバージョンに更新されている必要があります。 20 | 21 | [https://www.zuiki.co.jp/x68000z/update/](https://www.zuiki.co.jp/x68000z/update/) 22 | 23 | 「[本体アップデートマニュアル](https://www.zuiki.co.jp/x68000z/pdf/Update_ver100.pdf)」に従って本体を更新後、「[X68000 エミュレータ](https://www.zuiki.co.jp/x68000z/pdf/Emulator_ver131.pdf)」マニュアルの「1.5.Pseudo SCSI機能について」の記述に従って、USB メモリ内のハードディスクイメージからの起動ができることを確認してください。 24 | 25 | ## Windows PC 側の準備 26 | 27 | 1. C ドライブのルートディレクトリに `X68000Z` という名前でフォルダを作成し、フォルダを右クリック→「プロパティ」→「共有」タブを選択→「ネットワークのファイルとフォルダーの共有」で、`\\<ホスト名>\x68000z` というネットワークパスが共有されるように設定します。 28 | 29 | ![1-1](image/1-1.png) 30 | 31 | ![1-2](image/1-2.png) 32 | 33 | 2. 正しく共有設定が行われていれば、Windows エクスプローラのアドレスバーに `\\<ホスト名>` を入力すると 「ネットワーク > <ホスト名>」の中に `X68000Z` フォルダが見えているはずです。 34 | 35 | ![1-3](image/1-3.png) 36 | 37 | 3. 共有フォルダが作成出来たら、その中に以下のファイル、フォルダを配置します。 38 | * X68000Z Z-Club の 「[ソフト一覧](https://dev.zuiki.com/project-z/community/resource)」から「SCSI HDDイメージ (81MB)」をダウンロードして、ZIP ファイル内にあるファイル `SCSIHDD81M.HDS` を `X68000Z` フォルダに置きます 39 | * `X68000Z` フォルダに `HFS` という名前のフォルダを新規作成します 40 | 41 | ![1-4](image/1-4.png) 42 | 43 | ## ラズパイ Pico W 側の準備 44 | 45 | 1. ラズパイ Pico W に USB micro-B ケーブルを接続し、基板上の「BOOTSEL」ボタンを**押しながら** Windows PC に接続します。 46 | 47 | ![2-1](image/2-1.png) 48 | 49 | 接続したラズパイ Pico W が PC から USB メモリとして認識されます。`RPI-RP2` というボリューム名で、`INDEX.HTM` と `INFO_UF2.TXT` という 2 つのファイルが中に存在していれば、ラズパイ Pico W はファームウェア書き込みモードに入っています。 50 | 51 | ![2-2](image/2-2.png) 52 | 53 | 2. 本アーカイブ ([https://github.com/yunkya2/x68kzremotedrv/releases](https://github.com/yunkya2/x68kzremotedrv/releases) からダウンロードした ZIP ファイル) 内にあるファイル `x68kzremotedrv.uf2` を、このフォルダの中にドラッグ & ドロップします。 54 | 55 | しばらくするとファームウェアの書き込みが完了して、ラズパイ Pico W が再起動します。Windows PC から再度 USB メモリとして認識され、`X68000Z`、`config.txt`、`log.txt` の 3 つのファイル/フォルダが存在することを確認します。 56 | 57 | ![2-3](image/2-3.png) 58 | 59 | 3. ファームウェアが書き込まれていることが確認できたら、ラズパイ Pico W を PC から X68000Z に差し替えます。 60 | 61 | ## X68000Z の起動 (HDS ファイル) 62 | 63 | 1. ラズパイ Pico W が接続されている状態で X68000Z 本体の電源を入れて、ゲームランチャーから X68000 エミュレータを起動します。 64 | 初回起動の場合、しばらく待つと以下のような設定画面が表示されます。\ 65 | もし「ディスクから起動できません」のメッセージが出たり Human68k が普通に起動してしまう場合は、起動中にキーボードの F1 キーを押したままにしてみてください。\ 66 | (F1 キーは X68000 エミュレータを起動して X68000 Z のロゴが出た後あたりから押してください) 67 | 68 | ![3-1](image/3-1.png) 69 | 70 | 2. 設定画面上で Windows ファイル共有にアクセスするための設定を行います。\ 71 | 設定はキーボードのカーソルキー [↑][↓] で項目を移動して改行キーで選択することで行います。 72 | 73 | * WiFi 設定 74 | * `SSID` の項目を選択すると、WiFi をスキャンして接続可能なアクセスポイントの一覧が表示されます。 75 | 一覧の中から接続したいアクセスポイントを選択します。 76 | * `PASSWORD` には WiFi アクセスポイントのパスワードを入力します。入力は `*` でマスクされていますが、TAB キーを押すことで表示とマスクを切り替えることができます。 77 | * パスワードを入力すると、選択したアクセスポイントへの接続を開始します。設定欄右上の表示が `未接続` → `接続中` → `接続済` と変化していき、接続済になると Windows ファイル共有設定の項目が現れます。 78 | * Windows ファイル共有設定 79 | * `USERNAME` と `PASSWORD` には共有フォルダにアクセスするためのユーザ名とパスワードを設定します。 80 | * `WORKGROUP` には接続先サーバの所属するワークグループ名を設定します。Windows のデフォルト設定から変えていなければ変更の必要はありません。 81 | * `SERVER` には接続先サーバ名を指定します。サーバ名で接続が上手くいかない場合は IP アドレスでの入力を試してみてください。 82 | * 接続先サーバ名を入力すると、設定したサーバへの接続を開始します。設定欄右上の表示が `未接続` → `接続中` → `接続済` と変化していき、接続済になると HDS とリモートドライブ設定の項目が現れます。 83 | * HDS (SCSI ディスクイメージ) 設定 84 | * X68000Z に接続する HDS ファイルを共有フォルダの中から指定します。`HDS0` を選択するとサーバの共有フォルダの共有名一覧が表示されるので、`X68000Z` → `SCSIHDD81M.HDS` に設定します。 85 | * リモートドライブ設定 86 | * X68000Z に接続するリモートドライブを共有フォルダの中から指定します。 87 | * まずは `RMTBOOT` を 0 のまま (HDS ファイルから起動)、`RMTUNIT` を 1 に指定します。`RMTUNIT` の指定によって `REMOTE0` 項目が現れます。 88 | * `REMOTE0` を選択するとサーバの共有フォルダの共有名一覧が表示されるので、`X68000Z` → `HFS` → `./` に設定します。 89 | 90 | すべての設定が完了すると以下のような画面になります。`設定終了` を選択すると設定がラズパイ Pico W 内に保存されるので、電源スイッチで一度 X68000Z の電源を切って、再投入してください。 91 | 92 | ※ X68000 Z PRODUCT EDITION はリセットボタンでエミュレータホスト環境がリセットされないため、電源断が必要になります。HACKER'S EDITION や EARLY ACCESS KIT の場合は本体のリセットボタンでも大丈夫です。 93 | 94 | ![3-2](image/3-2.png) 95 | 96 | 3. X68000 エミュレータを起動すると、HDS ファイル `X68000Z/SCSIHDD81M.HDS` をハードディスクイメージとして起動します。 97 | 98 | 起動後に `drive` コマンドを実行すると、以下のように A: ドライブにSCSI ハードディスクイメージが、B: ドライブには「その他のタイプ」としてリモートドライブがあることが確認できます。 99 | 100 | ![3-3](image/3-3.png) 101 | 102 | 4. B:ドライブでのファイル操作が、PC に作成した `C:\X68000Z\HFS` フォルダ内のファイルとなります。 103 | 現在このフォルダの中は空なので、起動に使用したハードディスクイメージの内容をそのままコピーします。\ 104 | X68000Z のコマンドラインで `copyall A:*.* B:` と入力して、A: ドライブの内容をすべて B: ドライブにコピーします。 105 | 106 | PC の `C:\X68000Z\HFS` フォルダにコピーしたファイルが作成されていることが確認できます。 107 | 108 | ![3-4](image/3-4.png) 109 | 110 | ## X68000Z の起動 (リモートドライブ) 111 | 112 | 1. 設定変更のために X68000Z を一度リセットします。再起動中は F1 キーを押したままにして設定画面を出します。 113 | 114 | 2. リモートドライブ設定の `RMTBOOT` を 0 から 1 に変更します。これによって、HDS ファイルからの起動がリモートドライブからの起動に切り替わります。 115 | 116 | ![4-1](image/4-1.png) 117 | 118 | `設定終了` を選択後、電源スイッチで一度 X68000Z の電源を切って再投入してください。 119 | 120 | 3. X68000 エミュレータを起動すると、`C:\X68000Z\HFS` フォルダ内にコピーされた内容をリモートドライブとしてネットワークブートします。 121 | 122 | 起動後に `drive` コマンドを実行すると、起動に使用した A: ドライブがリモートドライブである「その他のタイプ」となっています。 123 | 124 | ![4-2](image/4-2.png) 125 | 126 | 以上でリモートドライブの設定と、そこからネットワークブートできることの確認ができました。 127 | 128 | 起動時に F1 キーを押して表示される設定画面で設定を変更することで、リモートドライブを最大 4 台まで追加したり、複数の HDS ファイルを認識させることもできるようになります。 129 | 130 | ## トラブルシューティング 131 | 132 | * WiFi 設定が `接続済` 状態にならない 133 | * ==> WiFi の SSID またはパスワード設定に誤りがあります 134 | * ラズパイ Pico W の WiFi は 2.4GHz 帯のみをサポートしています。5GHz 帯を使用する 802.11a のアクセスポイントには接続できないことに注意してください 135 | * Windows ファイル共有が `接続済` 状態にならない 136 | * ==> Windows ファイル共有のサーバ名やユーザ名、パスワード設定に誤りがあります。 137 | * 接続先サーバ名の名前解決ができない場合は IP アドレスを指定してみてください。 138 | * ファイル共有のユーザ名にはマイクロソフトアカウントは使用できません。サーバ上にローカルアカウントを作成してそれを指定し、ファイル共有もローカルアカウントに対してアクセス許可を与えるようにしてください。 139 | * 起動時にリモートドライブサービスの認識に失敗することがある(「ディスクから起動できません」と表示される) 140 | * 通常使用時は、ラズパイ Pico W から WiFi ルータと Windows ファイル共有への接続が完了した後で X68000Z から認識されるようになるのですが、環境によってはこのタイミングが遅くて X68000Z 側の認識処理に間に合わないことがあるようです。 141 | * 回避手段として、設定項目に `FASTCONN` (リモートドライブサービスの接続を高速化する) を追加しました。通常はこの項目は 0 のままで良いですが、認識に失敗する際は 1 にしてみてください。 142 | * 1 にすると X68000Z からの認識が高速化される一方で、すべての HDS ファイルが約4GBのファイルとして扱われるという副作用があります。フォーマット済みのディスクイメージを使用する限りはこの副作用が問題になることはありませんが、新規にディスクイメージファイルを用意して format.x の「装置初期化」でイメージファイルを初期化する場合、ファイルサイズが正常に認識されないため正しくフォーマットが行えなくなります。このような場合は `FASTCONN=0` にするか、エミュレータ等他の環境で作成したフォーマット済みの HDS ファイルを使用するようにしてください。 143 | * 起動時に F1 キーを押しても設定画面が出ない 144 | * X68000 エミュレータの起動中に USB キーボードのリセットが行われているようで、エミュレータ起動前からキーを押したままにしても認識されません。エミュレータの "X68000 Z" ロゴが出て数秒待ってから押すと良いようです。 145 | * SRAM 常駐型のブートセレクタを使用している場合は、SCSI ID 6 から起動することで設定画面が表示されます 146 | * 設定内容によっては (HDS イメージもリモートドライブもない状態で設定を保存してしまうなど) 設定画面を出すことができなくなってしまう場合があります。この対策として、FD 起動して設定画面を出すことのできるレスキューディスクイメージ (`zrmtrescue.xdf`) を用意しました。SD カードの `X68000Z` フォルダに入れてこのイメージファイルから起動することで、設定画面が表示されるようになります。 147 | -------------------------------------------------------------------------------- /driver/scsiremote.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | //**************************************************************************** 41 | // Global variables 42 | //**************************************************************************** 43 | 44 | int scsiid; 45 | 46 | #ifdef DEBUG 47 | int debuglevel = 0; 48 | #endif 49 | 50 | //**************************************************************************** 51 | // for debugging 52 | //**************************************************************************** 53 | 54 | #ifdef DEBUG 55 | char heap[1024]; // temporary heap for debug print 56 | void *_HSTA = heap; 57 | void *_HEND = heap + 1024; 58 | void *_PSP; 59 | 60 | void DPRINTF(int level, char *fmt, ...) 61 | { 62 | char buf[256]; 63 | va_list ap; 64 | 65 | if (debuglevel < level) 66 | return; 67 | 68 | va_start(ap, fmt); 69 | vsiprintf(buf, fmt, ap); 70 | va_end(ap); 71 | _iocs_b_print(buf); 72 | } 73 | 74 | void DNAMEPRINT(void *n, bool full, char *head) 75 | { 76 | struct dos_namestbuf *b = (struct dos_namestbuf *)n; 77 | 78 | DPRINTF1("%s%c:", head, b->drive + 'A'); 79 | for (int i = 0; i < 65 && b->path[i]; i++) { 80 | DPRINTF1("%c", (uint8_t)b->path[i] == 9 ? '\\' : (uint8_t)b->path[i]); 81 | } 82 | if (full) 83 | DPRINTF1("%.8s%.10s.%.3s", b->name1, b->name2, b->ext); 84 | } 85 | #endif 86 | 87 | //**************************************************************************** 88 | // Communication 89 | //**************************************************************************** 90 | 91 | struct vdbuf vdbuf_read; 92 | struct vdbuf vdbuf_write; 93 | 94 | int seqno = 0; 95 | int seqtim = 0; 96 | int sect = 0x400000; 97 | 98 | #define SCSICOMMID 6 99 | 100 | void com_cmdres(void *wbuf, size_t wsize, void *rbuf, size_t rsize) 101 | { 102 | struct vdbuf_header *h; 103 | int wcnt = (wsize - 1) / (512 - 16); 104 | int rcnt = (rsize - 1) / (512 - 16); 105 | 106 | h = &vdbuf_write.header; 107 | h->signature = 0x5836385a; /* "X68Z" */ 108 | h->session = seqtim; 109 | h->seqno = seqno; 110 | h->maxpage = wcnt; 111 | for (int i = 0; i <= wcnt; i++) { 112 | h->page = i; 113 | int s = wsize > (512 - 16) ? 512 - 16 : wsize; 114 | memcpy(vdbuf_write.buf, wbuf, s); 115 | wsize -= s; 116 | wbuf += s; 117 | _iocs_s_writeext(0x20, 1, SCSICOMMID, 1, &vdbuf_write); 118 | for (int i = 0; i < 128; i++) { 119 | DPRINTF1("%02x ", ((uint8_t *)&vdbuf_write)[i]); 120 | if ((i % 16) == 15) 121 | DPRINTF1("\r\n"); 122 | } 123 | } 124 | 125 | sect = ((sect - 8) % 0x200000) + 0x200000; 126 | h = &vdbuf_read.header; 127 | for (int i = 0; i <= rcnt; i++) { 128 | while (1) { 129 | DPRINTF1("sect=0x%x\r\n", sect); 130 | _iocs_s_readext(sect + (i & 7), 1, SCSICOMMID, 1, &vdbuf_read); 131 | for (int i = 0; i < 64; i++) { 132 | DPRINTF1("%02x ", ((uint8_t *)&vdbuf_read)[i]); 133 | if ((i % 16) == 15) 134 | DPRINTF1("\r\n"); 135 | } 136 | if (memcmp(&vdbuf_read, &vdbuf_write, 12) == 0) 137 | break; 138 | sect = ((sect - 0x10000) % 0x200000) + 0x200000; 139 | } 140 | int s = rsize > (512 - 16) ? 512 - 16 : rsize; 141 | memcpy(rbuf, vdbuf_read.buf, s); 142 | rcnt = h->maxpage; 143 | rsize -= s; 144 | rbuf += s; 145 | if ((i & 7) == 7) { 146 | sect = ((sect - 8) % 0x200000) + 0x200000; 147 | } 148 | } 149 | seqno++; 150 | } 151 | 152 | //**************************************************************************** 153 | // Utility routine 154 | //**************************************************************************** 155 | 156 | static int my_atoi(char *p) 157 | { 158 | int res = 0; 159 | while (*p >= '0' && *p <= '9') { 160 | res = res * 10 + *p++ - '0'; 161 | } 162 | return res; 163 | } 164 | 165 | //**************************************************************************** 166 | // Device driver interrupt rountine 167 | //**************************************************************************** 168 | 169 | int com_timeout(struct dos_req_header *req) 170 | { 171 | return 0x7002; 172 | } 173 | 174 | int com_init(struct dos_req_header *req) 175 | { 176 | #ifdef CONFIG_BOOTDRIVER 177 | _iocs_b_print 178 | #else 179 | _dos_print 180 | #endif 181 | ("\r\nX68000 Z Remote Drive Driver (version " GIT_REPO_VERSION ") ID="); 182 | 183 | #ifdef CONFIG_BOOTDRIVER 184 | extern uint8_t scsiidd2; 185 | #else 186 | int scsiidd2 = 1; 187 | #endif 188 | scsiid = scsiidd2 - 1; 189 | 190 | int unit = 0; 191 | 192 | volatile uint8_t *scsidrvflg = (volatile uint8_t *)0x000cec; 193 | *scsidrvflg |= (1 << scsiid); 194 | 195 | seqtim = _iocs_bindateget(); 196 | seqtim ^= _iocs_timeget() << 8; 197 | struct iocs_time it = _iocs_ontime(); 198 | seqtim ^= it.sec; 199 | 200 | #ifdef CONFIG_BOOTDRIVER 201 | _iocs_b_putc 202 | #else 203 | _dos_putchar 204 | #endif 205 | ('0' + scsiid); 206 | 207 | #ifdef CONFIG_BOOTDRIVER 208 | _iocs_b_print 209 | #else 210 | _dos_print 211 | #endif 212 | ("\r\n"); 213 | 214 | { 215 | struct cmd_getinfo cmd; 216 | struct res_getinfo res; 217 | cmd.command = CMD_GETINFO; 218 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 219 | if (res.year > 0) { 220 | *(volatile uint8_t *)0xe8e000 = 'T'; 221 | *(volatile uint8_t *)0xe8e000 = 'W'; 222 | *(volatile uint8_t *)0xe8e000 = 0; // disable RTC auto adjust 223 | _iocs_timeset(_iocs_timebcd((res.hour << 16) | (res.min << 8) | res.sec)); 224 | _iocs_bindateset(_iocs_bindatebcd((res.year << 16) | (res.mon << 8) | res.day)); 225 | } 226 | unit = res.unit; 227 | } 228 | { 229 | struct cmd_init cmd; 230 | struct res_init res; 231 | cmd.command = 0x00; /* init */ 232 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 233 | } 234 | 235 | #ifndef CONFIG_BOOTDRIVER 236 | char *p = (char *)req->status; 237 | p += strlen(p) + 1; 238 | while (*p != '\0') { 239 | if (*p == '/' || *p =='-') { 240 | p++; 241 | switch (*p | 0x20) { 242 | #ifdef DEBUG 243 | case 'd': // /D .. デバッグレベル増加 244 | debuglevel++; 245 | break; 246 | #endif 247 | } 248 | } 249 | p += strlen(p) + 1; 250 | } 251 | #endif 252 | 253 | #ifndef CONFIG_BOOTDRIVER 254 | _dos_print("ドライブ"); 255 | _dos_putchar('A' + *(char *)&req->fcb); 256 | _dos_print(":でSCSIに接続したリモートドライブが利用可能です\r\n"); 257 | #endif 258 | DPRINTF1("Debug level: %d\r\n", debuglevel); 259 | 260 | #ifdef CONFIG_BOOTDRIVER 261 | /* SCSI ROM のデバイスドライバ組み込み処理から渡される値 262 | *「何番目のパーティションから起動するか」を Human68k のドライバ初期化処理に返す 263 | * この値に基づいてどのドライブから起動するか (CONFIG.SYSをどのドライブから読むか) が決まる 264 | */ 265 | extern uint8_t bootpart; 266 | *(char *)&req->fcb = bootpart; 267 | #endif 268 | 269 | return unit; 270 | } 271 | -------------------------------------------------------------------------------- /md2txtconv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # md2txtconv.py - Convert markdown to text file 4 | # 5 | # Copyright (c) 2025 Yuichi Nakamura (@yunkya2) 6 | # 7 | # The MIT License (MIT) 8 | # 9 | # Permission is hereby granted, free of charge, to any person obtaining a copy 10 | # of this software and associated documentation files (the "Software"), to deal 11 | # in the Software without restriction, including without limitation the rights 12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | # copies of the Software, and to permit persons to whom the Software is 14 | # furnished to do so, subject to the following conditions: 15 | # 16 | # The above copyright notice and this permission notice shall be included in 17 | # all copies or substantial portions of the Software. 18 | # 19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | # THE SOFTWARE. 26 | 27 | import sys 28 | import re 29 | import codecs 30 | import os 31 | 32 | # 定数の定義 33 | WIDTH = 76 34 | PRO_HEAD_CHARS = "。、〕〉》」』】〙〗" 35 | 36 | def get_display_width(text): 37 | """ 38 | テキストの表示幅を計算する関数 39 | ASCII文字は1、その他のUnicode文字は2として計算 40 | """ 41 | width = 0 42 | for char in text: 43 | if ord(char) < 128: 44 | width += 1 45 | else: 46 | width += 2 47 | return width 48 | 49 | def wrap_text(text, width): 50 | """ 51 | テキストを指定された幅で折り返す関数 52 | 英数字が連続している単語の途中で改行しないようにする 53 | """ 54 | if not text.strip(): 55 | return text 56 | text = text.rstrip("\n") 57 | leading_spaces = len(text) - len(text.lstrip(' ')) 58 | next_leading_spaces = leading_spaces 59 | 60 | # リスト項目のインデントを調整 61 | if m := re.match(r'( *)\* ', text): 62 | text = " " + text 63 | leading_spaces = len(m.group(1)) + 1 64 | next_leading_spaces = len(m.group(0)) + 1 65 | if m := re.match(r'( *)\d+\. ', text): 66 | text = " " + text 67 | leading_spaces = len(m.group(1)) + 1 68 | next_leading_spaces = len(m.group(0)) + 1 69 | 70 | wrapped_lines = [] 71 | current_line = " " * leading_spaces 72 | current_width = leading_spaces 73 | 74 | word_buffer = "" 75 | word_width = 0 76 | 77 | for char in text.lstrip(' '): 78 | char_width = get_display_width(char) 79 | if char.isspace(): 80 | # スペースが見つかった場合、現在の単語を行に追加 81 | if current_width + word_width > width: 82 | wrapped_lines.append(current_line) 83 | leading_spaces = next_leading_spaces 84 | current_line = " " * leading_spaces + word_buffer 85 | current_width = leading_spaces + word_width 86 | else: 87 | current_line += word_buffer 88 | current_width += word_width 89 | current_line += char 90 | current_width += char_width 91 | word_buffer = "" 92 | word_width = 0 93 | else: 94 | if ord(char) < 128: 95 | # 英数字の場合、単語の途中で改行しないようにする 96 | if current_width + word_width + char_width > width: 97 | wrapped_lines.append(current_line) 98 | leading_spaces = next_leading_spaces 99 | current_line = " " * leading_spaces + word_buffer + char 100 | current_width = leading_spaces + word_width + char_width 101 | word_buffer = "" 102 | word_width = 0 103 | else: 104 | word_buffer += char 105 | word_width += char_width 106 | else: 107 | # その他のUnicode文字は途中で改行してもよい 108 | if word_width > 0: 109 | # まだ出力していない単語がある場合、行に追加 110 | current_line += word_buffer 111 | current_width += word_width 112 | word_buffer = "" 113 | word_width = 0 114 | 115 | if current_width + char_width > width: 116 | if char in PRO_HEAD_CHARS: 117 | # 禁則処理 118 | current_line += char 119 | char = "" 120 | char_width = 0 121 | wrapped_lines.append(current_line) 122 | leading_spaces = next_leading_spaces 123 | current_line = " " * leading_spaces + char 124 | current_width = leading_spaces + char_width 125 | else: 126 | current_line += char 127 | current_width += char_width 128 | 129 | if word_buffer: 130 | # 最後の単語を行に追加 131 | if current_width + word_width > width: 132 | wrapped_lines.append(current_line) 133 | current_line = " " * leading_spaces + word_buffer 134 | else: 135 | current_line += word_buffer 136 | 137 | if current_line.strip(): 138 | wrapped_lines.append(current_line) 139 | 140 | return "\n".join(wrapped_lines) + "\n" 141 | 142 | def process_file(filename, remove_original, replacements): 143 | """ 144 | ファイルを処理して変換する関数 145 | """ 146 | with codecs.open(filename, 'r', 'utf-8') as f: 147 | lines = f.readlines() 148 | 149 | with codecs.open(filename.replace('.md', '.txt'), 'w', 'cp932') as f: 150 | nowrap = False 151 | for line in lines: 152 | w = get_display_width(line) 153 | 154 | if '```' in line: 155 | # コードブロックの開始・終了を検出 156 | nowrap = not nowrap 157 | pos = line.find('```') 158 | if pos == 0: 159 | line = '-' * (WIDTH - 1) + '\n' 160 | else: 161 | line = line[:pos] + '-' * (WIDTH - pos - 1) + '\n' 162 | else: 163 | # マークダウンの記法をテキストに変換 164 | line = re.sub(r'`', '', line) 165 | line = re.sub(r'\\$', '', line) 166 | line = re.sub(r'\\<', '<', line) 167 | line = re.sub(r'\\>', '>', line) 168 | line = re.sub(r'^# (.*)', r'■■■ \1 ■■■', line) 169 | line = re.sub(r'^## ', '■ ', line) 170 | line = re.sub(r'^### ', '● ', line) 171 | line = re.sub(r'^#### ', '○ ', line) 172 | line = re.sub(r'\*\*', '', line) 173 | line = re.sub(r'\[([^]]*)\]\([^)]*\)', r'\1', line) 174 | line = re.sub(r'(--*):', r'\1-', line) 175 | 176 | # 可能であればテキストの折り返しを行う 177 | if not re.search(r'.*\|.*\|', line) and not nowrap: 178 | line = wrap_text(line, WIDTH) 179 | 180 | # キーワード置換を行う 181 | for keyword, value in replacements.items(): 182 | line = line.replace(keyword, value) 183 | 184 | # 改行コードを変換してファイルに書き込む 185 | line = re.sub(r'\n', '\r\n', line) 186 | f.write(line) 187 | 188 | # オプションに応じて元のファイルを削除する 189 | if remove_original: 190 | os.remove(filename) 191 | 192 | def print_usage(): 193 | """ 194 | 使用法のヘルプメッセージを表示する関数 195 | """ 196 | print("Usage: md2txtconv.py [options] ") 197 | print("Options:") 198 | print(" -r Remove original .md files after conversion") 199 | print(" -h Show this help message") 200 | print(" keyword=value Replace occurrences of 'keyword' with 'value' in the text") 201 | 202 | if __name__ == "__main__": 203 | remove_original = False 204 | filenames = [] 205 | replacements = {} 206 | 207 | for arg in sys.argv[1:]: 208 | if arg == '-r': 209 | remove_original = True 210 | elif arg == '-h': 211 | print_usage() 212 | sys.exit(0) 213 | elif '=' in arg: 214 | keyword, value = arg.split('=', 1) 215 | replacements[keyword] = value 216 | else: 217 | filenames.append(arg) 218 | 219 | if not filenames: 220 | print_usage() 221 | sys.exit(1) 222 | 223 | for filename in filenames: 224 | process_file(filename, remove_original, replacements) 225 | -------------------------------------------------------------------------------- /driver/settinguipat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | #include 26 | #include 27 | 28 | #ifndef _SETTINGUIPAT_H_ 29 | #define _SETTINGUIPAT_H_ 30 | 31 | const struct iocs_fntbuf keybdpat[] = 32 | { 33 | /* #a 34 | --------++++++++ 35 | ############## 36 | ####### ####### 37 | ###### ## ###### 38 | ##### #### ##### 39 | #### ###### #### 40 | ### ######## ### 41 | ## #### ## 42 | ##### #### ##### 43 | ##### #### ##### 44 | ##### #### ##### 45 | ##### #### ##### 46 | ##### #### ##### 47 | ##### #### ##### 48 | ##### #### ##### 49 | ##### ##### 50 | ############## 51 | */ 52 | { 16, 16, 53 | 0x7f, 0xfe, 0xfe, 0x7f, 0xfd, 0xbf, 0xfb, 0xdf, 54 | 0xf7, 0xef, 0xef, 0xf7, 0xc3, 0xc3, 0xfb, 0xdf, 55 | 0xfb, 0xdf, 0xfb, 0xdf, 0xfb, 0xdf, 0xfb, 0xdf, 56 | 0xfb, 0xdf, 0xfb, 0xdf, 0xf8, 0x1f, 0x7f, 0xfe, 57 | }, 58 | /* #b 59 | --------++++++++ 60 | ############## 61 | ##### ##### 62 | ##### #### ##### 63 | ##### #### ##### 64 | ##### #### ##### 65 | ##### #### ##### 66 | ##### #### ##### 67 | ##### #### ##### 68 | ##### #### ##### 69 | ## #### ## 70 | ### ######## ### 71 | #### ###### #### 72 | ##### #### ##### 73 | ###### ## ###### 74 | ####### ####### 75 | ############## 76 | */ 77 | { 16, 16, 78 | 0x7f, 0xfe, 0xf8, 0x1f, 0xfb, 0xdf, 0xfb, 0xdf, 79 | 0xfb, 0xdf, 0xfb, 0xdf, 0xfb, 0xdf, 0xfb, 0xdf, 80 | 0xfb, 0xdf, 0xc3, 0xc3, 0xef, 0xf7, 0xf7, 0xef, 81 | 0xfb, 0xdf, 0xfd, 0xbf, 0xfe, 0x7f, 0x7f, 0xfe, 82 | }, 83 | /* #c 84 | --------++++++++ 85 | ############## 86 | ################ 87 | ###### ######### 88 | ##### ######### 89 | #### # ######### 90 | ### ## # 91 | ## ########### # 92 | # ############ # 93 | # ############ # 94 | ## ########### # 95 | ### ## # 96 | #### # ######### 97 | ##### ######### 98 | ###### ######### 99 | ################ 100 | ############## 101 | */ 102 | { 16, 16, 103 | 0x7f, 0xfe, 0xff, 0xff, 0xfd, 0xff, 0xf9, 0xff, 104 | 0xf5, 0xff, 0xec, 0x01, 0xdf, 0xfd, 0xbf, 0xfd, 105 | 0xbf, 0xfd, 0xdf, 0xfd, 0xec, 0x01, 0xf5, 0xff, 106 | 0xf9, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x7f, 0xfe, 107 | }, 108 | /* #d 109 | --------++++++++ 110 | ############## 111 | ################ 112 | ######### ###### 113 | ######### ##### 114 | ######### # #### 115 | # ## ### 116 | # ########### ## 117 | # ############ # 118 | # ############ # 119 | # ########### ## 120 | # ## ### 121 | ######### # #### 122 | ######### ##### 123 | ######### ###### 124 | ################ 125 | ############## 126 | */ 127 | { 16, 16, 128 | 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xbf, 0xff, 0x9f, 129 | 0xff, 0xaf, 0x80, 0x37, 0xbf, 0xfb, 0xbf, 0xfd, 130 | 0xbf, 0xfd, 0xbf, 0xfb, 0x80, 0x37, 0xff, 0xaf, 131 | 0xff, 0x9f, 0xff, 0xbf, 0xff, 0xff, 0x7f, 0xfe, 132 | }, 133 | /* #e 134 | --------++++++++-------- 135 | ###################### 136 | ################ ## 137 | ################ #### ## 138 | ################ #### ## 139 | ################ #### ## 140 | ##### ########## #### ## 141 | #### ########## #### ## 142 | ### # ##### ## 143 | ## ################## ## 144 | # ################### ## 145 | # ################### ## 146 | ## ################## ## 147 | ### # ### 148 | #### ################## 149 | ##### ################## 150 | ###################### 151 | */ 152 | { 24, 16, 153 | 0x7f, 0xff, 0xfe, 0xff, 0xff, 0x03, 0xff, 0xff, 0x7b, 0xff, 0xff, 0x7b, 154 | 0xff, 0xff, 0x7b, 0xfb, 0xff, 0x7b, 0xf3, 0xff, 0x7b, 0xe8, 0x00, 0xfb, 155 | 0xdf, 0xff, 0xfb, 0xbf, 0xff, 0xfb, 0xbf, 0xff, 0xfb, 0xdf, 0xff, 0xfb, 156 | 0xe8, 0x00, 0x07, 0xf3, 0xff, 0xff, 0xfb, 0xff, 0xff, 0x7f, 0xff, 0xfe, 157 | }, 158 | /* #f 159 | aaaaaaaaaabbbbbbbbbbcccccccccc 160 | --------++++++++--------++++++++ 161 | ############################## 162 | ################################ 163 | ################################ 164 | ## ### ##### ### 165 | ## ######### ###### ### ##### ## 166 | ## ######### ######### ######### 167 | ## ######### ######### ######### 168 | ## ########## ######## ######### 169 | ## ###### #### ######### 170 | ## ############### ### ######### 171 | ## ################ ## ######### 172 | ## ################ ## ######### 173 | ## ######### ###### ### ##### ## 174 | ## ### ##### ### 175 | ################################ 176 | ############################## 177 | */ 178 | { 32, 16, 179 | 0x7f, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x38, 0x1f, 0x07, 180 | 0xdf, 0xf7, 0xee, 0xfb, 0xdf, 0xf7, 0xfd, 0xff, 0xdf, 0xf7, 0xfd, 0xff, 0xdf, 0xfb, 0xfd, 0xff, 181 | 0xc0, 0xfc, 0x3d, 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xdf, 0xff, 0xed, 0xff, 0xdf, 0xff, 0xed, 0xff, 182 | 0xdf, 0xf7, 0xee, 0xfb, 0xc0, 0x38, 0x1f, 0x07, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xfe, 183 | }, 184 | /* #g 185 | aaaaaaaaaabbbbbbbbbbcccccccccc 186 | --------++++++++--------++++++++ 187 | ############################## 188 | ################################ 189 | ################################ 190 | ## #### ##### #### 191 | ###### ###### #### ### ##### ### 192 | ###### ###### #### ### ###### ## 193 | ###### ##### ###### ## ###### ## 194 | ###### ##### ###### ## ##### ### 195 | ###### ##### ###### ## #### 196 | ###### ##### ## ##### ### 197 | ###### ##### ###### ## ###### ## 198 | ###### ##### ###### ## ###### ## 199 | ###### ##### ###### ## ##### ### 200 | ###### ##### ###### ## #### 201 | ################################ 202 | ############################## 203 | */ 204 | { 32, 16, 205 | 0x7f, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x1e, 0x7c, 0x0f, 206 | 0xfd, 0xfb, 0xdd, 0xf7, 0xfd, 0xfb, 0xdd, 0xfb, 0xfd, 0xf7, 0xed, 0xfb, 0xfd, 0xf7, 0xed, 0xf7, 207 | 0xfd, 0xf7, 0xec, 0x0f, 0xfd, 0xf0, 0x0d, 0xf7, 0xfd, 0xf7, 0xed, 0xfb, 0xfd, 0xf7, 0xed, 0xfb, 208 | 0xfd, 0xf7, 0xed, 0xf7, 0xfd, 0xf7, 0xec, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xfe, 209 | }, 210 | /* #h 211 | --------++++++++ 212 | ############## 213 | ################ 214 | ## ######### ### 215 | ### ####### #### 216 | #### ##### ##### 217 | ##### ### ###### 218 | ###### # ####### 219 | ####### ######## 220 | ####### ######## 221 | ####### ######## 222 | ####### ######## 223 | ####### ######## 224 | ####### ######## 225 | ####### ######## 226 | ################ 227 | ############## 228 | */ 229 | { 16, 16, 230 | 0x7f, 0xfe, 0xff, 0xff, 0xdf, 0xf7, 0xef, 0xef, 231 | 0xf7, 0xdf, 0xfb, 0xbf, 0xfd, 0x7f, 0xfe, 0xff, 232 | 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 233 | 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x7f, 0xfe, 234 | }, 235 | /* #i 236 | --------++++++++ 237 | ############## 238 | ################ 239 | ## ######### ### 240 | ## ######## ### 241 | ## # ####### ### 242 | ## ## ###### ### 243 | ## ### ##### ### 244 | ## #### #### ### 245 | ## ##### ### ### 246 | ## ###### ## ### 247 | ## ####### # ### 248 | ## ######## ### 249 | ## ######### ### 250 | ## ######### ### 251 | ################ 252 | ############## 253 | */ 254 | { 16, 16, 255 | 0x7f, 0xfe, 0xff, 0xff, 0xdf, 0xf7, 0xcf, 0xf7, 256 | 0xd7, 0xf7, 0xdb, 0xf7, 0xdd, 0xf7, 0xde, 0xf7, 257 | 0xdf, 0x77, 0xdf, 0xb7, 0xdf, 0xd7, 0xdf, 0xe7, 258 | 0xdf, 0xf7, 0xdf, 0xf7, 0xff, 0xff, 0x7f, 0xfe, 259 | }, 260 | }; 261 | 262 | #endif /* _SETTINGUIPAT_H_ */ 263 | -------------------------------------------------------------------------------- /src/connect.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024,2025 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "pico/cyw43_arch.h" 33 | #include "pico/stdio.h" 34 | #include "pico/stdlib.h" 35 | #include "FreeRTOS.h" 36 | #include "task.h" 37 | #include "semphr.h" 38 | #include "smb2.h" 39 | #include "libsmb2.h" 40 | #include "bsp/board_api.h" 41 | #include "tusb.h" 42 | 43 | #include "config.h" 44 | #include "main.h" 45 | #include "virtual_disk.h" 46 | #include "config_file.h" 47 | 48 | //**************************************************************************** 49 | // Global variables 50 | //**************************************************************************** 51 | 52 | uint64_t boottime = 0; 53 | 54 | volatile int sysstatus = STAT_WIFI_DISCONNECTED; 55 | 56 | const char *rootpath[8]; 57 | 58 | struct diskinfo diskinfo[7]; 59 | 60 | //**************************************************************************** 61 | // Static variables 62 | //**************************************************************************** 63 | 64 | //**************************************************************************** 65 | // Private functions 66 | //**************************************************************************** 67 | 68 | static void connection(int mode) 69 | { 70 | switch (mode) { 71 | case CONNECT_WIFI: 72 | printf("Connecting to WiFi...\n"); 73 | 74 | sysstatus = STAT_WIFI_CONNECTING; 75 | 76 | if (strlen(config.wifi_ssid) == 0 || 77 | cyw43_arch_wifi_connect_timeout_ms(config.wifi_ssid, config.wifi_passwd, 78 | CYW43_AUTH_WPA2_AES_PSK, 30000)) { 79 | sysstatus = STAT_WIFI_DISCONNECTED; 80 | printf("Failed to connect.\n"); 81 | break; 82 | } 83 | 84 | sysstatus = STAT_WIFI_CONNECTED; 85 | 86 | ip4_addr_t *address = &(cyw43_state.netif[0].ip_addr); 87 | printf("Connected to %s as %d.%d.%d.%d as host %s\n", 88 | config.wifi_ssid, 89 | ip4_addr1_16(address), ip4_addr2_16(address), ip4_addr3_16(address), ip4_addr4_16(address), 90 | cyw43_state.netif[0].hostname); 91 | 92 | /* fall through */ 93 | 94 | case CONNECT_SMB2: 95 | if (strlen(config.smb2_server) == 0) { 96 | printf("Failed to connect SMB2 server\n"); 97 | break; 98 | } 99 | 100 | sysstatus = STAT_SMB2_CONNECTING; 101 | 102 | struct smb2_context *smb2ipc; 103 | 104 | if ((smb2ipc = connect_smb2("IPC$")) == NULL) { 105 | sysstatus = STAT_WIFI_CONNECTED; 106 | break; 107 | } 108 | 109 | sysstatus = STAT_SMB2_CONNECTED; 110 | 111 | boottime = (smb2_get_system_time(smb2ipc) / 10) - (11644473600 * 1000000) - to_us_since_boot(get_absolute_time()); 112 | time_t tt = (time_t)((boottime + to_us_since_boot(get_absolute_time())) / 1000000); 113 | struct tm *tm = localtime(&tt); 114 | printf("Boottime UTC %04d/%02d/%02d %02d:%02d:%02d\n", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); 115 | 116 | disconnect_smb2(smb2ipc); 117 | 118 | /* fall through */ 119 | 120 | default: 121 | break; 122 | } 123 | } 124 | 125 | static int vd_mount(void) 126 | { 127 | uint32_t nvalue; 128 | struct dir_entry *dirent; 129 | int len; 130 | 131 | int remoteunit = atoi(config.remoteunit); 132 | int remoteboot = atoi(config.remoteboot); 133 | 134 | /* Set up remote drive */ 135 | for (int i = 0; i < remoteunit; i++) { 136 | struct smb2_context *smb2; 137 | const char *shpath; 138 | if ((smb2 = connect_smb2_path(config.remote[i], &shpath)) == NULL) 139 | continue; 140 | 141 | struct smb2_stat_64 st; 142 | if (smb2_stat(smb2, shpath, &st) < 0 || st.smb2_type != SMB2_TYPE_DIRECTORY) { 143 | printf("%s is not directory.\n", config.remote[i]); 144 | continue; 145 | } 146 | rootpath[i] = config.remote[i]; 147 | printf("REMOTE%u: %s\n", i, config.remote[i]); 148 | } 149 | 150 | int id = remoteboot ? 1 : 0; 151 | 152 | /* Set up remote HDS */ 153 | for (int i = 0; i < countof(config.hds); i++, id++) { 154 | struct smb2_context *smb2; 155 | const char *shpath; 156 | if ((smb2 = connect_smb2_path(config.hds[i], &shpath)) == NULL) 157 | continue; 158 | 159 | struct smb2_stat_64 st; 160 | if (smb2_stat(smb2, shpath, &st) < 0 || st.smb2_type != SMB2_TYPE_FILE) { 161 | printf("File %s not found.\n", config.hds[i]); 162 | continue; 163 | } 164 | if ((diskinfo[id].sfh = smb2_open(smb2, shpath, O_RDWR)) == NULL) { 165 | printf("File %s open failure.\n", config.hds[i]); 166 | continue; 167 | } 168 | diskinfo[id].smb2 = smb2; 169 | diskinfo[id].size = st.smb2_size; 170 | printf("HDS%u: %s size=%lld\n", i, config.hds[i], st.smb2_size); 171 | } 172 | 173 | for (int i = 0; i < 7; i++) { 174 | diskinfo[i].sects = (diskinfo[i].size + SECTOR_SIZE - 1) / SECTOR_SIZE; 175 | } 176 | 177 | sysstatus = STAT_CONFIGURED; 178 | } 179 | 180 | //**************************************************************************** 181 | // WiFi connection task 182 | //**************************************************************************** 183 | 184 | void connect_task(void *params) 185 | { 186 | /* Set up WiFi connection */ 187 | 188 | xSemaphoreTake(remote_sem, portMAX_DELAY); 189 | connection(CONNECT_WIFI); 190 | if (sysstatus >= STAT_SMB2_CONNECTED) { 191 | vd_mount(); 192 | } 193 | xSemaphoreGive(remote_sem); 194 | xTaskNotify(main_th, 1, eSetBits); 195 | 196 | while (1) { 197 | uint32_t nvalue; 198 | 199 | xTaskNotifyWait(1, 0, &nvalue, portMAX_DELAY); 200 | if (!(nvalue & CONNECT_WAIT)) 201 | continue; 202 | xSemaphoreTake(remote_sem, portMAX_DELAY); 203 | connection(nvalue & CONNECT_MASK); 204 | xSemaphoreGive(remote_sem); 205 | } 206 | } 207 | 208 | //**************************************************************************** 209 | // Remote connection keepalive task 210 | //**************************************************************************** 211 | 212 | void keepalive_task(void *params) 213 | { 214 | while (1) { 215 | TickType_t delay; 216 | xSemaphoreTake(remote_sem, portMAX_DELAY); 217 | if (sysstatus >= STAT_SMB2_CONNECTED) { 218 | keepalive_smb2_all(); 219 | delay = pdMS_TO_TICKS(5 * 60 * 1000); 220 | } else { 221 | delay = pdMS_TO_TICKS(30 * 1000); 222 | } 223 | xSemaphoreGive(remote_sem); 224 | vTaskDelay(delay); 225 | 226 | extern char __HeapLimit; 227 | struct mallinfo mi = mallinfo(); 228 | printf("arena=%d used=%d free=%d", mi.arena, mi.uordblks, mi.fordblks); 229 | printf(" heapfree=%d\n", &__HeapLimit - (char *)sbrk(0)); 230 | 231 | time_t tt = (time_t)((boottime + to_us_since_boot(get_absolute_time())) / 1000000); 232 | struct tm *tm = localtime(&tt); 233 | printf("%04d/%02d/%02d %02d:%02d:%02d\n", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); 234 | 235 | static TaskStatus_t pxTaskStatusArray[8]; 236 | unsigned long ulTotalRunTime; 237 | int uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, count_of(pxTaskStatusArray), &ulTotalRunTime); 238 | printf("ID Task Name S Pr Stack\n"); 239 | for (int x = 0; x < uxArraySize; x++) { 240 | printf("%2u %-16s %c %2u %5u\n", 241 | pxTaskStatusArray[x].xTaskNumber, 242 | pxTaskStatusArray[x].pcTaskName, 243 | "RRBSD"[pxTaskStatusArray[x].eCurrentState], 244 | pxTaskStatusArray[x].uxCurrentPriority, 245 | pxTaskStatusArray[x].usStackHighWaterMark); 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/fileop.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | #ifndef _FILEOP_H_ 26 | #define _FILEOP_H_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | #include "main.h" 39 | #include "iconv_mini.h" 40 | 41 | //**************************************************************************** 42 | // Data types 43 | //**************************************************************************** 44 | 45 | typedef struct smb2_stat_64 TYPE_STAT; 46 | #define STAT_SIZE(st) ((st)->smb2_size) 47 | #define STAT_MTIME(st) ((st)->smb2_mtime) 48 | #define STAT_ISDIR(st) ((st)->smb2_type == SMB2_TYPE_DIRECTORY) 49 | 50 | typedef struct smb2dirent TYPE_DIRENT; 51 | #define DIRENT_NAME(d) ((char *)(d)->name) 52 | 53 | typedef uint64_t TYPE_DIR; 54 | #define DIR_BADDIR (uint64_t)0 55 | union smb2dd { 56 | struct { 57 | struct smb2dir *dir; 58 | struct smb2_context *smb2; 59 | }; 60 | uint64_t dd; 61 | }; 62 | #define dir2dir(dir) (((union smb2dd *)&dir)->dir) 63 | #define dir2smb2(dir) (((union smb2dd *)&dir)->smb2) 64 | 65 | typedef uint64_t TYPE_FD; 66 | #define FD_BADFD (uint64_t)0 67 | union smb2fd { 68 | struct { 69 | struct smb2fh *sfh; 70 | struct smb2_context *smb2; 71 | }; 72 | uint64_t fd; 73 | }; 74 | #define fd2sfh(fd) (((union smb2fd *)&fd)->sfh) 75 | #define fd2smb2(fd) (((union smb2fd *)&fd)->smb2) 76 | 77 | //**************************************************************************** 78 | // Endian functions 79 | //**************************************************************************** 80 | 81 | static inline uint16_t bswap16 (uint16_t x) 82 | { 83 | return (x >> 8) | (x << 8); 84 | } 85 | static inline uint32_t bswap32 (uint32_t x) 86 | { 87 | return (bswap16(x & 0xffff) << 16) | (bswap16(x >> 16)); 88 | } 89 | #define htobe16(x) bswap16(x) 90 | #define htobe32(x) bswap32(x) 91 | #define be16toh(x) bswap16(x) 92 | #define be32toh(x) bswap32(x) 93 | 94 | //**************************************************************************** 95 | // SJIS <-> UTF-8 conversion 96 | //**************************************************************************** 97 | 98 | static inline int FUNC_ICONV_S2U(char **src_buf, size_t *src_len, char **dst_buf, size_t *dst_len) 99 | { 100 | return iconv_s2u(src_buf, src_len, dst_buf, dst_len); 101 | } 102 | static inline int FUNC_ICONV_U2S(char **src_buf, size_t *src_len, char **dst_buf, size_t *dst_len) 103 | { 104 | return iconv_u2s(src_buf, src_len, dst_buf, dst_len); 105 | } 106 | 107 | //**************************************************************************** 108 | // File attributes 109 | //**************************************************************************** 110 | 111 | static inline int FUNC_FILEMODE_ATTR(TYPE_STAT *st) 112 | { 113 | int atr = st->smb2_type == SMB2_TYPE_FILE ? 0x20 : 0; // regular file 114 | atr |= st->smb2_type == SMB2_TYPE_DIRECTORY ? 0x10 : 0; // directory 115 | return atr; 116 | } 117 | static inline int FUNC_ATTR_FILEMODE(int attr, TYPE_STAT *st) 118 | { 119 | return 0; /* TBD */ 120 | } 121 | 122 | static inline int FUNC_CHMOD(int unit, int *err, const char *path, int mode) 123 | { 124 | return 0; /* TBD */ 125 | } 126 | 127 | //**************************************************************************** 128 | // Filesystem operations 129 | //**************************************************************************** 130 | 131 | static inline int FUNC_STAT(int unit, int *err, const char *path, TYPE_STAT *st) 132 | { 133 | const char *shpath; 134 | struct smb2_context *smb2 = path2smb2(path, &shpath); 135 | int r = smb2_stat(smb2, shpath, st); 136 | if (err) 137 | *err = -r; 138 | return r; 139 | } 140 | static inline int FUNC_MKDIR(int unit, int *err, const char *path) 141 | { 142 | const char *shpath; 143 | struct smb2_context *smb2 = path2smb2(path, &shpath); 144 | int r = smb2_mkdir(smb2, shpath); 145 | if (err) 146 | *err = -r; 147 | return r; 148 | } 149 | static inline int FUNC_RMDIR(int unit, int *err, const char *path) 150 | { 151 | const char *shpath; 152 | struct smb2_context *smb2 = path2smb2(path, &shpath); 153 | int r = smb2_rmdir(smb2, shpath); 154 | if (err) 155 | *err = -r; 156 | return r; 157 | } 158 | static inline int FUNC_RENAME(int unit, int *err, const char *pathold, const char *pathnew) 159 | { 160 | const char *shpath; 161 | const char *shpath2; 162 | struct smb2_context *smb2 = path2smb2(pathold, &shpath); 163 | path2smb2(pathnew, &shpath2); 164 | int r = smb2_rename(smb2, shpath, shpath2); 165 | if (err) 166 | *err = -r; 167 | return r; 168 | } 169 | static inline int FUNC_UNLINK(int unit, int *err, const char *path) 170 | { 171 | const char *shpath; 172 | struct smb2_context *smb2 = path2smb2(path, &shpath); 173 | int r = smb2_unlink(smb2, shpath); 174 | if (err) 175 | *err = -r; 176 | return r; 177 | } 178 | 179 | //**************************************************************************** 180 | // Directory operations 181 | //**************************************************************************** 182 | 183 | static inline TYPE_DIR FUNC_OPENDIR(int unit, int *err, const char *path) 184 | { 185 | union smb2dd dir = { .dd = DIR_BADDIR }; 186 | const char *shpath; 187 | struct smb2_context *smb2 = path2smb2(path, &shpath); 188 | dir.dir = smb2_opendir(smb2, shpath); 189 | if (dir.dir) { 190 | dir.smb2 = smb2; 191 | } 192 | if (err) 193 | *err = nterror_to_errno(smb2_get_nterror(smb2)); 194 | return dir.dd; 195 | } 196 | static inline TYPE_DIRENT *FUNC_READDIR(int unit, int *err, TYPE_DIR dir) 197 | { 198 | TYPE_DIRENT *d = smb2_readdir(dir2smb2(dir), dir2dir(dir)); 199 | if (err) 200 | *err = nterror_to_errno(smb2_get_nterror(dir2smb2(dir))); 201 | return d; 202 | } 203 | static inline int FUNC_CLOSEDIR(int unit, int *err, TYPE_DIR dir) 204 | { 205 | smb2_closedir(dir2smb2(dir), dir2dir(dir)); 206 | return 0; 207 | } 208 | 209 | //**************************************************************************** 210 | // File operations 211 | //**************************************************************************** 212 | 213 | static inline TYPE_FD FUNC_OPEN(int unit, int *err, const char *path, int flags) 214 | { 215 | union smb2fd fd = { .fd = FD_BADFD }; 216 | const char *shpath; 217 | struct smb2_context *smb2 = path2smb2(path, &shpath); 218 | fd.sfh = smb2_open(smb2, shpath, flags); 219 | if (fd.sfh) { 220 | fd.smb2 = smb2; 221 | } 222 | if (err) 223 | *err = nterror_to_errno(smb2_get_nterror(smb2)); 224 | return fd.fd; 225 | } 226 | static inline int FUNC_CLOSE(int unit, int *err, TYPE_FD fd) 227 | { 228 | int r = smb2_close(fd2smb2(fd), fd2sfh(fd)); 229 | if (err) 230 | *err = -r; 231 | return r; 232 | } 233 | static inline ssize_t FUNC_READ(int unit, int *err, TYPE_FD fd, void *buf, size_t count) 234 | { 235 | ssize_t r = smb2_read(fd2smb2(fd), fd2sfh(fd), buf, count); 236 | if (err) 237 | *err = -r; 238 | return r; 239 | } 240 | static inline ssize_t FUNC_WRITE(int unit, int *err, TYPE_FD fd, const void *buf, size_t count) 241 | { 242 | ssize_t res = 0; 243 | while (count > 0) { 244 | int c = count > 1024 ? 1024 : count; 245 | ssize_t r = smb2_write(fd2smb2(fd), fd2sfh(fd), buf, c); 246 | if (r < 0) { 247 | res = r; 248 | break; 249 | } 250 | if (r == 0) 251 | break; 252 | buf += r; 253 | count -= r; 254 | res += r; 255 | } 256 | if (err) 257 | *err = -res; 258 | return res; 259 | } 260 | static inline int FUNC_FTRUNCATE(int unit, int *err, TYPE_FD fd, off_t length) 261 | { 262 | int r = smb2_ftruncate(fd2smb2(fd), fd2sfh(fd), length); 263 | if (err) 264 | *err = -r; 265 | return r; 266 | } 267 | static inline off_t FUNC_LSEEK(int unit, int *err, TYPE_FD fd, off_t offset, int whence) 268 | { 269 | uint64_t cur; 270 | off_t r = smb2_lseek(fd2smb2(fd), fd2sfh(fd), offset, whence, &cur); 271 | if (err) 272 | *err = -r; 273 | return r; 274 | } 275 | static inline int FUNC_FSTAT(int unit, int *err, TYPE_FD fd, TYPE_STAT *st) 276 | { 277 | int r = smb2_fstat(fd2smb2(fd), fd2sfh(fd), st); 278 | if (err) 279 | *err = -r; 280 | return r; 281 | } 282 | 283 | static inline int FUNC_FILEDATE(int unit, int *err, TYPE_FD fd, uint16_t time, uint16_t date) 284 | { 285 | struct tm tm; 286 | tm.tm_sec = (time << 1) & 0x3f; 287 | tm.tm_min = (time >> 5) & 0x3f; 288 | tm.tm_hour = (time >> 11) & 0x1f; 289 | tm.tm_mday = date & 0x1f; 290 | tm.tm_mon = ((date >> 5) & 0xf) - 1; 291 | tm.tm_year = ((date >> 9) & 0x7f) + 80; 292 | tm.tm_isdst = 0; 293 | time_t tt = mktime(&tm); 294 | 295 | struct smb2_timeval tv[2]; 296 | tv[0].tv_sec = tv[1].tv_sec = tt; 297 | tv[0].tv_usec = tv[1].tv_usec = 0; 298 | int r = smb2_futimes(fd2smb2(fd), fd2sfh(fd), tv); 299 | if (err) 300 | *err = -r; 301 | return r; 302 | } 303 | 304 | //**************************************************************************** 305 | // Misc functions 306 | //**************************************************************************** 307 | 308 | static inline int FUNC_STATFS(int unit, int *err, const char *path, uint64_t *total, uint64_t *free) 309 | { 310 | struct smb2_statvfs sf; 311 | const char *shpath; 312 | struct smb2_context *smb2 = path2smb2(path, &shpath); 313 | smb2_statvfs(smb2, shpath, &sf); 314 | *total = sf.f_blocks * sf.f_bsize; 315 | *free = sf.f_bfree * sf.f_bsize; 316 | return 0; 317 | } 318 | 319 | #endif /* _FILEOP_H_ */ 320 | -------------------------------------------------------------------------------- /src/config_file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023 Yuichi Nakamura 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 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "main.h" 35 | #include "vd_command.h" 36 | #include "virtual_disk.h" 37 | #include "config_file.h" 38 | 39 | //**************************************************************************** 40 | // Configuration file template 41 | //**************************************************************************** 42 | 43 | __asm__ ( 44 | ".section .rodata\n" 45 | ".balign 4\n" 46 | ".global config_template\n" 47 | "config_template:\n" 48 | ".incbin \"config.tmpl.txt\"\n" 49 | ".byte 0\n" 50 | ".balign 4\n" 51 | ); 52 | 53 | extern char config_template[]; 54 | 55 | //**************************************************************************** 56 | // Configuration data 57 | //**************************************************************************** 58 | 59 | char configtxt[2048]; 60 | struct config_data config; 61 | 62 | #define CF_HIDDEN 1 63 | #define CF_URL 2 64 | 65 | const struct config_item { 66 | const char *item; 67 | const char *defval; 68 | char *value; 69 | size_t valuesz; 70 | int flag; 71 | } config_items[] = { 72 | { "WIFI_SSID:", NULL, 73 | config.wifi_ssid, sizeof(config.wifi_ssid), 0 }, 74 | { "WIFI_PASSWORD:", NULL, 75 | config.wifi_passwd, sizeof(config.wifi_passwd), CF_HIDDEN }, 76 | 77 | { "SMB2_USERNAME:", NULL, 78 | config.smb2_user, sizeof(config.smb2_user), 0 }, 79 | { "SMB2_PASSWORD:", NULL, 80 | config.smb2_passwd, sizeof(config.smb2_passwd), CF_HIDDEN }, 81 | { "SMB2_WORKGROUP:", "WORKGROUP", 82 | config.smb2_workgroup, sizeof(config.smb2_workgroup), 0 }, 83 | { "SMB2_SERVER:", NULL, 84 | config.smb2_server, sizeof(config.smb2_server), 0 }, 85 | 86 | { "REMOTE_BOOT:", "0", 87 | config.remoteboot, sizeof(config.remoteboot), 0 }, 88 | { "REMOTE_UNIT:", "0", 89 | config.remoteunit, sizeof(config.remoteunit), 0 }, 90 | { "REMOTE0:", NULL, 91 | config.remote[0], sizeof(config.remote[0]), CF_URL }, 92 | { "REMOTE1:", NULL, 93 | config.remote[1], sizeof(config.remote[1]), CF_URL }, 94 | { "REMOTE2:", NULL, 95 | config.remote[2], sizeof(config.remote[2]), CF_URL }, 96 | { "REMOTE3:", NULL, 97 | config.remote[3], sizeof(config.remote[3]), CF_URL }, 98 | { "REMOTE4:", NULL, 99 | config.remote[4], sizeof(config.remote[4]), CF_URL }, 100 | { "REMOTE5:", NULL, 101 | config.remote[5], sizeof(config.remote[5]), CF_URL }, 102 | { "REMOTE6:", NULL, 103 | config.remote[6], sizeof(config.remote[6]), CF_URL }, 104 | { "REMOTE7:", NULL, 105 | config.remote[7], sizeof(config.remote[7]), CF_URL }, 106 | 107 | { "HDS0:", NULL, 108 | config.hds[0], sizeof(config.hds[0]), CF_URL }, 109 | { "HDS1:", NULL, 110 | config.hds[1], sizeof(config.hds[1]), CF_URL }, 111 | { "HDS2:", NULL, 112 | config.hds[2], sizeof(config.hds[2]), CF_URL }, 113 | { "HDS3:", NULL, 114 | config.hds[3], sizeof(config.hds[3]), CF_URL }, 115 | 116 | { "TZ:", "JST-9", 117 | config.tz, sizeof(config.tz), 0 }, 118 | { "TADJUST:", "2", 119 | config.tadjust, sizeof(config.tadjust), 0 }, 120 | { "FASTCONNECT:", "0", 121 | config.fastconnect, sizeof(config.fastconnect), 0 }, 122 | }; 123 | 124 | //**************************************************************************** 125 | // Configuration functions 126 | //**************************************************************************** 127 | 128 | #define CONFIG_ITEMS (sizeof(config_items) / sizeof(config_items[0])) 129 | 130 | #define CONFIG_FLASH_OFFSET (0x1f0000) 131 | #define CONFIG_FLASH_ADDR ((uint8_t *)(0x10000000 + CONFIG_FLASH_OFFSET)) 132 | #define CONFIG_FLASH_MAGIC_v3 "X68000Z Remote Drive Config v3" 133 | #define CONFIG_FLASH_MAGIC "X68000Z Remote Drive Config v4" 134 | 135 | void config_read(void) 136 | { 137 | int i; 138 | for (i = 0; i < CONFIG_ITEMS; i++) { 139 | const struct config_item *c = &config_items[i]; 140 | memset(c->value, 0, c->valuesz); 141 | } 142 | 143 | const uint8_t *config_flash_addr = CONFIG_FLASH_ADDR; 144 | const char *p = &config_flash_addr[32]; 145 | if (memcmp(&config_flash_addr[0], CONFIG_FLASH_MAGIC, sizeof(CONFIG_FLASH_MAGIC)) == 0) { 146 | for (i = 0; i < CONFIG_ITEMS; i++) { 147 | const struct config_item *c = &config_items[i]; 148 | memcpy(c->value, p, c->valuesz); 149 | p += c->valuesz; 150 | } 151 | } else if (memcmp(&config_flash_addr[0], CONFIG_FLASH_MAGIC_v3, sizeof(CONFIG_FLASH_MAGIC_v3)) == 0) { 152 | for (i = 0; i < CONFIG_ITEMS - 1; i++) { 153 | const struct config_item *c = &config_items[i]; 154 | memcpy(c->value, p, c->valuesz); 155 | p += c->valuesz; 156 | } 157 | strcpy(config.fastconnect, "0"); 158 | } else { 159 | for (i = 0; i < CONFIG_ITEMS; i++) { 160 | const struct config_item *c = &config_items[i]; 161 | if (c->defval) 162 | strcpy(c->value, c->defval); 163 | } 164 | } 165 | 166 | for (i = 0; i < 8; i++) { 167 | for (char *p = config.remote[i]; *p != '\0'; p++) { 168 | if (*p == '/') 169 | *p = '\\'; 170 | } 171 | } 172 | for (i = 0; i < 4; i++) { 173 | for (char *p = config.hds[i]; *p != '\0'; p++) { 174 | if (*p == '/') 175 | *p = '\\'; 176 | } 177 | } 178 | 179 | memset(configtxt, 0, sizeof(configtxt)); 180 | snprintf(configtxt, sizeof(configtxt) - 1 , config_template, 181 | config.wifi_ssid, 182 | config.smb2_user, config.smb2_workgroup, config.smb2_server, 183 | config.hds[0], 184 | config.hds[1], 185 | config.hds[2], 186 | config.hds[3], 187 | config.remoteboot, config.remoteunit, 188 | config.remote[0], 189 | config.remote[1], 190 | config.remote[2], 191 | config.remote[3], 192 | config.remote[4], 193 | config.remote[5], 194 | config.remote[6], 195 | config.remote[7], 196 | config.tz, 197 | config.tadjust, 198 | config.fastconnect); 199 | 200 | for (i = 0; i < 8; i++) { 201 | for (char *p = config.remote[i]; *p != '\0'; p++) { 202 | if (*p == '\\') 203 | *p = '/'; 204 | } 205 | } 206 | for (i = 0; i < 4; i++) { 207 | for (char *p = config.hds[i]; *p != '\0'; p++) { 208 | if (*p == '\\') 209 | *p = '/'; 210 | } 211 | } 212 | } 213 | 214 | void config_write(void) 215 | { 216 | uint8_t flash_data[SECTOR_SIZE * 4]; 217 | memcpy(&flash_data[0], CONFIG_FLASH_MAGIC, sizeof(CONFIG_FLASH_MAGIC)); 218 | char *p = &flash_data[32]; 219 | for (int i = 0; i < CONFIG_ITEMS; i++) { 220 | const struct config_item *c = &config_items[i]; 221 | memcpy(p, c->value, c->valuesz); 222 | p += c->valuesz; 223 | } 224 | 225 | uint32_t stat = save_and_disable_interrupts(); 226 | flash_range_erase(CONFIG_FLASH_OFFSET, FLASH_SECTOR_SIZE * 4); 227 | flash_range_program(CONFIG_FLASH_OFFSET, flash_data, sizeof(flash_data)); 228 | restore_interrupts(stat); 229 | } 230 | 231 | void config_erase(void) 232 | { 233 | uint32_t stat = save_and_disable_interrupts(); 234 | flash_range_erase(CONFIG_FLASH_OFFSET, FLASH_SECTOR_SIZE * 4); 235 | restore_interrupts(stat); 236 | } 237 | 238 | void config_parse(uint8_t *buf) 239 | { 240 | char *p = buf; 241 | while (*p != '\0') { 242 | int i; 243 | if (*p < ' ') { 244 | p++; 245 | continue; 246 | } 247 | for (i = 0; i < CONFIG_ITEMS; i++) { 248 | const struct config_item *c = &config_items[i]; 249 | if (strncmp(p, c->item, strlen(c->item)) == 0) { 250 | p += strlen(c->item); 251 | while (*p == ' ') 252 | p++; 253 | char *q = c->value; 254 | if (c->flag & CF_HIDDEN) { 255 | char *r = p; 256 | while (*r == '*') 257 | r++; 258 | if (*r < ' ') 259 | continue; 260 | } 261 | for (int j = 0; j < c->valuesz - 1; j++) { 262 | if (*p < ' ') 263 | break; 264 | if (c->flag & CF_URL) { 265 | char c = *p++; 266 | if (c == '"') 267 | continue; 268 | *q++ = (c == '\\') ? '/' : c; 269 | } else { 270 | *q++ = *p++; 271 | } 272 | } 273 | *q = '\0'; 274 | p++; 275 | break; 276 | } 277 | } 278 | if (i >= CONFIG_ITEMS) { 279 | while (*p >= ' ') 280 | p++; 281 | while (*p < ' ' && *p != '\0') 282 | p++; 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/vd_command.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | #include 26 | #include 27 | #include 28 | 29 | #include "pico/cyw43_arch.h" 30 | #include "pico/stdio.h" 31 | #include "pico/stdlib.h" 32 | #include "hardware/watchdog.h" 33 | 34 | #include "smb2.h" 35 | #include "libsmb2.h" 36 | #include "libsmb2-raw.h" 37 | 38 | typedef unsigned int nfds_t; 39 | struct pollfd 40 | { 41 | int fd; 42 | short events; 43 | short revents; 44 | }; 45 | int lwip_poll(struct pollfd *fds, nfds_t nfds, int timeout); 46 | 47 | #include "main.h" 48 | #include "virtual_disk.h" 49 | #include "vd_command.h" 50 | #include "config_file.h" 51 | #include "remoteserv.h" 52 | #include "fileop.h" 53 | 54 | //**************************************************************************** 55 | // Callback functions 56 | //**************************************************************************** 57 | 58 | static struct res_wifi_scan wifi_scan_data; 59 | static int16_t wifi_rssi[countof(wifi_scan_data.ssid)]; 60 | 61 | static int scan_result(void *env, const cyw43_ev_scan_result_t *result) 62 | { 63 | if (result == NULL) 64 | return 0; 65 | 66 | printf("ssid: %-16s rssi:%3d chan:%3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %u\n", 67 | result->ssid, result->rssi, result->channel, 68 | result->bssid[0], result->bssid[1], result->bssid[2], result->bssid[3], result->bssid[4], result->bssid[5], 69 | result->auth_mode); 70 | 71 | if (strlen(result->ssid) == 0) { 72 | return 0; 73 | } 74 | for (int i = 0; i < wifi_scan_data.n_items; i++) { 75 | if (strcmp(wifi_scan_data.ssid[i], result->ssid) == 0) { 76 | return 0; 77 | } 78 | } 79 | 80 | if (wifi_scan_data.n_items < countof(wifi_scan_data.ssid)) { 81 | wifi_rssi[wifi_scan_data.n_items] = result->rssi; 82 | strcpy(wifi_scan_data.ssid[wifi_scan_data.n_items++], result->ssid); 83 | } 84 | 85 | for (int i = 0; i < wifi_scan_data.n_items; i++) { 86 | int maxrssi = -100; 87 | int maxndx = -1; 88 | for (int j = i; j < wifi_scan_data.n_items; j++) { 89 | if (wifi_rssi[j] > maxrssi) { 90 | maxrssi = wifi_rssi[j]; 91 | maxndx = j; 92 | } 93 | } 94 | if (maxndx >= 0 && maxndx != i) { 95 | int tmp = wifi_rssi[i]; 96 | wifi_rssi[i] = wifi_rssi[maxndx]; 97 | wifi_rssi[maxndx] = tmp; 98 | char tmps[32]; 99 | strcpy(tmps, wifi_scan_data.ssid[i]); 100 | strcpy(wifi_scan_data.ssid[i], wifi_scan_data.ssid[maxndx]); 101 | strcpy(wifi_scan_data.ssid[maxndx], tmps); 102 | } 103 | } 104 | return 0; 105 | } 106 | 107 | static volatile bool smb2_enum_finished; 108 | static struct res_smb2_enum *smb2_enum_ptr; 109 | 110 | static void se_cb(struct smb2_context *smb2, int status, 111 | void *command_data, void *private_data) 112 | { 113 | struct srvsvc_NetrShareEnum_rep *rep = command_data; 114 | 115 | if (smb2_enum_ptr == NULL) 116 | return; 117 | 118 | if (status) { 119 | printf("failed to enumerate shares (%s) %s\n", 120 | strerror(-status), smb2_get_error(smb2)); 121 | smb2_enum_ptr->status = -1; 122 | smb2_enum_finished = true; 123 | return; 124 | } 125 | 126 | printf("Number of shares:%d\n", rep->ses.ShareInfo.Level1.EntriesRead); 127 | for (int i = 0; i < rep->ses.ShareInfo.Level1.EntriesRead; i++) { 128 | if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_DISKTREE && 129 | !(rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & SHARE_TYPE_HIDDEN)) { 130 | if (smb2_enum_ptr->n_items < countof(smb2_enum_ptr->share)) { 131 | smb2_enum_ptr->share[smb2_enum_ptr->n_items][sizeof(smb2_enum_ptr->share[0]) - 1] = '\0'; 132 | strncpy(smb2_enum_ptr->share[smb2_enum_ptr->n_items++], 133 | rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].netname.utf8, sizeof(smb2_enum_ptr->share[0]) - 1); 134 | } 135 | } 136 | 137 | printf("%-20s %-20s", rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].netname.utf8, 138 | rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].remark.utf8); 139 | if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_DISKTREE) { 140 | printf(" DISKTREE"); 141 | } 142 | if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_PRINTQ) { 143 | printf(" PRINTQ"); 144 | } 145 | if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_DEVICE) { 146 | printf(" DEVICE"); 147 | } 148 | if ((rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & 3) == SHARE_TYPE_IPC) { 149 | printf(" IPC"); 150 | } 151 | if (rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & SHARE_TYPE_TEMPORARY) { 152 | printf(" TEMPORARY"); 153 | } 154 | if (rep->ses.ShareInfo.Level1.Buffer->share_info_1[i].type & SHARE_TYPE_HIDDEN) { 155 | printf(" HIDDEN"); 156 | } 157 | printf("\n"); 158 | } 159 | 160 | smb2_free_data(smb2, rep); 161 | smb2_enum_ptr->status = 0; 162 | smb2_enum_finished = true; 163 | } 164 | 165 | //**************************************************************************** 166 | // vd_command service 167 | //**************************************************************************** 168 | 169 | int vd_command(uint8_t *cbuf, uint8_t *rbuf) 170 | { 171 | DPRINTF2("----VDCommand: 0x%04x\n", (cbuf[0] << 8) | cbuf[1]); 172 | int rsize = -1; 173 | 174 | switch ((cbuf[0] << 8) | cbuf[1]) { 175 | case CMD_GETINFO: 176 | { 177 | struct cmd_getinfo *cmd = (struct cmd_getinfo *)cbuf; 178 | struct res_getinfo *res = (struct res_getinfo *)rbuf; 179 | rsize = sizeof(*res); 180 | 181 | memset(res, 0, rsize); 182 | if (boottime != 0 && atoi(config.tadjust) != 0) { 183 | time_t tt = (time_t)((boottime + to_us_since_boot(get_absolute_time())) / 1000000); 184 | tt += atoi(config.tadjust); 185 | struct tm *tm = localtime(&tt); 186 | res->year = htobe16(tm->tm_year + 1900); 187 | res->mon = tm->tm_mon + 1; 188 | res->day = tm->tm_mday; 189 | res->hour = tm->tm_hour; 190 | res->min = tm->tm_min; 191 | res->sec = tm->tm_sec; 192 | res->unit = atoi(config.remoteunit); 193 | } 194 | res->version = PROTO_VERSION; 195 | strncpy(res->verstr, GIT_REPO_VERSION, sizeof(res->verstr) - 1); 196 | break; 197 | } 198 | 199 | case CMD_GETCONFIG: 200 | { 201 | struct cmd_getconfig *cmd = (struct cmd_getconfig *)cbuf; 202 | struct res_getconfig *res = (struct res_getconfig *)rbuf; 203 | rsize = sizeof(*res); 204 | res->data = config; 205 | break; 206 | } 207 | 208 | case CMD_SETCONFIG: 209 | { 210 | struct cmd_setconfig *cmd = (struct cmd_setconfig *)cbuf; 211 | struct res_setconfig *res = (struct res_setconfig *)rbuf; 212 | config = cmd->data; 213 | res->status = 0; 214 | rsize = sizeof(*res); 215 | xTaskNotify(connect_th, cmd->mode | CONNECT_WAIT, eSetBits); 216 | break; 217 | } 218 | 219 | case CMD_GETSTATUS: 220 | { 221 | struct cmd_getstatus *cmd = (struct cmd_getstatus *)cbuf; 222 | struct res_getstatus *res = (struct res_getstatus *)rbuf; 223 | rsize = sizeof(*res); 224 | res->status = sysstatus; 225 | break; 226 | } 227 | 228 | case CMD_FLASHCONFIG: 229 | { 230 | struct cmd_flashconfig *cmd = (struct cmd_flashconfig *)cbuf; 231 | struct res_flashconfig *res = (struct res_flashconfig *)rbuf; 232 | config_write(); 233 | res->status = 0; 234 | rsize = sizeof(*res); 235 | break; 236 | } 237 | 238 | case CMD_FLASHCLEAR: 239 | { 240 | struct cmd_flashclear *cmd = (struct cmd_flashclear *)cbuf; 241 | struct res_flashclear *res = (struct res_flashclear *)rbuf; 242 | config_erase(); 243 | config_read(); 244 | res->status = 0; 245 | rsize = sizeof(*res); 246 | xTaskNotify(connect_th, CONNECT_WAIT, eSetBits); 247 | break; 248 | } 249 | 250 | case CMD_REBOOT: 251 | { 252 | // reboot by watchdog 253 | watchdog_enable(500, 1); 254 | while (1) 255 | ; 256 | } 257 | 258 | case CMD_WIFI_SCAN: 259 | { 260 | struct cmd_wifi_scan *cmd = (struct cmd_wifi_scan *)cbuf; 261 | struct res_wifi_scan *res = (struct res_wifi_scan *)rbuf; 262 | 263 | rsize = sizeof(*res); 264 | if (cmd->clear) { 265 | memset(&wifi_scan_data, 0, sizeof(wifi_scan_data)); 266 | } 267 | 268 | printf("scan status %d\n", cyw43_wifi_scan_active(&cyw43_state)); 269 | 270 | cyw43_wifi_scan_options_t scan_options = {0}; 271 | int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result); 272 | if (err != 0) { 273 | printf("Failed to start scan: %d\n", err); 274 | res->status = -1; 275 | break; 276 | } 277 | printf("Performing wifi scan\n"); 278 | while (cyw43_wifi_scan_active(&cyw43_state)) { 279 | vTaskDelay(pdMS_TO_TICKS(200)); 280 | } 281 | *res = wifi_scan_data; 282 | res->status = 0; 283 | break; 284 | } 285 | 286 | case CMD_SMB2_ENUM: 287 | { 288 | struct cmd_smb2_enum *cmd = (struct cmd_smb2_enum *)cbuf; 289 | struct res_smb2_enum *res = (struct res_smb2_enum *)rbuf; 290 | struct pollfd pfd; 291 | struct smb2_context *smb2ipc; 292 | 293 | rsize = sizeof(*res); 294 | memset(res, 0, rsize); 295 | res->status = -1; 296 | 297 | if ((smb2ipc = connect_smb2("IPC$")) == NULL) { 298 | break; 299 | } 300 | 301 | smb2_enum_finished = false; 302 | smb2_enum_ptr = res; 303 | if (smb2_share_enum_async(smb2ipc, 1, se_cb, NULL) != 0) { 304 | printf("smb2_share_enum failed. %s\n", smb2_get_error(smb2ipc)); 305 | goto errout_enum; 306 | } 307 | 308 | while (!smb2_enum_finished) { 309 | pfd.fd = smb2_get_fd(smb2ipc); 310 | pfd.events = smb2_which_events(smb2ipc); 311 | 312 | if (lwip_poll(&pfd, 1, 1000) < 0) { 313 | printf("Poll failed"); 314 | goto errout_enum; 315 | } 316 | if (pfd.revents == 0) { 317 | continue; 318 | } 319 | if (smb2_service(smb2ipc, pfd.revents) < 0) { 320 | printf("smb2_service failed with : %s\n", smb2_get_error(smb2ipc)); 321 | goto errout_enum; 322 | } 323 | } 324 | errout_enum: 325 | smb2_enum_finished = false; 326 | smb2_enum_ptr = NULL; 327 | disconnect_smb2(smb2ipc); 328 | break; 329 | } 330 | 331 | case CMD_SMB2_LIST: 332 | { 333 | struct cmd_smb2_list *cmd = (struct cmd_smb2_list *)cbuf; 334 | struct res_smb2_list *res = (struct res_smb2_list *)rbuf; 335 | struct smb2_context *smb2; 336 | 337 | rsize = sizeof(*res); 338 | memset(res, 0, rsize); 339 | 340 | char path[256]; 341 | char *dst_buf = path; 342 | size_t dst_len = sizeof(path) - 1; 343 | char *src_buf = cmd->path; 344 | size_t src_len = strlen(cmd->path); 345 | if (iconv_s2u(&src_buf, &src_len, &dst_buf, &dst_len) < 0) { 346 | dst_buf = path; 347 | } 348 | *dst_buf = '\0'; 349 | 350 | res->status = -1; 351 | 352 | if ((smb2 = connect_smb2(cmd->share)) == NULL) { 353 | break; 354 | } 355 | 356 | struct smb2dir *dir; 357 | struct smb2dirent *ent; 358 | char *p = res->list; 359 | char *q = &res->list[sizeof(res->list)]; 360 | 361 | res->status = -2; 362 | 363 | dir = smb2_opendir(smb2, path); 364 | if (dir != NULL) { 365 | while ((ent = smb2_readdir(smb2, dir))) { 366 | if (ent->st.smb2_type == SMB2_TYPE_DIRECTORY || 367 | ent->st.smb2_type == SMB2_TYPE_FILE) { 368 | if (strcmp(ent->name, ".") == 0 || strcmp(ent->name, "..") == 0) 369 | continue; 370 | 371 | /* 拡張子 .HDS のファイルのみ返す */ 372 | if (ent->st.smb2_type == SMB2_TYPE_FILE) { 373 | int len; 374 | if ((len = strlen(ent->name)) <= 4) 375 | continue; 376 | if (!(((ent->name[len - 4] == '.' && 377 | (ent->name[len - 3] & 0xdf) == 'H' && 378 | (ent->name[len - 2] & 0xdf) == 'D' && 379 | (ent->name[len - 1] & 0xdf) == 'S')) || 380 | ((ent->name[len - 4] == '.' && 381 | (ent->name[len - 3] & 0xdf) == 'M' && 382 | (ent->name[len - 2] & 0xdf) == 'O' && 383 | (ent->name[len - 1] & 0xdf) == 'S')))) 384 | continue; 385 | } 386 | 387 | printf("%s\n", ent->name); 388 | char buf[128]; 389 | char *dst_buf = buf; 390 | size_t dst_len = sizeof(buf) - 1; 391 | char *src_buf = (char *)ent->name; 392 | size_t src_len = strlen(ent->name); 393 | if (iconv_u2s(&src_buf, &src_len, &dst_buf, &dst_len) < 0) { 394 | continue; 395 | } 396 | *dst_buf = '\0'; 397 | int l = strlen(buf); 398 | if (p + l + 3 < q) { 399 | strcpy(p, buf); 400 | if (ent->st.smb2_type == SMB2_TYPE_DIRECTORY) { 401 | strcat(p, "/"); 402 | } 403 | p += strlen(p) + 1; 404 | } 405 | } 406 | } 407 | smb2_closedir(smb2, dir); 408 | *p++ = '\0'; 409 | res->status = 0; 410 | } 411 | 412 | disconnect_smb2(smb2); 413 | break; 414 | } 415 | 416 | default: 417 | break; 418 | } 419 | 420 | return rsize; 421 | } 422 | -------------------------------------------------------------------------------- /driver/settingui.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Yuichi Nakamura (@yunkya2) 3 | * 4 | * The MIT License (MIT) 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 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "config.h" 33 | #include "vd_command.h" 34 | #include "settinguisub.h" 35 | 36 | //**************************************************************************** 37 | // Global variables 38 | //**************************************************************************** 39 | 40 | int sysstatus = STAT_WIFI_DISCONNECTED; 41 | int remoteunit = 0; 42 | struct config_data config 43 | #ifdef XTEST 44 | = { 45 | .tz = "JST-9", 46 | .tadjust= "2", 47 | .fastconnect = "0", 48 | } 49 | #endif 50 | ; 51 | 52 | //**************************************************************************** 53 | // Definition 54 | //**************************************************************************** 55 | 56 | #define isvisible(n) (((itemtbl[n].stat) & 0xf) <= sysstatus && \ 57 | !((itemtbl[n].stat & 0x20) && \ 58 | (remoteunit <= ((itemtbl[n].stat & 0xf00) >> 8)))) 59 | #define istabstop(n) ((itemtbl[n].stat) & 0x10) 60 | #define issetconf(n) ((itemtbl[n].stat) & 0x40) 61 | #define isupdconf(n) ((itemtbl[n].stat) & 0x80) 62 | 63 | int flash_config(struct itemtbl *it, void *v); 64 | int flash_clear(struct itemtbl *it, void *v); 65 | 66 | //**************************************************************************** 67 | // Menu data 68 | //**************************************************************************** 69 | 70 | static struct numlist_opt opt_bool = { 0, 1 }; 71 | static struct numlist_opt opt_rmtunit = { 0, 4 }; 72 | static struct numlist_opt opt_tadjust = { 0, 4 }; 73 | 74 | struct itemtbl itemtbl[] = { 75 | { 0x010, 4, 4, 17, "SSID", 76 | "WiFi 接続先の SSID を設定します", 77 | "WiFi 接続先の SSID を選択してください", 78 | "#a #b (選択) #e (確定) #f (前に戻る)", 79 | 16, 28, config.wifi_ssid, sizeof(config.wifi_ssid), input_wifiap }, 80 | { 0x040, 4, 5, 18, "PASSWORD", 81 | "WiFi 接続先のパスワードを設定します", 82 | "WiFi 接続先のパスワードを入力してください", 83 | "#e (確定) #f (前に戻る) #g (パスワードを表示)", 84 | 16, 16, config.wifi_passwd, sizeof(config.wifi_passwd), input_passwd, (void *)CONNECT_WIFI }, 85 | 86 | { 0x012, 4, 8, -1, "USERNAME", 87 | "Windows ファイル共有のユーザ名を設定します", 88 | "Windows ファイル共有のユーザ名を入力してください", 89 | "#e (確定) #f (前に戻る)", 90 | 16, 16, config.smb2_user, sizeof(config.smb2_user), input_entry }, 91 | { 0x002, 4, 9, -1, "PASSWORD", 92 | "Windows ファイル共有のパスワードを設定します", 93 | "Windows ファイル共有のパスワードを入力してください", 94 | "#e (確定) #f (前に戻る) #g (パスワードを表示)", 95 | 16, 16, config.smb2_passwd, sizeof(config.smb2_passwd), input_passwd }, 96 | { 0x002, 4, 10, -1, "WORKGROUP", 97 | "Windows ファイル共有のワークグループを設定します", 98 | "Windows ファイル共有のワークグループを入力してください", 99 | "#e (確定) #f (前に戻る)", 100 | 16, 16, config.smb2_workgroup, sizeof(config.smb2_workgroup), input_entry }, 101 | { 0x042, 4, 11, -1, "SERVER", 102 | "Windows ファイル共有のサーバ名を設定します", 103 | "Windows ファイル共有のサーバ名または IP アドレスを入力してください", 104 | "#e (確定) #f (前に戻る)", 105 | 16, 28, config.smb2_server, sizeof(config.smb2_server), input_entry, (void *)CONNECT_SMB2 }, 106 | 107 | { 0x014, 4, 14, -1, "HDS0", 108 | "HDS ファイル 0 を設定します", 109 | "HDS ファイル 0 を選択してください (空文字列にすると HDS ファイルを割り当てません)", 110 | "#a #b (選択) #e (確定) #f (前に戻る)", 111 | 16, 76, config.hds[0], sizeof(config.hds[0]), input_dirfile, (void *)1}, 112 | { 0x004, 4, 15, -1, "HDS1", 113 | "HDS ファイル 1 を設定します", 114 | "HDS ファイル 1 を選択してください (空文字列にすると HDS ファイルを割り当てません)", 115 | "#a #b (選択) #e (確定) #f (前に戻る)", 116 | 16, 76, config.hds[1], sizeof(config.hds[1]), input_dirfile, (void *)1}, 117 | { 0x004, 4, 16, -1, "HDS2", 118 | "HDS ファイル 2 を設定します", 119 | "HDS ファイル 2 を選択してください (空文字列にすると HDS ファイルを割り当てません)", 120 | "#a #b (選択) #e (確定) #f (前に戻る)", 121 | 16, 76, config.hds[2], sizeof(config.hds[2]), input_dirfile, (void *)1}, 122 | { 0x004, 4, 17, -1, "HDS3", 123 | "HDS ファイル 3 を設定します", 124 | "HDS ファイル 3 を選択してください (空文字列にすると HDS ファイルを割り当てません)", 125 | "#a #b (選択) #e (確定) #f (前に戻る)", 126 | 16, 76, config.hds[3], sizeof(config.hds[3]), input_dirfile, (void *)1}, 127 | 128 | { 0x014, 4, 20, -1, "RMTBOOT", 129 | "リモートドライブからの起動を行うかどうかを設定します", 130 | "リモートドライブからの起動を行うかどうかを選択してください (0=行わない/1=行う)", 131 | "#a #b (選択) #e (確定) #f (前に戻る)", 132 | 16, 76, config.remoteboot, sizeof(config.remoteboot), input_numlist, &opt_bool }, 133 | { 0x084, 4, 21, -1, "RMTUNIT", 134 | "リモートドライブの個数を設定します (0-4)", 135 | "リモートドライブの個数を選択してください (0=リモートドライブは使用しない)", 136 | "#a #b (選択) #e (確定) #f (前に戻る)", 137 | 16, 76, config.remoteunit, sizeof(config.remoteunit), input_numlist, &opt_rmtunit }, 138 | { 0x024, 4, 22, -1, "REMOTE0", 139 | "リモートドライブ 0 のファイル共有のパス名を設定します", 140 | "リモートドライブ 0 のファイル共有のパス名を選択してください (ディレクトリ内で \"./\" を選択)", 141 | "#a #b (選択) #e (確定) #f (前に戻る)", 142 | 16, 76, config.remote[0], sizeof(config.remote[0]), input_dirfile }, 143 | { 0x124, 4, 23, -1, "REMOTE1", 144 | "リモートドライブ 1 のファイル共有のパス名を設定します", 145 | "リモートドライブ 1 のファイル共有のパス名を選択してください (ディレクトリ内で \"./\" を選択)", 146 | "#a #b (選択) #e (確定) #f (前に戻る)", 147 | 16, 76, config.remote[1], sizeof(config.remote[1]), input_dirfile }, 148 | { 0x224, 4, 24, -1, "REMOTE2", 149 | "リモートドライブ 2 のファイル共有のパス名を設定します", 150 | "リモートドライブ 2 のファイル共有のパス名を選択してください (ディレクトリ内で \"./\" を選択)", 151 | "#a #b (選択) #e (確定) #f (前に戻る)", 152 | 16, 76, config.remote[2], sizeof(config.remote[2]), input_dirfile }, 153 | { 0x324, 4, 25, -1, "REMOTE3", 154 | "リモートドライブ 3 のファイル共有のパス名を設定します", 155 | "リモートドライブ 3 のファイル共有のパス名を選択してください (ディレクトリ内で \"./\" を選択)", 156 | "#a #b (選択) #e (確定) #f (前に戻る)", 157 | 16, 76, config.remote[3], sizeof(config.remote[3]), input_dirfile }, 158 | 159 | { 0x014, 4, 27, 19, " 設定終了 ", 160 | "設定を反映して終了します", 161 | "設定を反映して終了します よろしいですか?", 162 | "#h (設定を反映する) #i #f (前に戻る)", 163 | -1, -1, NULL, 0, flash_config }, 164 | 165 | { 0x010, 52, 4, 0, "TZ", 166 | "Windows から取得した時刻のタイムゾーンを設定します", 167 | "Windows から取得した時刻のタイムゾーンを入力してください", 168 | "#e (確定) #f (前に戻る)", 169 | 64, 16, config.tz, sizeof(config.tz), input_entry }, 170 | { 0x000, 52, 5, 1, "TADJUST", 171 | "Windows から取得した時刻を X68000Z に設定する際のオフセット値を設定します", 172 | "Windows から取得した時刻を設定する際のオフセット値を選択してください (0=設定しない)", 173 | "#a #b (選択) #e (確定) #f (前に戻る)", 174 | 64, 8, config.tadjust, sizeof(config.tadjust), input_numlist, &opt_tadjust }, 175 | { 0x000, 52, 6, 1, "FASTCONN", 176 | "リモートドライブサービスの接続を高速化するかどうかを設定します", 177 | "起動時にリモートドライブサービスの認識に失敗する場合のみ 1 を設定してください", 178 | "(HDSのイメージサイズが正しく取得できないためformat.xの装置初期化の際は 0 にしてください)", 179 | 64, 4, config.fastconnect, sizeof(config.fastconnect), input_numlist, &opt_bool }, 180 | 181 | { 0x080, 82, 27, 16, "設定クリア", 182 | "保存されている設定内容をクリアします", 183 | "保存されている設定内容をクリアします よろしいですか?", 184 | "#h (クリアする) #i #f (前に戻る)", 185 | -1, -1, NULL, 0, flash_clear }, 186 | }; 187 | 188 | #define N_ITEMTBL (countof(itemtbl)) 189 | 190 | //**************************************************************************** 191 | // Top view 192 | //**************************************************************************** 193 | 194 | int topview(void) 195 | { 196 | bool wstat = false; 197 | bool sstat = false; 198 | 199 | _iocs_b_color(3); 200 | _iocs_b_locate(0, 3); 201 | _iocs_b_clr_ed(); 202 | 203 | switch (sysstatus) { 204 | default: 205 | /* fall through */ 206 | 207 | case STAT_SMB2_CONNECTED: 208 | drawmsg(38, 7, 3, "接続済"); 209 | sstat = true; 210 | 211 | drawmsg(4, 13, 3, "HDS (SCSI ディスクイメージ) 設定"); 212 | drawframe3(2, 14, 92, 4, 2, 10); 213 | 214 | drawmsg(4, 19, 3, "リモートドライブ設定"); 215 | drawframe3(2, 20, 92, remoteunit + 2, 2, 10); 216 | 217 | drawframe3(2, 27, 14, 1, 2, -1); 218 | /* fall through */ 219 | 220 | case STAT_SMB2_CONNECTING: 221 | if (!sstat) { 222 | drawmsg(38, 7, 2, "接続中"); 223 | sstat = true; 224 | } 225 | /* fall through */ 226 | 227 | case STAT_WIFI_CONNECTED: 228 | drawmsg(38, 3, 3, "接続済"); 229 | wstat = true; 230 | 231 | drawmsg(4, 7, 3, "Windows ファイル共有設定"); 232 | if (!sstat) 233 | drawmsg(38, 7, 2, "未接続"); 234 | drawframe3(2, 8, 44, 4, 2, 10); 235 | /* fall through */ 236 | 237 | case STAT_WIFI_CONNECTING: 238 | if (!wstat) { 239 | drawmsg(38, 3, 2, "接続中"); 240 | wstat = true; 241 | } 242 | /* fall through */ 243 | 244 | case STAT_WIFI_DISCONNECTED: 245 | drawmsg(4, 3, 3, "WiFi 設定"); 246 | if (!wstat) 247 | drawmsg(38, 3, 2, "未接続"); 248 | drawframe3(2, 4, 44, 2, 2, 10); 249 | 250 | drawmsg(52, 3, 3, "その他の設定"); 251 | drawframe3(50, 4, 44, 3, 2, 10); 252 | 253 | drawframe3(80, 27, 14, 1, 2, -1); 254 | } 255 | drawframe2(1, 28, 94, 4, 1, -1); 256 | 257 | for (int i = 0; i < N_ITEMTBL; i++) { 258 | struct itemtbl *it = &itemtbl[i]; 259 | if (isvisible(i)) { 260 | drawmsg(it->x, it->y, 3, it->msg); 261 | if (it->xd >= 0) { 262 | drawvalue(3, it, it->value, it->func == input_passwd); 263 | } 264 | } 265 | } 266 | } 267 | 268 | //**************************************************************************** 269 | // Flash command 270 | //**************************************************************************** 271 | 272 | int flash_config(struct itemtbl *it, void *v) 273 | { 274 | int res = 0; 275 | 276 | while (1) { 277 | /* キー入力処理 */ 278 | int k = keyinp(-1); 279 | int c = k & 0xff; 280 | if (c == 'y' || c == 'Y') { // Y 281 | _iocs_b_putmes(3, 3, 29, 89, "設定を反映しました X68000 Zの電源を一度切って再投入してください"); 282 | _iocs_b_putmes(3, 3, 30, 89, ""); 283 | #ifndef XTEST 284 | { 285 | struct cmd_setconfig cmd; 286 | struct res_setconfig res; 287 | cmd.command = CMD_SETCONFIG; 288 | cmd.mode = CONNECT_NONE; 289 | cmd.data = config; 290 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 291 | } 292 | { 293 | struct cmd_flashconfig cmd; 294 | struct res_flashconfig res; 295 | cmd.command = CMD_FLASHCONFIG; 296 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 297 | } 298 | { 299 | struct cmd_reboot cmd; 300 | struct res_reboot res; 301 | cmd.command = CMD_REBOOT; 302 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 303 | } 304 | while (1) 305 | ; 306 | #endif 307 | res = 1; 308 | break; 309 | } else if (c == 'n' || c == 'N' || c == '\x1b') { // N or ESC 310 | break; 311 | } 312 | } 313 | 314 | return res; 315 | } 316 | 317 | int flash_clear(struct itemtbl *it, void *v) 318 | { 319 | int res = 0; 320 | 321 | while (1) { 322 | /* キー入力処理 */ 323 | int k = keyinp(-1); 324 | int c = k & 0xff; 325 | if (c == 'y' || c == 'Y') { // Y 326 | #ifndef XTEST 327 | { 328 | struct cmd_flashclear cmd; 329 | struct res_flashclear res; 330 | cmd.command = CMD_FLASHCLEAR; 331 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 332 | } 333 | { 334 | struct cmd_getconfig cmd; 335 | struct res_getconfig res; 336 | cmd.command = CMD_GETCONFIG; 337 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 338 | config = res.data; 339 | } 340 | #endif 341 | res = 1; 342 | break; 343 | } else if (c == 'n' || c == 'N' || c == '\x1b') { // N or ESC 344 | break; 345 | } 346 | } 347 | 348 | return res; 349 | } 350 | 351 | //**************************************************************************** 352 | // Main 353 | //**************************************************************************** 354 | 355 | int main() 356 | { 357 | #ifdef XTEST 358 | printf("\x1b[2J\x1b[>1h"); 359 | fflush(stdout); 360 | #endif 361 | _iocs_os_curof(); 362 | 363 | _iocs_b_clr_st(); 364 | char title[200]; 365 | strcpy(title, "Remote Drive Service for X68000 Z Version " GIT_REPO_VERSION); 366 | title[88] = '\0'; 367 | drawframe2(0, 0, strlen(title) + 6, 3, 1, -1); 368 | drawmsg(3, 1, 3, title); 369 | 370 | #ifndef XTEST 371 | com_init(); 372 | 373 | { 374 | char buf[512]; 375 | _iocs_s_readext(0, 1, 6, 1, buf); 376 | if (memcmp(buf, "X68SCSI1", 8) != 0 || 377 | memcmp(&buf[16], "X68000ZRemoteDrv", 16) != 0) { 378 | drawframe2(1, 28, 94, 4, 1, -1); 379 | _iocs_b_putmes(3, 3, 29, 89, "X68000 Z Remote Drive Service が見つかりません"); 380 | _iocs_b_putmes(3, 3, 30, 89, "リモートドライブ ファームウェアを書き込んだ Raspberry Pi Pico W を接続してください"); 381 | while (1) 382 | ; 383 | } 384 | } 385 | 386 | { 387 | struct cmd_getinfo cmd; 388 | struct res_getinfo res; 389 | cmd.command = CMD_GETINFO; 390 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 391 | if (res.version != PROTO_VERSION) { 392 | drawframe2(1, 28, 94, 4, 1, -1); 393 | _iocs_b_putmes(3, 3, 29, 89, "X68000 Z Remote Drive Service のバージョンが合致しません"); 394 | _iocs_b_putmes(3, 3, 30, 89, "同一バージョンのレスキューディスクを使用してください"); 395 | while (1) 396 | ; 397 | } 398 | } 399 | 400 | { 401 | struct cmd_getconfig cmd; 402 | struct res_getconfig res; 403 | cmd.command = CMD_GETCONFIG; 404 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 405 | config = res.data; 406 | } 407 | remoteunit = atoi(config.remoteunit); 408 | 409 | { 410 | struct cmd_getstatus cmd; 411 | struct res_getstatus res; 412 | cmd.command = CMD_GETSTATUS; 413 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 414 | sysstatus = res.status; 415 | } 416 | #endif 417 | 418 | /***********************************************************/ 419 | 420 | int n = 0; 421 | int pren = -1; 422 | bool update = true; 423 | while (1) { 424 | if (update) { 425 | topview(); 426 | while (!isvisible(n)) { 427 | n = (n + N_ITEMTBL - 1) % N_ITEMTBL; 428 | } 429 | pren = -1; 430 | update = false; 431 | } 432 | struct itemtbl *it = &itemtbl[n]; 433 | drawmsg(it->x, it->y, 10, it->msg); 434 | if (pren != n) { 435 | _iocs_b_putmes(3, 3, 29, 89, it->help1); 436 | drawhelp(3, 3, 30, 89, "#a #b (選択) #e (確定)"); 437 | pren = n; 438 | } 439 | 440 | /* キー入力を待ちながら2秒おきに設定状態をチェックする */ 441 | int k; 442 | do { 443 | #ifndef XTEST 444 | struct cmd_getstatus cmd; 445 | struct res_getstatus res; 446 | cmd.command = CMD_GETSTATUS; 447 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 448 | if (sysstatus != res.status) { 449 | sysstatus = res.status; 450 | update = true; 451 | break; 452 | } 453 | #endif 454 | } while ((k = keyinp(200)) < 0); 455 | if (update) 456 | continue; 457 | 458 | int c = k & 0xff; 459 | if (c == '\r') { /* CR */ 460 | if (sysstatus == STAT_SMB2_CONNECTING || sysstatus == STAT_WIFI_CONNECTING) { 461 | continue; 462 | } 463 | if (it->func == input_wifiap && sysstatus == STAT_SMB2_CONNECTED) { 464 | continue; 465 | } 466 | drawmsg(it->x, it->y, 7, it->msg); 467 | if (it->func == NULL) { 468 | break; 469 | } 470 | if (it->func) { 471 | if (it->help2) _iocs_b_putmes(3, 3, 29, 89, it->help2); 472 | if (it->help3) drawhelp(3, 3, 30, 89, it->help3); 473 | int res = it->func(it, it->opt); 474 | _iocs_b_putmes(3, 3, 29, 89, it->help1); 475 | drawhelp(3, 3, 30, 89, "#a #b (選択) #e (確定)"); 476 | remoteunit = atoi(config.remoteunit); 477 | if (res == 1) { 478 | update = isupdconf(n); 479 | if (issetconf(n)) { 480 | #ifndef XTEST 481 | struct cmd_setconfig cmd; 482 | struct res_setconfig res; 483 | cmd.command = CMD_SETCONFIG; 484 | cmd.mode = (int)it->opt; 485 | cmd.data = config; 486 | com_cmdres(&cmd, sizeof(cmd), &res, sizeof(res)); 487 | #endif 488 | } 489 | n++; 490 | if (n >= N_ITEMTBL || !isvisible(n)) { 491 | n--; 492 | } 493 | } 494 | } 495 | #ifdef XTEST 496 | } else if (c == '\x1b') { /* ESC */ 497 | break; 498 | #endif 499 | } 500 | 501 | drawmsg(it->x, it->y, 3, it->msg); 502 | if (c == '\x0e' || k == 0x3e00) { /* CTRL+N or ↓ */ 503 | do { 504 | n = (n + 1) % N_ITEMTBL; 505 | } while (!isvisible(n)); 506 | } else if (c == '\x10' || k == 0x3c00) { /* CTRL+P or ↑ */ 507 | do { 508 | n = (n + N_ITEMTBL - 1) % N_ITEMTBL; 509 | } while (!isvisible(n)); 510 | } else if (c == '\x02' || k == 0x3b00) { /* CTRL+B or ← */ 511 | n = it->xn >= 0 ? it->xn : n; 512 | } else if (c == '\x06' || k == 0x3d00) { /* CTRL+F or → */ 513 | n = it->xn >= 0 ? it->xn : n; 514 | } else if (c == '\t') { /* TAB */ 515 | do { 516 | n = (n + 1) % N_ITEMTBL; 517 | } while (!isvisible(n) || !istabstop(n)); 518 | #ifdef XTEST 519 | } else if (c == '+') { 520 | sysstatus = (sysstatus + 1) > STAT_CONFIGURED ? STAT_CONFIGURED : sysstatus + 1; 521 | update = true; 522 | } else if (c == '-') { 523 | sysstatus = (sysstatus - 1) < STAT_WIFI_DISCONNECTED ? STAT_WIFI_DISCONNECTED : sysstatus - 1; 524 | update = true; 525 | #endif 526 | } 527 | } 528 | 529 | #ifndef XTEST 530 | while (1) 531 | ; 532 | #endif 533 | #ifdef XTEST 534 | printf("\x1b[0m\x1b[2J\x1b[>1l"); 535 | _iocs_os_curon(); 536 | #endif 537 | 538 | return 0; 539 | } 540 | -------------------------------------------------------------------------------- /src/virtual_disk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2023,2024,2025 Yuichi Nakamura 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 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "hardware/watchdog.h" 33 | #include "pico/cyw43_arch.h" 34 | 35 | #include "smb2.h" 36 | #include "libsmb2.h" 37 | 38 | #include "main.h" 39 | #include "virtual_disk.h" 40 | #include "config_file.h" 41 | #include "remoteserv.h" 42 | #include "fileop.h" 43 | 44 | #include "scsiremote.inc" 45 | #include "bootloader.inc" 46 | #include "hdsboot.inc" 47 | #include "settingui.inc" 48 | 49 | //**************************************************************************** 50 | // Global variables 51 | //**************************************************************************** 52 | 53 | int debuglevel = 0; 54 | 55 | //**************************************************************************** 56 | // Static variables 57 | //**************************************************************************** 58 | 59 | static int remoteunit; 60 | static bool remoteboot; 61 | static bool fastconnect; 62 | 63 | //**************************************************************************** 64 | // for debugging 65 | //**************************************************************************** 66 | 67 | void DPRINTF(int level, char *fmt, ...) 68 | { 69 | if (debuglevel >= level) { 70 | va_list ap; 71 | va_start(ap, fmt); 72 | vprintf(fmt, ap); 73 | va_end(ap); 74 | } 75 | } 76 | 77 | //**************************************************************************** 78 | // BPB 79 | //**************************************************************************** 80 | 81 | #define lsb_hword(x) ((x) & 0xff), (((x) >> 8) & 0xff) 82 | #define lsb_word(x) ((x) & 0xff), (((x) >> 8) & 0xff), \ 83 | (((x) >> 16) & 0xff), (((x) >> 24) & 0xff) 84 | 85 | #define MEDIA_TYPE 0xf8 86 | 87 | static const uint8_t boot_sector[] = { 88 | 0xeb, 0x58, 0x90, // +0 JmpBoot 89 | 'M', 'S', 'W', 'I', 'N', '4', '.', '1', // +3 OEMName 90 | lsb_hword(512), // +11 BytsPerSec 91 | (CLUSTER_SIZE / SECTOR_SIZE), // +13 SecPerClus 92 | lsb_hword(32), // +14 RsvdSecCnt 93 | 2, // +16 NumFATs 94 | lsb_hword(0), // +17 RootEntCnt 95 | lsb_hword(0), // +19 TotSec16 96 | MEDIA_TYPE, // +21 Media 97 | lsb_hword(0), // +22 FATSz16 98 | lsb_hword(0x3f), // +24 SecPerTrk 99 | lsb_hword(0xff), // +26 NumHeads 100 | lsb_word(0), // +28 HiddSec 101 | lsb_word(VOLUME_SECTOR_COUNT), // +32 TotSec32 102 | lsb_word(FAT_SECTORS), // +36 FATSz32 103 | lsb_hword(0), // +40 ExtFlags 104 | lsb_hword(0), // +42 FSVer 105 | lsb_word(2), // +44 RootClus 106 | lsb_hword(1), // +48 FSInfo 107 | lsb_hword(6), // +50 BkBootSec 108 | 0,0,0,0,0,0,0,0,0,0,0,0, // +52 Reserved 109 | 0x80, // +64 DrvNum 110 | 0, // +65 Reserved 111 | 0x29, // +66 BootSig 112 | lsb_word(0x12345678), // +67 VolID 113 | 'N', 'O', ' ', 'N', 'A', 'M', 'E', ' ', ' ', ' ', ' ', // +71 VolLab 114 | 'F', 'A', 'T', '3', '2', ' ', ' ', ' ', // +82 FilSysType 115 | 0xeb, 0xfe // while(1) // +90 BootCode32 116 | }; 117 | 118 | static const uint8_t fsinfo1[] = { 119 | lsb_word(0x41615252) // +0 LeadSig 120 | }; 121 | static const uint8_t fsinfo2[] = { 122 | lsb_word(0x61417272), // +484 StrucSig 123 | lsb_word(0xffffffff), // +488 Free_Count 124 | lsb_word(0xffffffff), // +492 Nxt_Free 125 | lsb_word(0), // +496 reserved 126 | lsb_word(0), // +500 reserved 127 | lsb_word(0), // +504 reserved 128 | lsb_word(0xaa550000) // +508 TrailSig 129 | }; 130 | 131 | //**************************************************************************** 132 | // Directory entry 133 | //**************************************************************************** 134 | 135 | #define ATTR_READONLY 0x01 136 | #define ATTR_HIDDEN 0x02 137 | #define ATTR_SYSTEM 0x04 138 | #define ATTR_VOLUME_LABEL 0x08 139 | #define ATTR_DIR 0x10 140 | #define ATTR_ARCHIVE 0x20 141 | 142 | struct dir_entry { 143 | uint8_t name[11]; 144 | uint8_t attr; 145 | uint8_t ntRes; 146 | uint8_t crtTimeTenth; 147 | uint16_t crtTime; 148 | uint16_t crtDate; 149 | uint16_t lstAccDate; 150 | uint16_t fstClusHI; 151 | uint16_t wrtTime; 152 | uint16_t wrtDate; 153 | uint16_t fstClusLO; 154 | uint32_t fileSize; 155 | }; 156 | 157 | void init_dir_entry(struct dir_entry *entry, const char *fn, 158 | int attr, int ntres, int cluster, int len) 159 | { 160 | memcpy(entry->name, fn, 11); 161 | entry->attr = (attr == 0) ? ATTR_ARCHIVE : attr; 162 | entry->ntRes = ntres; 163 | entry->crtTimeTenth = 0; 164 | entry->crtTime = entry->wrtTime = 165 | ((12 << 11) | (0 << 5) | (0 >> 1)); 166 | entry->crtDate = entry->wrtDate = entry->lstAccDate = 167 | (((2025 - 1980) << 9) | (1 << 5) | (1)); 168 | entry->fstClusHI = cluster >> 16; 169 | entry->fstClusLO = cluster & 0xffff; 170 | entry->fileSize = len; 171 | } 172 | 173 | //**************************************************************************** 174 | // Virtual FAT32 functions 175 | //**************************************************************************** 176 | 177 | static uint32_t fat[SECTOR_SIZE]; 178 | static uint8_t rootdir[32 * 8]; 179 | static uint8_t x68zdir[32 * 8]; 180 | static uint8_t imagedir[32 * 16]; 181 | static uint8_t pscsiini[256]; 182 | static int imagedir_init = false; 183 | 184 | static void vd_sync(void) 185 | { 186 | static bool synced = false; 187 | if (!synced) { 188 | xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); 189 | synced = true; 190 | } 191 | } 192 | 193 | int vd_init(void) 194 | { 195 | uint32_t nvalue; 196 | struct dir_entry *dirent; 197 | int len; 198 | 199 | setenv("TZ", config.tz, true); 200 | 201 | remoteunit = atoi(config.remoteunit); 202 | remoteboot = atoi(config.remoteboot); 203 | fastconnect = atoi(config.fastconnect); 204 | 205 | if (strlen(config.wifi_ssid) == 0 || strlen(config.smb2_server) == 0) { 206 | /* not configured */ 207 | for (int i = 0; i < 6; i++) { 208 | diskinfo[i].type = DTYPE_REMOTEBOOT; 209 | diskinfo[i].size = 0x800; 210 | } 211 | } else { 212 | /* Set up remote drive boot */ 213 | int id = 0; 214 | if (remoteboot) { 215 | diskinfo[id].type = DTYPE_REMOTEBOOT; 216 | diskinfo[id].size = 0x20000; 217 | id++; 218 | } 219 | 220 | /* Set up remote HDS */ 221 | for (int i = 0; i < countof(config.hds); i++, id++) { 222 | if (strlen(config.hds[i]) == 0) 223 | continue; 224 | diskinfo[id].type = DTYPE_HDS; 225 | diskinfo[id].size = 0xfffffe00; /* tentative size */ 226 | } 227 | } 228 | 229 | /* SCSI ID 6 : for remote communication */ 230 | diskinfo[6].type = DTYPE_REMOTECOMM; 231 | diskinfo[6].size = 0x80000000; 232 | 233 | for (int i = 0; i < 7; i++) { 234 | diskinfo[i].sects = (diskinfo[i].size + SECTOR_SIZE - 1) / SECTOR_SIZE; 235 | } 236 | 237 | strcpy(pscsiini, "[pscsi]\r\n"); 238 | for (int i = 0; i < 7; i++) { 239 | if (diskinfo[i].type != DTYPE_NOTUSED) { 240 | char str[32]; 241 | sprintf(str, "ID%d=image/%d%s.hds\r\n", 242 | i, i, 243 | diskinfo[i].type == DTYPE_REMOTEBOOT ? "remote" : 244 | (diskinfo[i].type == DTYPE_HDS ? "rmthds" : "rmtcom")); 245 | strcat(pscsiini, str); 246 | } 247 | } 248 | 249 | /* Initialize FAT */ 250 | 251 | memset(fat, 0, sizeof(fat)); 252 | fat[0] = 0x0fffff00u | MEDIA_TYPE; 253 | fat[1] = 0x0fffffff; 254 | fat[2] = 0x0ffffff8; /* cluster 2: root directory */ 255 | fat[3] = 0x0fffffff; /* cluster 3: X68000Z directory */ 256 | fat[4] = 0x0fffffff; /* cluster 4: pscsi.ini */ 257 | fat[5] = 0x0fffffff; /* cluster 5: log.txt */ 258 | fat[6] = 0x0fffffff; /* cluster 6: config.txt */ 259 | fat[7] = 0x0fffffff; /* cluster 7: X68000Z/image directory */ 260 | 261 | /* Initialize root directory */ 262 | 263 | memset(rootdir, 0, sizeof(rootdir)); 264 | dirent = (struct dir_entry *)rootdir; 265 | init_dir_entry(dirent++, "LOG TXT", 0, 0x18, 5, LOGSIZE); 266 | init_dir_entry(dirent++, "CONFIG TXT", 0, 0x18, 6, strlen(configtxt)); 267 | init_dir_entry(dirent++, "X68000Z ", ATTR_DIR, 0, 3, 0); 268 | 269 | #if 0 270 | if (!fastconnect) 271 | vd_sync(); 272 | #endif 273 | 274 | /* Initialize "X68000Z" directory */ 275 | 276 | memset(x68zdir, 0, sizeof(x68zdir)); 277 | dirent = (struct dir_entry *)x68zdir; 278 | init_dir_entry(dirent++, ". ", ATTR_DIR, 0, 3, 0); 279 | init_dir_entry(dirent++, ".. ", ATTR_DIR, 0, 0, 0); 280 | init_dir_entry(dirent++, "PSCSI INI", 0, 0x18, 4, strlen(pscsiini)); 281 | init_dir_entry(dirent++, "IMAGE ", ATTR_DIR, 0x18, 7, 0); 282 | 283 | return 0; 284 | } 285 | 286 | uint8_t vdbuf_read[(512 - 16) * 8 * 4]; 287 | uint8_t vdbuf_write[(512 - 16) * 8 * 4]; 288 | int vdbuf_rpages; 289 | int vdbuf_rcnt; 290 | 291 | struct vdbuf_header vdbuf_header; 292 | 293 | int vd_read_block(uint32_t lba, uint8_t *buf) 294 | { 295 | memset(buf, 0, 512); 296 | 297 | if (lba < 0x20) { 298 | if (lba == 0 || lba == 6) { 299 | // BPB 300 | memcpy(buf, boot_sector, sizeof(boot_sector)); 301 | buf[0x1fe] = 0x55; 302 | buf[0x1ff] = 0xaa; 303 | } else if (lba == 1) { 304 | // FSINFO 305 | memcpy(buf, fsinfo1, sizeof(fsinfo1)); 306 | memcpy(&buf[484], fsinfo2, sizeof(fsinfo2)); 307 | } 308 | return 0; 309 | } 310 | 311 | if (lba < 0x4020) { 312 | // FAT 313 | lba -= 0x20; 314 | if (lba >= 0x2000) { 315 | lba -= 0x2000; 316 | } 317 | if (lba == 0) { 318 | // FAT for directory and small files 319 | memcpy(buf, fat, SECTOR_SIZE); 320 | } else if (lba >= 0x400) { 321 | // "disk0~6.hds"ファイル用のFATデータを作る 322 | uint32_t *lbuf = (uint32_t *)buf; 323 | int id = (lba - 0x400) / 0x400; 324 | lba %= 0x400; 325 | if (diskinfo[id].type != DTYPE_NOTUSED) { 326 | // 1セクタ分のFAT領域(512/4=128エントリ)が占めるディスク領域 (128*32kB = 4MB) 327 | int fatdsz = FATENTS_SECT * CLUSTER_SIZE; 328 | // ファイルに使用するFAT領域のセクタ数(1セクタ未満切り捨て) 329 | int fatsects = diskinfo[id].size / fatdsz; 330 | // 1セクタに満たない分のFATエントリ数 331 | int fatmod = (diskinfo[id].size % fatdsz) / CLUSTER_SIZE; 332 | // アクセスしようとしているFAT領域先頭のクラスタ番号 333 | int clsno = 0x20000 + id * 0x20000 + FATENTS_SECT * lba; 334 | if (lba < fatsects) { 335 | // アクセスしようとしているFAT領域のセクタはすべて使用中 336 | for (int i = 0; i < FATENTS_SECT; i++) { 337 | lbuf[i] = clsno + i + 1; // クラスタチェインを作る 338 | } 339 | } else if (lba == fatsects) { 340 | // アクセスしようとしているFAT領域のセクタは部分的に使われている 341 | for (int i = 0; i < fatmod; i++) { 342 | lbuf[i] = clsno + i + 1; // クラスタチェインを作る 343 | } 344 | lbuf[fatmod] = 0x0fffffff; // クラスタ末尾 345 | } 346 | } 347 | } 348 | return 0; 349 | } 350 | 351 | if (lba == 0x4020) { 352 | // Root directory 353 | memcpy(buf, rootdir, sizeof(rootdir)); 354 | return 0; 355 | } 356 | if (lba == 0x4060) { 357 | // "X68000Z" directory 358 | memcpy(buf, x68zdir, sizeof(x68zdir)); 359 | return 0; 360 | } 361 | if (lba == 0x40a0) { 362 | // "pscsi.ini" file 363 | strcpy(buf, pscsiini); 364 | return 0; 365 | } 366 | 367 | if (lba == 0x40e0 || lba == 0x40e1) { 368 | // "log.txt" file 369 | memcpy(buf, &log_txt[(lba - 0x40e0) * SECTOR_SIZE], SECTOR_SIZE); 370 | return 0; 371 | } 372 | 373 | if (lba >= 0x4120 && lba < 0x4124) { 374 | // "config.txt" file 375 | memcpy(buf, &configtxt[(lba - 0x4120) * SECTOR_SIZE], SECTOR_SIZE); 376 | return 0; 377 | } 378 | 379 | if (lba == 0x4160) { 380 | // "X68000Z/image" directory 381 | if (!imagedir_init) { 382 | /* Lazy initialization of "X68000Z/image" directory */ 383 | struct dir_entry *dirent; 384 | vd_sync(); 385 | memset(imagedir, 0, sizeof(imagedir)); 386 | dirent = (struct dir_entry *)imagedir; 387 | init_dir_entry(dirent++, ". ", ATTR_DIR, 0, 4, 0); 388 | init_dir_entry(dirent++, ".. ", ATTR_DIR, 0, 0, 0); 389 | for (int i = 0; i < 7; i++) { 390 | struct diskinfo *di = &diskinfo[i]; 391 | if (di->type != DTYPE_NOTUSED) { 392 | char fn[16]; 393 | sprintf(fn, "%d%s HDS", i, 394 | diskinfo[i].type == DTYPE_REMOTEBOOT ? "REMOTE" : 395 | (diskinfo[i].type == DTYPE_HDS ? "RMTHDS" : "RMTCOM")); 396 | init_dir_entry(dirent++, fn, 0, 0x18, 0x20000 + 0x20000 * i, diskinfo[i].size); 397 | } 398 | } 399 | imagedir_init = true; 400 | } 401 | memcpy(buf, imagedir, sizeof(imagedir)); 402 | return 0; 403 | } 404 | 405 | if (lba >= 0x00803fa0) { 406 | // "disk0~6.hds" file read 407 | lba -= 0x00803fa0; 408 | int id = lba / 0x800000; 409 | lba %= 0x800000; 410 | if (diskinfo[id].type == DTYPE_NOTUSED) 411 | return -1; 412 | if (lba >= diskinfo[id].sects) 413 | return -1; 414 | DPRINTF3("disk %d: read 0x%x\n", id, lba); 415 | 416 | vd_sync(); 417 | 418 | if (diskinfo[id].type == DTYPE_HDS && diskinfo[id].sfh != NULL) { 419 | if (lba == 2) { 420 | // boot loader 421 | memcpy(buf, hdsboot, sizeof(hdsboot)); 422 | return 0; 423 | } 424 | if (lba == 0x20 || lba == 0x21) { 425 | lba -= 0x20 - 2; 426 | } 427 | if (hds_cache_read(diskinfo[id].smb2, diskinfo[id].sfh, lba, buf) < 0) 428 | return -1; 429 | return 0; 430 | } 431 | 432 | if (diskinfo[id].type == DTYPE_REMOTEBOOT || 433 | diskinfo[id].type == DTYPE_REMOTECOMM) { 434 | if (lba == 0) { 435 | // SCSI disk signature 436 | memcpy(buf, "X68SCSI1", 8); 437 | memcpy(&buf[16], "X68000ZRemoteDrv", 16); 438 | return 0; 439 | } else if (lba == 2) { 440 | // boot loader 441 | memcpy(buf, bootloader, sizeof(bootloader)); 442 | if (diskinfo[id].type == DTYPE_REMOTEBOOT) 443 | buf[5] = remoteboot ? sysstatus : 0; 444 | return 0; 445 | } 446 | } 447 | if (diskinfo[id].type == DTYPE_REMOTEBOOT || 448 | (!remoteboot && diskinfo[id].type == DTYPE_REMOTECOMM)) { 449 | if (lba == 4) { 450 | // SCSI partition signature 451 | memcpy(buf, "X68K", 4); 452 | for (int i = 0; i < remoteunit; i++) { 453 | memcpy(buf + 16 + i * 16, "Human68k", 8); 454 | } 455 | return 0; 456 | } else if (lba >= (0x0c00 / 512) && lba < (0x4000 / 512)) { 457 | // SCSI device driver 458 | lba -= 0xc00 / 512; 459 | if (lba <= sizeof(scsiremote) / 512) { 460 | memcpy(buf, &scsiremote[lba * 512], 512); 461 | } 462 | return 0; 463 | } 464 | } 465 | if (diskinfo[id].type == DTYPE_REMOTEBOOT) { 466 | if (lba >= (0x8000 / 512) && lba < (0x20000 / 512)) { 467 | // HUMAN.SYS 468 | lba -= 0x8000 / 512; 469 | uint64_t cur; 470 | static uint32_t humanlbamax = (uint32_t)-1; 471 | if (lba <= humanlbamax && diskinfo[id].sfh == NULL) { 472 | char human[256]; 473 | strcpy(human, rootpath[0]); 474 | strcat(human, "/HUMAN.SYS"); 475 | const char *shpath; 476 | diskinfo[id].smb2 = path2smb2(human, &shpath); 477 | char *p = strchr(human, '/') + 1; 478 | if ((diskinfo[id].sfh = smb2_open(diskinfo[id].smb2, p, O_RDONLY)) == NULL) { 479 | DPRINTF1("HUMAN.SYS open failure.\n"); 480 | } else { 481 | DPRINTF1("HUMAN.SYS opened.\n"); 482 | } 483 | } 484 | if (diskinfo[id].sfh != NULL && 485 | smb2_lseek(diskinfo[id].smb2, diskinfo[id].sfh, lba * 512, SEEK_SET, &cur) >= 0) { 486 | if (smb2_read(diskinfo[id].smb2, diskinfo[id].sfh, buf, 512) != 512) { 487 | smb2_close(diskinfo[id].smb2, diskinfo[id].sfh); 488 | diskinfo[id].sfh = NULL; 489 | humanlbamax = lba; 490 | DPRINTF1("HUMAN.SYS closed.\n"); 491 | } 492 | } 493 | return 0; 494 | } 495 | } 496 | if (diskinfo[id].type == DTYPE_REMOTECOMM) { 497 | if (lba >= (0x20000 / 512) && lba < (0x40000 / 512)) { 498 | // settingui.bin 499 | lba -= 0x20000 / 512; 500 | if (lba <= sizeof(settingui) / 512) { 501 | memcpy(buf, &settingui[lba * 512], 512); 502 | } 503 | return 0; 504 | } else if (lba >= (0x40000 / 512)) { 505 | int page = vdbuf_rcnt + (lba % 8); 506 | struct vdbuf *b = (struct vdbuf *)buf; 507 | b->header = vdbuf_header; 508 | b->header.maxpage = vdbuf_rpages; 509 | b->header.page = page; 510 | memcpy(b->buf, &vdbuf_read[page * (512 - 16)], sizeof(b->buf)); 511 | if ((lba % 8) == 7) { 512 | vdbuf_rcnt += 8; 513 | } 514 | return 0; 515 | } 516 | } 517 | } 518 | 519 | return -1; 520 | } 521 | 522 | static int configtxtlen = 0; 523 | 524 | int vd_write_block(uint32_t lba, uint8_t *buf) 525 | { 526 | if (lba == 0x4020) { 527 | // Root directory 528 | if (memcmp(&buf[32], "CONFIG TXT", 11) == 0) { 529 | configtxtlen = *(uint32_t *)&buf[60]; 530 | } 531 | return 0; 532 | } 533 | 534 | if (lba >= 0x4120 && lba < 0x4124) { 535 | // "config.txt" file update 536 | memcpy(&configtxt[(lba - 0x4120) * SECTOR_SIZE], buf, SECTOR_SIZE); 537 | if (configtxtlen > 0 && 538 | (lba - 0x4120) == (configtxtlen - 1) / SECTOR_SIZE) { 539 | configtxt[configtxtlen] = '\0'; 540 | configtxt[sizeof(configtxt) - 1] = '\0'; 541 | config_parse(configtxt); 542 | config_write(); 543 | 544 | // reboot by watchdog 545 | watchdog_enable(500, 1); 546 | while (1) 547 | ; 548 | } 549 | } 550 | 551 | if (lba >= 0x00803fa0) { 552 | // "disk0~6.hds" file write 553 | lba -= 0x00803fa0; 554 | int id = lba / 0x800000; 555 | lba %= 0x800000; 556 | if (diskinfo[id].type == DTYPE_NOTUSED) 557 | return -1; 558 | if (lba >= diskinfo[id].sects) 559 | return -1; 560 | DPRINTF3("disk %d: write 0x%x\n", id, lba); 561 | 562 | vd_sync(); 563 | 564 | if (diskinfo[id].type == DTYPE_HDS && diskinfo[id].sfh != NULL) { 565 | if (hds_cache_write(diskinfo[id].smb2, diskinfo[id].sfh, lba, buf) < 0) 566 | return -1; 567 | return 0; 568 | } 569 | if (diskinfo[id].type == DTYPE_REMOTECOMM) { 570 | struct vdbuf *b = (struct vdbuf *)buf; 571 | if (b->header.signature != 0x5a383658) { /* "X68Z" (big endian) */ 572 | return -1; 573 | } 574 | vdbuf_header = b->header; 575 | memcpy(&vdbuf_write[b->header.page * (512 - 16)], b->buf, sizeof(b->buf)); 576 | if (b->header.page == b->header.maxpage) { 577 | // last page copy 578 | int rsize; 579 | xSemaphoreTake(remote_sem, portMAX_DELAY); 580 | cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); 581 | if ((rsize = vd_command(vdbuf_write, vdbuf_read)) < 0) { 582 | rsize = remote_serv(vdbuf_write, vdbuf_read); 583 | } 584 | cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); 585 | xSemaphoreGive(remote_sem); 586 | vdbuf_rpages = (rsize < 0) ? 0 : ((rsize - 1) / (512 - 16)); 587 | vdbuf_rcnt = 0; 588 | DPRINTF3("vdbuf_rpages=%d\n", vdbuf_rpages); 589 | } 590 | return 0; 591 | } 592 | } 593 | 594 | return -1; 595 | } 596 | --------------------------------------------------------------------------------