diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..5eebc68f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: akuker diff --git a/.github/workflows/rpi_image_creation.yml b/.github/workflows/rpi_image_creation.yml index 748d77ae..873223d9 100644 --- a/.github/workflows/rpi_image_creation.yml +++ b/.github/workflows/rpi_image_creation.yml @@ -1,6 +1,4 @@ -# This is a basic workflow to help you get started with Actions - -name: CI +name: Generate a RaSCSI OS image, based upon the official Rapsberry Pi OS # Controls when the workflow will run on: @@ -21,16 +19,51 @@ jobs: runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job + # TODO: I don't think this step is needed.... steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - name: Checkout RaSCSI + uses: actions/checkout@v2 + with: + path: RASCSI + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - name: Checkout RaSCSI + uses: actions/checkout@v2 + with: + repository: akuker/pi-gen + path: pi-gen + + - name: Install Raspberry Pi build toolchain + run: sudo apt-get install coreutils quilt parted qemu-user-static debootstrap zerofree zip dosfstools libarchive-tools libcap2-bin grep rsync xz-utils file git curl bc qemu-utils kpartx - # Runs a single command using the runners shell - - name: Run a one-line script - run: echo Hello, world! - - # Runs a set of commands using the runners shell - - name: Run a multi-line script + - name: Configure the build run: | - echo Add other actions to build, - echo test, and deploy your project. + echo ----------------------------------------------------- + echo "IMG_NAME=RaSCSI-$GITHUB_RUN_ID" > config + echo "TARGET_HOSTNAME=rascsi" >> config + echo "ENABLE_SSH=1" >> config + echo "LOCALE_DEFAULT=en_US.UTF-8" >> config + echo "KEYBOARD_KEYMAP=us" >> config + echo "KEYBOARD_LAYOUT=\"English (US)\"" >> config + echo ----------------------------------------------------- + cat config + working-directory: pi-gen + + - name: Run the Raspberry Pi build generation tool + run: sudo ./build.sh + working-directory: pi-gen + + - name: List the files in the deploy directory + run: | + echo ----------------------------------------------------- + ls -alh pi-gen/deploy/ + echo ----------------------------------------------------- + + - name: Archive the build artifacts + uses: actions/upload-artifact@v2.2.4 + with: + # Artifact name + name: raspberry-pi-image + # A file, directory or wildcard pattern that describes what to upload + path: pi-gen/deploy/* diff --git a/.gitignore b/.gitignore index e17ba71f..a9d4c876 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,8 @@ core *.swp __pycache__ src/web/current +src/web/rascsi_interface_pb2.py src/oled_monitor/current +src/oled_monitor/rascsi_interface_pb2.py +src/raspberrypi/hfdisk/ +*~ diff --git a/LICENSE b/LICENSE index ffef9a2f..3960dd7b 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,8 @@ BSD 3-Clause License Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) Copyright (C) 2014-2020 GIMONS -Copyright (c) 2020, akuker +Copyright (c) 2020-2021 akuker +Copyright (c) RaSCSI project contributors (github.com/akuker/rascsi) All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/RASCSI_webpage_translated.pdf b/RASCSI_webpage_translated.pdf deleted file mode 100644 index d7ec3d69..00000000 Binary files a/RASCSI_webpage_translated.pdf and /dev/null differ diff --git a/README.md b/README.md index 5652d747..63e62676 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,5 @@ -![C/C++ CI](https://github.com/akuker/RASCSI/workflows/C/C++%20CI/badge.svg) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/akuker/RASCSI/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/akuker/RASCSI/?branch=master) - - Coverity Scan Build Status - - # What is RaSCSI? -RaSCSI is a virtual SCSI device emulator that runs on a Raspberry Pi. It runs in userspace, and can emulate several SCSI devices at one time. There is a control interface to attach / detach drives during runtime, as well as insert and eject removable media. This project is aimed at users of vintage Macintosh computers from the 1980's and 1990's. +RaSCSI is a virtual SCSI device emulator that runs on a Raspberry Pi. It runs in userspace, and can emulate several SCSI devices at one time. There is a control interface to attach / detach drives during runtime, as well as insert and eject removable media. This project is aimed at users of vintage Macintosh computers and more (see [compatibility list](https://github.com/akuker/RASCSI/wiki/Compatibility)) from the 1980's and 1990's. Please check out the full story with much more detail on the [wiki](https://github.com/akuker/RASCSI/wiki)! diff --git a/doc/converter.txt b/doc/converter.txt deleted file mode 100644 index 25ea42a7..00000000 --- a/doc/converter.txt +++ /dev/null @@ -1,116 +0,0 @@ ------------------------------------------------------------------------------- - - SCSI Target Emulator RaSCSI for Raspberry Pi - - Powered by XM6 TypeG Technology. - Copyright (C) 2016-2020 GIMONS - ------------------------------------------------------------------------------- - -□変換基板の必要性について - SCSIはTTLレベルで5Vを220Ωと330Ωで分圧(パッシブターミネータの場合)する - ことで各信号線に3V弱の電圧がかかった状態が定常状態(信号的にはネゲート)に - なっています。 - - イニシエータ側もしくはターゲット側が信号をアサートする(=0V)にしようと - すると両端のターミネータから合わせて5000÷220×2=45mAの電流が流れることに - なります(X68000のSCSIコントローラであるMB89352のデータシートを見ればシンク - 電流としてIol48mAとなっています)。 - - RPIのGPIOはこのような大きなシンク電流は吸収できません。電気的に安全な接続 - を行うためには汎用ロジックIC等で変換基板を作る必要があります。汎用ロジック - ICで48mAものシンク電流に耐えるのは74LS06とか07といったオープンコレクタで - ハイパワータイプのものを使用します。 - - 作者は74HC541×3,74HC126×1,74HC04×1で基本的なSCSIの方向制御を行い更に - 74LS07×3を使ってバスをドライブする回路を組んでみたところ問題なく動作する - ことを確認しました。 - - 他にも74LS641の派生版である74LS641-1を使用すると回路はシンプルに構成できる - でしょう。ノーマル品と違ってシンク電流が48mA対応なので74LS07を使用する必要 - はありません。しかし入手性はそれほど良くありません。 - -□変換基板の回路図案 - 同じフォルダに回路図案を入れています。 - - ・target.png - SCSIのターゲットモードを使用するための変換基板回路図です。基本機能である - HDDやMOのエミュレーションを行うのであればこの回路図相当の物を作れば良い - でしょう。使用するGPIOピンも最も少ない構成になります。 - - ピンアサインを変更しなければRaSCSIのstandardディレクトリに含まれる - バイナリを使用することが可能です。 - - ・fullspec.png - SCSIのターゲットモード、イニシエータモードを利用できる変換基板回路図です。 - 全ての74LS641-1の方向制御をRaSCSIから行いますのでGPIOピンを三つ余分に使用 - してしまいます。 - - ピンアサインのカスタマイズで、PIN_TAD,PIN_IND,PIN_DTDにそれぞれ標準 - では6,7,8を設定してコンパイルする必要があります。ピンアサインの - カスタマイズを参照してください。 - -□既存のものを手に入れる方法 - 最近では主にTwitter界隈を通じてRaSCSI用の変換基板を作成していただいて - いる方々がいらっしゃいます。直ぐに見つかると思いますのでここでの紹介 - は省略します。時期は未定ですが公式版の有償頒布を計画しています。 - -□ピンアサインのカスタマイズ - GPIOの信号制御論理やピンアサインはgpiobus.hに定義があります。 - - カスタマイズ例としてgpiobus.hに下記の二つの変換基板用定義例を用意しました。 - 配布物の中にはコンパイル済みバイナリも含まれています。 - - ・あいぼむ版 - ・GAMERnium版 - -□カスタマイズ方法 - ・RaSCSI起動時のメッセージです。 - CONNECT_DESC - - ・信号制御モードを選択します。 - SIGNAL_CONTROL_MODE - - 0:SCSI論理仕様 - 直結またはHPに公開した74LS641-1等を使用する変換基板 - アーサート:0V - ネゲート :オープンコレクタ出力(バスから切り離す) - - 1:負論理仕様(負論理->SCSI論理への変換基板を使用する場合) - 現時点でこの仕様による変換基板は存在しません - アーサート:0V -> (CONVERT) -> 0V - ネゲート :3.3V -> (CONVERT) -> オープンコレクタ出力 - - 2:正論理仕様(正論理->SCSI論理への変換基板を使用する場合) - RaSCSI Adapter Rev.C @132sync等 - - アーサート:3.3V -> (CONVERT) -> 0V - ネゲート :0V -> (CONVERT) -> オープンコレクタ出力 - - ・制御信号ピンアサイン - PIN_ACT:SCSIコマンドを処理中の状態を示す信号のピン番号。 - PIN_ENB:起動から終了の間の有効信号を示す信号のピン番号。 - PIN_TAD:ターゲット信号(BSY,IO,CD,MSG,REG)の入出力方向を示す信号のピン番号。 - PIN_IND:イニシーエータ信号(SEL,ATN,RST,ACK)の入出力方向を示す信号のピン番号。 - PIN_DTD:データ信号(DT0...DT7,DP)の入出力方向を示す信号のピン番号。 - - ・制御信号出力論理 - 0V:FALSE 3.3V:TRUEで指定します。 - - ACT_ON:PIN_ACT信号の論理です。 - ENB_ON:PIN_ENB信号の論理です。 - TAD_IN:PIN_TAD入力方向時の論理です。 - IND_IN:PIN_ENB入力方向時の論理です。 - DTD_IN:PIN_ENB入力方向時の論理です。 - - ・SCSI信号ピンアサイン - PIN_DT0~PIN_SEL:それぞれSCSI信号のピン番号です。 - -□コンパイル方法 - - ・実行ファイル(rascsi,rasctl) - gpiobus.hを修正 - make clean - make - -[EOF] diff --git a/doc/converter_en.txt b/doc/converter_en.txt deleted file mode 100644 index 2b7a01cc..00000000 --- a/doc/converter_en.txt +++ /dev/null @@ -1,116 +0,0 @@ --------------------------------------------------- ---------------------------- - - SCSI Target Emulator RaSCSI for Raspberry Pi - - Powered by XM6 TypeG Technology. - Copyright (C) 2016-2020 GIMONS - --------------------------------------------------- ---------------------------- - -□ Necessity of conversion board - SCSI divides 5V at 220Ω and 330Ω at TTL level (in the case of passive terminator) - As a result, the state where a voltage of less than 3 V is applied to each signal line becomes a steady state (signal-wise negate) - It has become. - - Initiator side or target side tries to assert signal (=0V) - Then, a total current of 5000÷220×2=45mA flows from both terminators. - If you look at the data sheet of MB89352 which is the SCSI controller of X68000, - The current is Iol48mA). - - The RPI GPIO cannot absorb such a large sink current. Electrically secure connection - To do this, it is necessary to make a conversion board with a general-purpose logic IC. General logic - It is an open collector such as 74LS06 or 07 that can withstand a sink current of 48 mA with IC. - Use the high power type. - - The author does basic SCSI direction control with 74HC541×3,74HC126×1,74HC04×1 - I tried to build a circuit to drive the bus using 74LS07 × 3 and it works without problems - I confirmed that. - - If you use 74LS641-1 which is a derivative of 74LS641, you can configure the circuit simply. - Would be Unlike normal products, the sink current is compatible with 48 mA, so it is necessary to use 74LS07 - there is no. But availability is not so good. - -□ Circuit board of conversion board - The circuit design is put in the same folder. - - ・Target.png - Conversion board circuit diagram for using SCSI target mode. It is a basic function - If you want to emulate HDD or MO, you can make something equivalent to this circuit diagram. - Would be It also uses the fewest GPIO pins. - - If you do not change the pin assignment, it will be included in the standard directory of RaSCSI - It is possible to use the binary. - - ・Fullspec.png - It is a conversion board circuit diagram that can use SCSI target mode and initiator mode. - All 74LS641-1 direction control is performed from RaSCSI, so use 3 extra GPIO pins - I will do it. - - Customize pin assignments to PIN_TAD, PIN_IND, PIN_DTD respectively - Now you need to set 6,7,8 and compile. Pin assigned - See Customization. - -□ How to get existing ones - Recently, you have created a conversion board for RaSCSI mainly through the Twitter area. - There are people who are. Introducing here - Is omitted. The timing is undecided, but we plan to distribute the official version for a fee. - -□ Customize pin assignment - The signal control logic and pin assignment of GPIO are defined in gpiobus.h. - - As a customization example, the following two conversion board definition examples are prepared in gpiobus.h. - Compiled binaries are also included in the distribution. - - ・Aibomu version - ・GAMERnium version - -□ How to customize - -This is a message when starting up RaSCSI. - CONNECT_DESC - - ・Select the signal control mode. - SIGNAL_CONTROL_MODE - - 0: SCSI logical specification - Conversion board using 74LS641-1 etc. directly connected or published on HP - Arthurt: 0V - Negative: Open collector output (disconnect from bus) - - 1: Negative logic specification (when using the conversion board for negative logic -> SCSI logic) - There are no conversion boards with this specification at this time - Arthurt: 0V -> (CONVERT) -> 0V - Negative: 3.3V -> (CONVERT) -> Open collector output - - 2: Positive logic specification (when using the conversion board for positive logic -> SCSI logic) - RaSCSI Adapter Rev.C @132sync etc. - - Arthurt: 3.3V -> (CONVERT) -> 0V - Negative: 0V -> (CONVERT) -> Open collector output - - ・Control signal pin assignment - PIN_ACT: The pin number of the signal that indicates the status that the SCSI command is being processed. - PIN_ENB: Pin number of the signal that indicates the valid signal from start to finish. - PIN_TAD: Pin number of the signal that indicates the input/output direction of the target signal (BSY,IO,CD,MSG,REG). - PIN_IND: Pin number of the signal indicating the input/output direction of the initiator signal (SEL, ATN, RST, ACK). - PIN_DTD: Pin number of the signal that indicates the input/output direction of the data signal (DT0...DT7,DP). - - ・Control signal output logic - 0V:FALSE Specify with 3.3V:TRUE. - - ACT_ON: PIN_ACT signal logic. - ENB_ON: Logic of PIN_ENB signal. - TAD_IN:PIN_TAD This is the logic in the input direction. - IND_IN:PIN_ENB Logic in the input direction. - DTD_IN:PIN_ENB Logic in the input direction. - - ・ SCSI signal pin assignment - PIN_DT0 to PIN_SEL: Each is the pin number of the SCSI signal. - -□ How to compile - - ・Executable files (rascsi, rasctl) - Fix gpiobus.h - make clean - make - -[EOF] \ No newline at end of file diff --git a/doc/fullspec.png b/doc/fullspec.png deleted file mode 100644 index b80fee28..00000000 Binary files a/doc/fullspec.png and /dev/null differ diff --git a/doc/pinassign.png b/doc/pinassign.png deleted file mode 100644 index a334ade7..00000000 Binary files a/doc/pinassign.png and /dev/null differ diff --git a/doc/rascsi.1 b/doc/rascsi.1 index 07ab3e05..e8c8e1f4 100644 --- a/doc/rascsi.1 +++ b/doc/rascsi.1 @@ -3,19 +3,28 @@ rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins .SH SYNOPSIS .B rascsi -[\fB\-IDn\fR \fIfile\fR] -[\fB\-HDn\fR \fIfile\fR]... +[\fB\-F\f® \fIFOLDER\fR] +[\fB\-L\f® \fILOG_LEVEL\fR] +[\fB\-h\fR] +[\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR] +[\fB\-p\f® \fIPORT\fR] +[\fB\-r\fR \fIRESERVED_IDS\fR] +[\fB\-n\fR \fITYPE\fR] +[\fB\-v\fR] +[\fB\-IDn:[u]\fR \fIFILE\fR] +[\fB\-HDn[:u]\fR \fIFILE\fR]... .SH DESCRIPTION .B rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins. .PP -In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) devices can be specified. -The number (n) after the ID or HD idnetifier specifies the ID number for that device. -For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer).Note that SASI is considered rare and only used on very early Sharp X68000 computers. +In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) or SASI (-HDn[:u]) devices can be specified. +The number (n) after the ID or HD identifier specifies the ID number for that device. The optional number (u) specifies the LUN (logical unit) for that device. The default LUN is 0. +For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer). The LUN is limited from 0-31. Note that SASI is considered rare and only used on very early Sharp X68000 computers. .PP -RaSCSI will determin the type of device based upon the file extension of the FILE argument. +RaSCSI will determine the type of device based upon the file extension of the FILE argument. hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used with X68000) - hds: SCSI Hard Disk image (XM6 SCSI HD image - typically only used with X68000) + hds: SCSI Hard Disk image (generic, non-removable) + hdr: SCSI Hard Disk image (generic, removable) hdn: SCSI Hard Disk image (NEC GENUINE) hdi: SCSI Hard Disk image (Anex86 HD image) nhd: SCSI Hard Disk image (T98Next HD image) @@ -26,37 +35,69 @@ RaSCSI will determin the type of device based upon the file extension of the FIL For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command: sudo rascsi -ID0 /path/to/drive/hdimage.hda -Once RaSCSI starts, it will open a socket (port 6868) to allow external management commands. -If another process is using port 6868, RaSCSI will terminate, since it is likely another instance of RaSCSI. +Once RaSCSI starts, it will open a socket (default port is 6868) to allow external management commands. +If another process is using the rascsi port, RaSCSI will terminate, since it is likely another instance of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used to send commands. To quit RaSCSI, press Control + C. If it is running in the background, you can kill it using an INT signal. .SH OPTIONS .TP -.BR \-ID\fIn " " \fIFILE -n is the SCSI ID number (0-7) +.BR \-b\fI " " \fIBLOCK_SIZE +The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes. +.TP +.BR \-F\fI " " \fIFOLDER +The default folder for image files. For files in this folder no absolute path needs to be specified. The initial default folder is '~/images'. +.TP +.BR \-L\fI " " \fILOG_LEVEL +The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'. +.TP +.BR \-h\fI " " \fI +Show a help page. +.TP +.BR \-n\fI " " \fIVENDOR:PRODUCT:REVISION +Set the vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed. +.TP +.BR \-p\fI " " \fIPORT +The rascsi server port, default is 6868. +.TP +.BR \-r\fI " " \fIRESERVED_IDS +Comma-separated list of IDs to reserve. +.BR \-p\fI " " \fITYPE +The optional case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP). If no type is specified for devices that support an image file, rascsi tries to derive the type from the file extension. +.TP +.BR \-v\fI " " \fI +Display the rascsi version. +.TP +.BR \-ID\fIn[:u] " " \fIFILE +n is the SCSI ID number (0-7). u (0-31) is the optional LUN (logical unit). The default LUN is 0. .IP -FILE is the name of the image file to attach to that ID. +FILE is the name of the image file to use for the SCSI device. For devices that do not support an image file (SCBR, SCDP) a dummy name must be provided. .TP -.BR \-HD\fIn " " \fIFILE -n is the SASI ID number (0-15) +.BR \-HD\fIn[:u] " " \fIFILE +n is the SASI ID number (0-15). The effective SASI ID is calculated as n/2, the effective SASI LUN is calculated is the remainder of n/2. Alternatively the n:u syntax can be used, where ns is the SASI ID (0-7) and u the LUN (0-1). .IP -FILE is the name of the image file to attach to that ID. +FILE is the name of the image file to use for the SASI device. .IP -Note: SASI usage is rare, and is typically limited to early Sharp X68000 systems. +Note: SASI usage is rare, and is typically limited to early Unix workstations and Sharp X68000 systems. .SH EXAMPLES Launch RaSCSI with no emulated drives attached: rascsi -Launch RaSCSI with an Apple hard drive image as ID0 and a CD-ROM as ID 2 +Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID 2 rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso +Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw device file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter as ID 6: + rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp DUMMY_FILENAME + To create an empty, 100MB HD image, use the following command: dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800 +In case the fallocate command is available a much faster alternative to the dd command is: + fallocate -l 104857600 /path/to/newimage.hda + .SH SEE ALSO -rasctl(1), scsimon(1) +rasctl(1), scsimon(1), rasdump(1), sasidump(1) Full documentation is available at: diff --git a/doc/rascsi.txt b/doc/rascsi.txt deleted file mode 100644 index 6b1c6d90..00000000 --- a/doc/rascsi.txt +++ /dev/null @@ -1,335 +0,0 @@ ------------------------------------------------------------------------------- - - SCSI Target Emulator RaSCSI (*^..^*) - for Raspberry Pi - - Powered by XM6 TypeG Technology. - Copyright (C) 2016-2020 GIMONS - ------------------------------------------------------------------------------- -□RaSCSIとは - RaSCSIはRaspberry Piで動作するSCSIデバイス(ハードディスク,MO,CD-ROM)を - 仮想的に再現するエミュレータです。SCSIを採用したSHARPのX68000で使用する - ことを目的として開発しました。RaSCSIを導入したRaspberry PiをX68000のSCSI - コネクタに接続するだけで物理的なSCSIデバイスとして認識されます。 - - X68000以外にもSCSIを採用したFM TOWNSやPC98等のレトロPCでも使用できるかも - しれません。作者はFM TOWNSとPC9821Ceで動作するところまでは確認しています。 - - RaSCSIはSCSIデバイスをエミュレートするソフトウェアに加えてRaspberry Piの - GPIOコネクタをSCSIコネクタに変換する機構の総称を指します。 - -□動作環境 - (1)Raspberry Pi - Raspberry Pi Zero - Raspberry Pi Zero W - Raspberry Pi Zero WH - Raspberry Pi 2 Model B - Raspberry Pi 3 Model B(推奨) - Raspberry Pi 3 Model A+ - Raspberry Pi 3 Model B+ - Raspberry Pi 4 Model B - - Zero/Zero W/Zero WHでは性能的に少し不安定かもしれません。 - - 3 Model A+/3 Model B+/4 Model Bは高性能ですが熱の影響でCPUクロックが - 変動することがありますので対策が必要でしょう。 - - (2)OS - RASPBIAN BUSTERで開発およびテストしています。 - RaSCSIはSCSI信号をGPIOを利用して制御しているので可能な限り低レイテンシー - の状態で使用する必要があります。したがってCUIモードで利用することを推奨 - します。 - -□SCSIコネクタとの接続方法 - 状況が複雑になってきましたのでRaSCSIのホームページ上で情報提供しています。 - このドキュメントの最後にある公式ホームページを参考にして下さい。 - -□配布物 - 配布するアーカイブには実行ファイル、ドキュメント、ソースコードのそれぞれが - ディレクトリで分かれて含まれています。 - - bin/ ・・・ 実行ファイル - raspberrypi/ ・・・ RPI用のプログラム - rascsi.tar.gz ・・・ 実行ファイルをtar+gzipしたもの。 - - x68k/ ・・・ X68000用の専用ドライバ - RASDRIVER.XDF・・・ 二つのドライバを含むフロッピーイメージ - RASDRIVER.HDS・・・ 二つのドライバを含むSCSI HDイメージ - RASDRIVER.HDF・・・ 二つのドライバを含むSASI HDイメージ - - doc/ ・・・ ドキュメント - rascsi.txt ・・・ 当ドキュメント - x68k.txt ・・・ X68000固有機能の説明 - converter.txt ・・・ 変換基板の説明 - pinassign.png ・・・ ピンアサイン図 - target.png ・・・ 変換基板回路図案(ターゲットモード) - fullspec.png ・・・ 変換基板回路図案(フルスペック) - - src/ ・・・ ソースコード - raspberrypi/ ・・・ RPI用のプログラムソース一式 - x68k/ ・・・ X68000用のプログラム一式 - - - RPIで使用するプログラムはrascsi.tar.gzですのでRPI上に転送してから解凍して - 下さい。パーミッション等を維持するためにRPI上で解凍することを推奨します。 - - rascsi.tar.gzにはstandard,fullspec,aibom,gamerniumのディレクトリが含まれ - ています。 - - 直結ケーブルや直結基板を使用する場合はstandardディレクトリの実行ファイル - を使用して下さい。 - - 同様にフルスペック版と説明された変換基板の場合はfullspecのディレクトリの - ものを使用します(直結でも動くと思います)。 - - aibom,gamerniumディレクトリのものは"あいぼむ版","GAMERnium版"の変換基板を - 使用する時のものです。 - - X68000用のドライバはRASDRIVER.XDFもしくはRASDRIVER.HDSの中に次の二つが含ま - れています。 - RASDRV.SYS ・・・ ホストドライブドライバ - RASETHER.SYS ・・・ イーサネットドライバ - -□RASCI本体の使用方法(rascsi) - - ID指定の場合 - rascsi [-IDn FILE] ... - n:0~7 - - HD指定の場合(X68000 SASI機のHD指定互換) - rascsi [-HDn FILE] ... - n:0~15 - - ルート権限が必要ですのでsudo等で起動する必要があります。 - オプションに-hを付けると簡単なHELPが表示されます - - Usage: rascsi [-IDn FILE] ... - - n is SCSI identification number(0-7). - FILE is disk image file. - - Usage: rascsi [-HDn FILE] ... - - n is X68000 SASI HD number(0-15). - FILE is disk image file. - - Image type is detected based on file extension. - hdf : SASI HD image(XM6 SASI HD image) - hds : SCSI HD image(XM6 SCSI HD image) - hdn : SCSI HD image(NEC GENUINE) - hdi : SCSI HD image(Anex86 HD image) - nhd : SCSI HD image(T98Next HD image) - hda : SCSI HD image(APPLE GENUINE) - mos : SCSI MO image(XM6 SCSI MO image) - iso : SCSI CD image(ISO 9660 image) - - 引数では-IDnもしくは-HDnとFILEの一組で一つのSCSI(SASI)デバイスを指定できます。 - -IDの後ろの番号はSCSI(SASI) IDです。IDは0-7を指定できますが通常レトロPC本体 - がイニシエータとしてID7等を使用していると思います。その場合は0-6を指定する - ことになります。 - - FILEは仮想ディスクイメージのファイルパスです。イメージファイル名には拡張子 - が必要です。拡張子によってHD,MO,CDの種別を判定しています。 - - 例)SCSI ID0にHDIMAGE0.HDS,ID1にHDIMAGE1.HDSを指定して起動する場合、 - sudo rascsi -ID0 HDIMAGE0.HDS -ID1 HDIMAGE1.HDS - - 終了する場合はCTRL+Cで停止します。 - バックグラウンドで起動した場合にはkillコマンド該当プロセスにINTシグナルか - HUPシグナルを送ることで終了します。 - - rascsiは起動後にソケット(6868ポート)を開いて外部からの管理コマンドを受け - 付ける状態になります。したがって既に別プロセスとしてrascsiが起動している - 場合はエラーメッセージとともに起動を中断します。 - -□管理ツールの使用方法(rasctl) - バージョン1.10からrasctlという管理ツールを提供します。これはrascsiプロセス - がバックグラウンドで起動(6868ポートで接続待ちの状態)している場合にディスク - 操作のコマンドを発行することが可能となります。コマンドラインは下記の通り。 - - rasctl -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] - - ID : SCSI ID(0~7) - UNIT : ユニット番号(0または1) - CMD : 操作コマンド - attach : ディスクを取り付ける - detach : ディスクを取り外す - insert : メディアを挿入する(MOまたはCDのみ) - eject : メディアを取り出す(MOまたはCDのみ) - protect : メディアを書き込み禁止にする(MOのみ) - TYPE : ディスク種別 - hd : ハードディスク(SASI/SCSI) - mo : MO(光磁気ディスク) - cd : CDROM(CDROMドライブ) - bridge : ブリッジデバイス - FILE : ディスクイメージファイルのパス - - IDは必須です。UNITは省略時は0です(SCSIの場合は0を基本とします)。 - CMDは省略時はattachと解釈します。TYPEはコマンドがattachの場合にはFILEの拡張子 - から自動判定します。FILEはTYPEを明示的に指定している場合は拡張子が異なっても - 構いません。基本的CMD,TYPEの解釈は大文字小文字を無視します。最初の1文字でのみ - 判定しています。 - - コマンド例 - rasctl -i 0 -f HDIMAGE0.HDS - - の場合はSCSI IDは0。CMDはデフォルトでattachでありTYPEは拡張子HDSから判断 - するのでhdと推測することになりrascsi起動時のオプション指定と同等です。 - - 現在の状態を確認するにために-lオプションのみを指定するとデバイス一覧が表示 - されます。コマンドラインは下記の通り。 - - rasctl -l - - rasctl自体の起動にはルート権限は必要ありません。 - -□ディスクダンプツールの使用方法(rasdump) - 直結もしくは直結基板、またはフルスペック基板向けのサンプルプログラムです。 - - 名前の通りSCSI HDDやMOのイメージをダンプ(オプションでリストア)します。 - 自身のIDはBIDで指定して下さい。省略時は7を使用します。 - - rasdump -i ID [-b BID] -f FILE [-r] - ID : ターゲットデバイスのSCSI ID - BID : RaSCSI自身のSCSI ID - FILE : ダンプファイル名 - -r : リストアモード - - サンプルなので必要最低限の処理しか実装していませんので改造するなりして - ご使用下さい。 - -□SASI専用ディスクダンプツールの使用方法(sasidump) - rasdumpをベースにSASI専用に作成したダンプツールです。 - SASI HDイメージをダンプ(オプションでリストア)します。 - - sasidump -i ID [-u UT] [-b BSIZE] -c COUNT -f FILE [-r] - ID : ターゲットデバイスのSASI ID - UT : ターゲットデバイスのUNIT ID - BSIZE: ブロックサイズ(デフォルトは512) - COUNT: ブロック数 - FILE : ダンプファイル名 - -r : リストアモード - -□ソースから実行ファイルをコンパイルする場合 - スタンダード版 - make CONNECT_TYPE=STANDARD - - フルスペック版 - make CONNECT_TYPE=FULLSPEC - - あいぼむ版 - make CONNECT_TYPE=AIBOM - - GAMERnium版 - make CONNECT_TYPE=GAMERNIUM - -□サポートするディスクイメージ - (1)SCSI ハードディスク - HDSファイル形式 (拡張子HDS/HDN/HDI/NHD/HDA) - ファイルサイズは10MB以上4095MB以下の範囲で任意のサイズ(但し512バイト単位) - - 拡張子が"HDN"の場合はNEC純正55ボード(PC-9801-55)向けの純正ハードディスク - エミュレーションを行います。INQUIRYで返却される情報やMODE SENSEのサイズに - に違いがあります。 - - 拡張子が"HDI","NHD"の場合はそれぞれPC98エミュレータであるAnex86及びT98Next - のSCSIハードディスクイメージを使用するものです。HDNの時と同様に一部の情報 - がNEC用に変換されます。 - - 拡張子が"HDA"の場合はAPPLE純正ハードディスクエミュレーションを行います。 - INQUIRY及びMODE SENSEで返却される情報に違いがあります。 - - (2)SASI ハードディスク - HDFファイル形式 (拡張子HDF) - ファイルサイズは10441728バイト、20748288バイト、41496576バイトのいずれか - (それぞれ10MBドライブ、20MBドライブ、40MBドライブに対応)を推奨します。 - 256バイト単位であれば10M~512Mの任意のファイルサイズがマウント可能です。 - - Version1.46から22437888バイトのイメージはMZ-2500/MZ-2800 MZ-1F23専用の - 20MBイメージとして認識します(ブロックサイズが1024という特殊イメージ)。 - - (3)SCSI 光磁気(MO)ディスク - MOSファイル形式 (拡張子MOS) - ファイルサイズは次の4種類のいずれか: - 128MBタイプ (127398912バイト) - 230MBタイプ (228518400バイト) - 540MBタイプ (533248000バイト) - 640MBタイプ (635600896バイト) - 128MB,230MB,540MBは512バイト/セクタ、640MBは2048バイト/セクタになります。 - - (4)SCSI CD-ROMディスク - ISOファイル形式 (拡張子ISO、ISO9660ベタイメージ) - モード1(2048バイト/セクタ)で、データのみ格納されたファイルとRAW形式で記録 - されたファイルの両方に対応しています。 - -□ディスクイメージの作成 - RaSCSI自体がX68000エミュレータであるXM6 TypeGの派生物です。従ってディスク - イメージの作成はXM6 TypeGの「ツール」メニューから行うことを前提としています。 - もちろん先に説明した仕様に従えばdd等で空のイメージファイルを作成することも - 可能です。 - - 例)100MBのHDSイメージ(空ファイル)を作る場合 - - dd if=/dev/zero of=HARDDISK.HDS bs=512 count=204800 - -□動作実績 - 作者の開発環境であるX68000 PRO(内蔵SASI/純正SCSIボード)、X68030 内蔵SCSI、 - XVI Compact 内蔵SCSIで動作確認しています。Mach-2でも動作しました。 - - 他にも初代X68000,ACE,EXPERT,XVI,PRO2,SUPER等で動作報告がありましたので、 - X68000シリーズはほぼ問題ないでしょう。 - - その他のレトロPCではFM TOWNSシリーズ、Apple Macintosh、MSX(MEGA-SCSI利用) - で動作報告があります。PC98シリーズは動作したという報告も多数ありますが、 - SCSIボードによっては全く動作しないという報告もあります。 - -□活用方法 - XM6等のX68000エミュレータを使用している場合はエミュレータで環境構築したHDD - イメージをFTP等でRaspberry Piに転送することでX68000の実機に接続できます。 - またその逆も然りで実機上に存在するファイルを格納したHDDイメージをPCにFTP等 - で転送することでエミュレータで活用することができます。 - -□ライセンス - RaSCSIはあるがまま"AS IS"で配布されるソフトウェアです。 - - つまり使用者が受けたあらゆる損害に対して一切責任を持ちません。またソフト - ウェアに不備もしくは不具合があったとしてもそれを修正する責任もありません。 - - RaSCSIを利用することでRaspberry PiやレトロPCが故障するリスクがあります。 - あくまで自己責任でチャレンジしてください。 - - XM6 TypeG同様に実験成果公開という性質上私のHP以外での配布を認めておりません。 - - XM6のライセンス条項を継承していますので雑誌/書籍での紹介事前の許諾が必要です。 - そもそも2019年にもなってSCSIに反応するのは限られた人だけだと思います。 - -□変換基板の頒布について - 変換基板を有償で頒布する場合は下記の条件に従う限り作者に許諾を得る必要は - ありません。重要なことは基板を購入したユーザーに十分な情報を提供することと - 心得て下さい。 - - 1.頒布価格 - 基板製作費 + パーツ費用 + 運送費 +(社会通念上一般的な)手数料。 - - 2.回路図 - 購入者に回路図を提供して下さい。基板頒布と同時もしくは別途ホームページから - のダウンロード等手段は自由です。 - - 3.動作検証 - X68000実機環境の動作検証は必須とします。実機が手に入らない場合はX68000 - ユーザーの方に検証の協力をお願いしてもよいでしょう。動作検証は起動確認以外 - に書き込みや負荷テストをお願いします。検証結果は使用した環境やと共に公開 - して下さい。 - -□変換基板(公式版)について - BOOTHで2019年3月以降配布しています(数に限りがありますので不定期です)。 - -□公開ホームページ - http://retropc.net/gimons/rascsi/ - -□連絡先 - twitter https://twitter.com/kugimoto0715 - e-mail gimons.developer.works@gmail.com - -[EOF] diff --git a/doc/rascsi_en.txt b/doc/rascsi_en.txt deleted file mode 100644 index fa9a49cf..00000000 --- a/doc/rascsi_en.txt +++ /dev/null @@ -1,335 +0,0 @@ --------------------------------------------------- ---------------------------- - - SCSI Target Emulator RaSCSI (*^..^*) - for Raspberry Pi - - Powered by XM6 TypeG Technology. - Copyright (C) 2016-2020 GIMONS - --------------------------------------------------- ---------------------------- -□ What is RaSCSI - RaSCSI is a SCSI device (hard disk, MO, CD-ROM) that operates on the Raspberry Pi. - It is a virtual emulator. Use with SHARP X68000 that adopts SCSI - It was developed for that purpose. Raspberry Pi with RaSCSI installed on the X68000 SCSI - Simply connect it to the connector and it will be recognized as a physical SCSI device. - - In addition to the X68000, it may be possible to use it with a retro PC such as FM TOWNS or PC 98 that adopted SCSI. - May. The author has confirmed that it works with FM TOWNS and PC9821Ce. - - RaSCSI is a software that emulates SCSI devices plus Raspberry Pi - Refers to the general term for the mechanism that converts a GPIO connector into a SCSI connector. - -□ Operating environment - (1) Raspberry Pi - Raspberry Pi Zero - Raspberry Pi Zero W - Raspberry Pi Zero WH - Raspberry Pi 2 Model B - Raspberry Pi 3 Model B (recommended) - Raspberry Pi 3 Model A+ - Raspberry Pi 3 Model B+ - Raspberry Pi 4 Model B - - Performance may be a little unstable with Zero/Zero W/Zero WH. - - 3 Model A+/3 Model B+/4 Model B has high performance, but CPU clock is affected by heat. - It may fluctuate, so it will be necessary to take measures. - - (2) OS - Developed and tested by RASPBIAN BUSTER. - RaSCSI uses GPIO to control SCSI signals, so latency is as low as possible. - Must be used in the state of. Therefore, it is recommended to use it in CUI mode. - To do. - -□ Connection method with SCSI connector - Since the situation has become complicated, we provide information on the RaSCSI home page. - Please refer to the official website at the end of this document. - -□ Handout - The executable file, the document, and the source code are stored in the distributed archive. - It is divided and included in the directory. - - bin/ ・・・ Executable file - raspberrypi/ ・・・ RPI program - rascsi.tar.gz ・・・ The tar+gzip of the executable file. - - x68k/ ・・・ Dedicated driver for X68000 - RASDRIVER.XDF... A floppy image containing two drivers - RASDRIVER.HDS... A SCSI HD image containing two drivers - RASDRIVER.HDF... SASI HD image containing two drivers - - doc/ ・・・Documents - rascsi.txt ・・・ This document - x68k.txt ・・・ Description of X68000 specific functions - converter.txt ・・・ Description of converter board - pinassign.png ・・・ Pin assignment diagram - target.png ・・・ Conversion board circuit pattern (target mode) - fullspec.png ・・・Conversion board circuit pattern (full spec) - - src/ ・・・ Source code - raspberrypi/ ・・・ Set of program source for RPI - x68k/ ・・・ Set of programs for X68000 - - - The program used in RPI is rascsi.tar.gz, so transfer it to RPI and decompress it. - Please. It is recommended to unzip on RPI to maintain permissions etc. - - rascsi.tar.gz contains standard, fullspec, aibom and gamernium directories. - I am. - - Executable file in the standard directory when using a direct connection cable or direct connection board - Please use. - - Similarly, in the case of the conversion board described as the full spec version, in the fullspec directory - I will use one (I think that it works even if it is directly connected). - - For the aibom and gamernium directories, use the conversion boards of "Aibomu version" and "GAMERnium version". - It is when using. - - The driver for X68000 includes the following two in RASDRIVER.XDF or RASDRIVER.HDS - It is - RASDRV.SYS ・・・ Host drive driver - RASETHER.SYS ・・・ Ethernet driver - -□ How to use RASCI main unit (rascsi) - - When ID is specified - rascsi [-IDn FILE] ... - n:0 ~ 7 - - When specifying HD (compatible with HD specification of X68000 SASI machine) - rascsi [-HDn FILE] ... - n: 0 ~ 15 - - Since root authority is required, it is necessary to start with sudo etc. - If you add -h to the option, a simple HELP will be displayed - - Usage: rascsi [-IDn FILE] ... - - n is SCSI identification number(0-7). - FILE is disk image file. - - Usage: rascsi [-HDn FILE] ... - - n is X68000 SASI HD number(0-15). - FILE is disk image file. - - Image type is detected based on file extension. - hdf: SASI HD image(XM6 SASI HD image) - hds: SCSI HD image(XM6 SCSI HD image) - hdn: SCSI HD image (NEC GENUINE) - hdi: SCSI HD image(Anex86 HD image) - nhd: SCSI HD image(T98Next HD image) - hda: SCSI HD image(APPLE GENUINE) - mos: SCSI MO image(XM6 SCSI MO image) - iso: SCSI CD image (ISO 9660 image) - - In the argument, one SCSI (SASI) device can be specified by a pair of -IDn or -HDn and FILE. - -The number after the ID is the SCSI (SASI) ID. You can specify 0-7 as the ID, but usually the retro PC body - I think you are using ID7 etc. as the initiator. In that case, specify 0-6 - It will be. - - FILE is the file path of the virtual disk image. The image file name has an extension - Is required. The type of HD, MO, CD is determined by the extension. - - Example) When you specify HDIMAGE0.HDS for SCSI ID0 and HDIMAGE1.HDS for ID1 and start up, - sudo rascsi -ID0 HDIMAGE0.HDS -ID1 HDIMAGE1.HDS - - When you finish, press CTRL+C to stop. - If it is started in the background, is the kill command an INT signal to the relevant process? - It ends by sending a HUP signal. - - After starting, rascsi opens a socket (6868 port) and receives external management commands. - It is ready to be attached. Therefore, rascsi has already started as another process. - If so, the boot will abort with an error message. - -□ How to use the management tool (rasctl) - We provide a management tool called rasctl from version 1.10. This is the rascsi process - Disk is running in the background (waiting for connection on port 6868) - It is possible to issue operation commands. The command line is as follows. - - rasctl -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] - - ID: SCSI ID (0 to 7) - UNIT: Unit number (0 or 1) - CMD: Operation command - attach: attach disk - detach: detach disk - insert: insert media (MO or CD only) - eject: Eject media (MO or CD only) - protect: Write protect the media (MO only) - TYPE: Disc type - hd: Hard disk (SASI/SCSI) - mo: MO (magneto-optical disk) - cd: CDROM (CDROM drive) - bridge: Bridge device - FILE: Path of disk image file - - ID is required. UNIT defaults to 0 (default is 0 for SCSI). - CMD is interpreted as attach when omitted. TYPE is the extension of FILE when the command is attach - It will automatically judge from. FILE has a different extension if TYPE is explicitly specified. - does not matter. Basic interpretation of CMD and TYPE is case insensitive. Only the first letter - Judging. - - Command example - rasctl -i 0 -f HDIMAGE0.HDS - - Is 0, the SCSI ID is 0. CMD is attach by default and TYPE is determined from the extension HDS - Therefore, it is supposed to be hd and it is equivalent to the option specification when starting rascsi. - - If only -l option is specified to check the current status, the device list is displayed. - Will be done. The command line is as follows. - - rasctl -l - - You do not need root privileges to start rasctl itself. - -□ How to use Disk Dump Tool (rasdump) - Sample program for direct connection or direct connection board, or full-spec board. - - As the name suggests, it dumps (optionally restores) the image of SCSI HDD or MO. - Specify your own ID as BID. If omitted, 7 is used. - - rasdump -i ID [-b BID] -f FILE [-r] - ID: SCSI ID of the target device - BID: RaSCSI's own SCSI ID - FILE: dump file name - -r: Restore mode - - Since it is a sample, only the minimum necessary processing is implemented, so do not modify it - Please use. - -□ How to use SASI dedicated disk dump tool (sasidump) - A dump tool created for SASI based on rasdump. - Dump (optionally restore) the SASI HD image. - - sasidump -i ID [-u UT] [-b BSIZE] -c COUNT -f FILE [-r] - ID: SASI ID of the target device - UT: UNIT ID of the target device - BSIZE: Block size (default 512) - COUNT: Number of blocks - FILE: dump file name - -r: Restore mode - -□ When compiling the executable file from the source - Standard edition - make CONNECT_TYPE=STANDARD - - Full spec version - make CONNECT_TYPE=FULLSPEC - - Aibomu version - make CONNECT_TYPE=AIBOM - - GAMERnium version - make CONNECT_TYPE=GAMERNIUM - -□ Supported disk images - (1) SCSI hard disk - HDS file format (extension HDS/HDN/HDI/NHD/HDA) - The file size can be any size within the range of 10MB to 4095MB (in 512-byte units) - - If the extension is "HDN", a genuine hard disk for NEC genuine 55 board (PC-9801-55) - Perform emulation. For information returned by INQUIRY and MODE SENSE size - There is a difference. - - If the extension is "HDI" or "NHD", the PC98 emulator is Annex86 or T98Next. - It is intended to use a SCSI hard disk image. Some information as in HDN - Is converted for NEC. - - If the extension is "HDA", APPLE genuine hard disk emulation is performed. - There is a difference in the information returned by INQUIRY and MODE SENSE. - - (2) SASI hard disk - HDF file format (extension HDF) - File size is either 10441728 bytes, 20748288 bytes, or 41496576 bytes - We recommend 10MB drive, 20MB drive and 40MB drive respectively. - Any file size from 10M to 512M can be mounted in 256-byte units. - - Images from version 1.46 to 22437888 bytes are for MZ-2500/MZ-2800 MZ-1F23 - Recognized as a 20MB image (special image with a block size of 1024). - - (3) SCSI magneto-optical (MO) disk - MOS file format (extension MOS) - File size is one of the following four types: - 128MB type (127398912 bytes) - 230MB type (228518400 bytes) - 540MB type (533248000 bytes) - 640MB type (635600896 bytes) - 128MB, 230MB, 540MB is 512 bytes/sector and 640MB is 2048 bytes/sector. - - (4) SCSI CD-ROM disk - ISO file format (extension ISO, ISO9660 solid image) - Record in a file containing only data and in RAW format in mode 1 (2048 bytes/sector) - Both files are supported. - -□ Disk image creation - RaSCSI itself is a derivative of the XM6 TypeG, an X68000 emulator. Therefore the disc - It is assumed that the image is created from the "Tools" menu of XM6 TypeG. - Of course, you can also create an empty image file with dd etc. according to the specifications explained above. - Is possible. - - Example) When creating a 100 MB HDS image (empty file) - - dd if=/dev/zero of=HARDDISK.HDS bs=512 count=204800 - -□ Operation record - The author's development environment X68000 PRO (built-in SASI/genuine SCSI board), X68030 built-in SCSI, - We have confirmed the operation with the XVI Compact built-in SCSI. It also worked on Mach-2. - - Since there were other operation reports on the first generation X68000, ACE, EXPERT, XVI, PRO2, SUPER, etc., - The X68000 series should be fine. - - For other retro PCs, FM TOWNS series, Apple Macintosh, MSX (using MEGA-SCSI) - There is an operation report in. There are many reports that the PC98 series worked, but - Some SCSI boards do not even work at all. - -□ How to use - If you are using an X68000 emulator such as XM6, HDD that is built by the emulator - You can connect to the actual X68000 machine by transferring the image to the Raspberry Pi by FTP etc. - The reverse is also true, and the HDD image that stores the files that exist on the actual machine is FTPed to the PC, etc. - You can use it in the emulator by transferring with. - -□ License - RaSCSI is software distributed as is "AS IS". - - That is, we do not take any responsibility for any damages that the user receives. Soft again - We are not responsible for fixing any defects or defects in the software. - - There is a risk that the Raspberry Pi and retro PC will break down by using RaSCSI. - Please challenge at your own risk. - - As with XM6 TypeG, we are not allowed to distribute it on other than my HP due to the nature of experimental results disclosure. - - As it inherits the license terms of XM6, introduction in magazines/books requires prior permission. - In the first place, I think only a limited number of people will respond to SCSI in 2019. - -□About distribution of conversion boards - If you want to distribute the conversion board for a fee, it is not necessary to obtain permission from the author as long as you follow the conditions below. - There is none. It is important to provide enough information to the user who purchased the board. - Please understand. - - 1.Distribution price - Board production cost + parts cost + transportation cost + (general social convention) fee. - - 2. Schematic - Please provide the buyer with the schematic. Simultaneously with board distribution or separately from homepage - There are no restrictions on the means of downloading. - - 3. Operation verification - Operation verification of the X68000 actual machine environment is mandatory. If you can not get the real machine X68000 - You may ask the user to cooperate with the verification. Operation verification is other than startup confirmation - Please write and load test. Verification results are published together with the environment in which they were used - please do it. - -□ About conversion board (official version) - It has been distributed on BOOTH since March 2019 (it is irregular because the number is limited). - -□ Public homepage - http://retropc.net/gimons/rascsi/ - -□ Contact information - twitter https://twitter.com/kugimoto0715 - e-mail gimons.developer.works@gmail.com - -[EOF] \ No newline at end of file diff --git a/doc/rascsi_man_page.txt b/doc/rascsi_man_page.txt index de2f98f8..ca6b25cc 100644 --- a/doc/rascsi_man_page.txt +++ b/doc/rascsi_man_page.txt @@ -2,65 +2,136 @@ !! ------ The native file is rascsi.1. Re-run 'make docs' after updating -rascsi(1) General Commands Manual rascsi(1) +rascsi(1) General Commands Manual rascsi(1) NAME rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins SYNOPSIS - rascsi [-IDn file] [-HDn file]... + rascsi [-F[u00AE] FOLDER] [-L[u00AE] LOG_LEVEL] [-h] [-n VENDOR:PROD‐ + UCT:REVISION] [-p[u00AE] PORT] [-r RESERVED_IDS] [-n TYPE] [-v] + [-IDn:[u] FILE] [-HDn[:u] FILE]... DESCRIPTION rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins. - In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) devices can be specified. The number (n) after the ID or HD idnetifier specifies the ID number for that device. For SCSI: - The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer).Note that SASI is considered rare and only used on very early Sharp X68000 com‐ - puters. + In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) or SASI + (-HDn[:u]) devices can be specified. The number (n) after the ID or HD + identifier specifies the ID number for that device. The optional number + (u) specifies the LUN (logical unit) for that device. The default LUN + is 0. For SCSI: The ID is limited from 0-7. However, typically SCSI ID + 7 is reserved for the "initiator" (the host computer). The LUN is lim‐ + ited from 0-31. Note that SASI is considered rare and only used on very + early Sharp X68000 computers. - RaSCSI will determin the type of device based upon the file extension of the FILE argument. - hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used with X68000) - hds: SCSI Hard Disk image (XM6 SCSI HD image - typically only used with X68000) + RaSCSI will determine the type of device based upon the file extension + of the FILE argument. + hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used + with X68000) + hds: SCSI Hard Disk image (generic, non-removable) + hdr: SCSI Hard Disk image (generic, removable) hdn: SCSI Hard Disk image (NEC GENUINE) hdi: SCSI Hard Disk image (Anex86 HD image) nhd: SCSI Hard Disk image (T98Next HD image) - hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac SCSI emulation) - mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only used with X68000) + hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac + SCSI emulation) + mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only + used with X68000) iso: SCSI CD-ROM image (ISO 9660 image) - For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command: + For example, if you want to specify an Apple-compatible HD image on ID + 0, you can use the following command: sudo rascsi -ID0 /path/to/drive/hdimage.hda - Once RaSCSI starts, it will open a socket (port 6868) to allow external management commands. If another process is using port 6868, RaSCSI will terminate, since it is likely another in‐ - stance of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used to send commands. + Once RaSCSI starts, it will open a socket (default port is 6868) to al‐ + low external management commands. If another process is using the + rascsi port, RaSCSI will terminate, since it is likely another instance + of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used + to send commands. - To quit RaSCSI, press Control + C. If it is running in the background, you can kill it using an INT signal. + To quit RaSCSI, press Control + C. If it is running in the background, + you can kill it using an INT signal. OPTIONS - -IDn FILE - n is the SCSI ID number (0-7) + -b BLOCK_SIZE + The optional block size. For SCSI drives 512, 1024, 2048 or 4096 + bytes, default size is 512 bytes. For SASI drives 256 or 1024 + bytes, default is 256 bytes. - FILE is the name of the image file to attach to that ID. + -F FOLDER + The default folder for image files. For files in this folder no + absolute path needs to be specified. The initial default folder + is '~/images'. - -HDn FILE - n is the SASI ID number (0-15) + -L LOG_LEVEL + The rascsi log level (trace, debug, info, warn, err, critical, + off). The default log level is 'info'. - FILE is the name of the image file to attach to that ID. + -h Show a help page. - Note: SASI usage is rare, and is typically limited to early Sharp X68000 systems. + -n VENDOR:PRODUCT:REVISION + Set the vendor, product and revision for the device, to be re‐ + turned with the INQUIRY data. A complete set of name components + must be provided. VENDOR may have up to 8, PRODUCT up to 16, RE‐ + VISION up to 4 characters. Padding with blanks to the maxium + length is automatically applied. Once set the name of a device + cannot be changed. + + -p PORT + The rascsi server port, default is 6868. + + -r RESERVED_IDS + Comma-separated list of IDs to reserve. -p TYPE The optional + case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, + SCBR, SCDP). If no type is specified for devices that support an + image file, rascsi tries to derive the type from the file exten‐ + sion. + + -v Display the rascsi version. + + -IDn[:u] FILE + n is the SCSI ID number (0-7). u (0-31) is the optional LUN + (logical unit). The default LUN is 0. + + FILE is the name of the image file to use for the SCSI device. + For devices that do not support an image file (SCBR, SCDP) a + dummy name must be provided. + + -HDn[:u] FILE + n is the SASI ID number (0-15). The effective SASI ID is calcu‐ + lated as n/2, the effective SASI LUN is calculated is the re‐ + mainder of n/2. Alternatively the n:u syntax can be used, where + ns is the SASI ID (0-7) and u the LUN (0-1). + + FILE is the name of the image file to use for the SASI device. + + Note: SASI usage is rare, and is typically limited to early Unix + workstations and Sharp X68000 systems. EXAMPLES Launch RaSCSI with no emulated drives attached: rascsi - Launch RaSCSI with an Apple hard drive image as ID0 and a CD-ROM as ID 2 + Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID + 2 rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso + Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw de‐ + vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter + as ID 6: + rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp DUMMY_FILENAME + To create an empty, 100MB HD image, use the following command: dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800 + In case the fallocate command is available a much faster alternative to + the dd command is: + fallocate -l 104857600 /path/to/newimage.hda + SEE ALSO - rasctl(1), scsimon(1) + rasctl(1), scsimon(1), rasdump(1), sasidump(1) - Full documentation is available at: + Full documentation is available at: + - rascsi(1) + rascsi(1) diff --git a/doc/rasctl.1 b/doc/rasctl.1 index 1533a182..71cdd7c8 100644 --- a/doc/rasctl.1 +++ b/doc/rasctl.1 @@ -3,12 +3,33 @@ rasctl \- Sends management commands to the rascsi process .SH SYNOPSIS .B rasctl +\fB\-e\fR | \fB\-l\fR | -\fB\-i\fR \fIID\fR -[\fB\-u\fR \fIUNIT\fR] +\fB\-m\fR | +\fB\-s\fR | +\fB\-v\fR | +\fB\-D\fR | +\fB\-I\fR | +\fB\-L\fR | +\fB\-O\fR | +\fB\-T\fR | +\fB\-V\fR | +\fB\-X\fR | +[\fB\-C\fR \fIFILENAME:FILESIZE\fR] +[\fB\-E\fR \fIFILENAME\fR] +[\fB\-F\fR \fIIMAGE_FOLDER\fR] +[\fB\-R\fR \fICURRENT_NAME:NEW_NAME\fR] [\fB\-c\fR \fICMD\fR] +[\fB\-f\fR \fIFILE|PARAM\fR] +[\fB\-g\fR \fILOG_LEVEL\fR] +[\fB\-h\fR \fIHOST\fR] +[\fB\-i\fR \fIID\fR +[\fB\-n\fR \fINAME\fR] +[\fB\-p\fR \fIPORT\fR] +[\fB\-r\fR \fIRESERVED_IDS\fR] [\fB\-t\fR \fITYPE\fR] -[\fB\-f\fR \fIFILE\fR] +[\fB\-u\fR \fIUNIT\fR] +[\fB\-x\fR \fICURRENT_NAME:NEW_NAME\fR] .SH DESCRIPTION .B rasctl Sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices. @@ -17,55 +38,128 @@ Either the -i or -l option should be specified at one time. Not both. You do NOT need root privileges to use rasctl. -Note: The command and type arguments are case insensitive. Only the first letter of the command/type are evaluated by the tool. +Note: The command and type arguments are case insensitive. Only the first letter of the command/type is evaluated by the tool. .SH OPTIONS .TP +.BR \-C\fI " "\fIFILENAME:FILESIZE +Create an image file in the default image folder with the specified name and size in bytes. +.TP +.BR \-D\fI +Detach all devices. +.TP +.BR \-E\fI " " \fIFILENAME +Display information on an image file. +.TP +.BR \-F\fI " "\fIIMAGE_FOLDER +Set the default image folder. +.TP +.BR \-I\fI +Gets the list of reserved device IDs. +.TP +.BR \-L\fI " "\fILOG_LEVEL +Set the rascsi log level (trace, debug, info, warn, err, critical, off). +.TP +.BR \-h\fI " " \fIHOST +The rascsi host to connect to, default is 'localhost'. +.TP +.BR \-e\fI +List all images files in the default image folder. +.TP +.BR \-N\fI +Lists all available network interfaces provided that they are up. +.TP +.BR \-O\fI +Display the available rascsi server log levels and the current log level. +.TP .BR \-l\fI List all of the devices that are currently being emulated by RaSCSI, as well as their current status. .TP +.BR \-m\fI +List all file extensions recognized by RaSCSI and the device types they map to. +.TP +.BR \-R\fI " "\fICURRENT_NAME:NEW_NAME +Rename an image file in the default image folder. +.TP +.BR \-p\fI " " \fIPORT +The rascsi port to connect to, default is 6868. +.TP +.BR \-r\fI " " \fIRESERVED_IDS +Comma-separated list of IDs to reserve. +.TP +.BR \-s\fI +Display server-side settings like available images or supported device types. +.TP +.BR \-T\fI +Display all device types and their properties. +.TP +.BR \-v\fI " " \fI +Display the rascsi server version. +.TP +.BR \-V\fI " " \fI +Display the rasctl version. +.TP +.BR \-X\fI " " \fI +Shut down the rascsi process. +.TP +.BR \-d\fI " "\fIFILENAME +Delete an image file in the default image folder. +.TP +.BR \-x\fI " "\fICURRENT_NAME:NEW_NAME +Copy an image file in the default image folder. +.TP .BR \-i\fI " " \fIID ID is the SCSI ID that you want to control. (0-7) .TP -.BR \-u\fI " " \fIUNIT -Unit number (0 or 1). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common) -.TP .BR \-c\fI " " \fICMD -Command is the operation being requested. options are: - attach: attach disk - detach: detach disk - insert: insert media (Magneto-Optical and CD only) - eject: eject media (Magneto-Optical and CD only) - protect: Write protect the media (Magneto-Optical only) +Command is the operation being requested. Options are: + a(ttach): Attach disk + d(etach): Detach disk + i(nsert): Insert media (removable media devices only) + e(ject): Eject media (removable media devices only) + p(rotect): Write protect the medium (not for CD-ROMs, which are always read-only) + u(nprotect): Remove write protection from the medium (not for CD-ROMs, which are always read-only) + s(how): Display device information .IP -When the command is omited, rasctl will default to the 'attach' command +eject, protect and unprotect are idempotent. +.TP +.BR \-b\fI " " \fIBLOCK_SIZE +The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes. +.TP +.BR \-f\fI " " \fIFILE|PARAM +Device-specific: Either a path to a disk image file, or a parameter for a non-disk device. See the rascsi(1) man page for permitted file types. .TP .BR \-t\fI " " \fITYPE -Specifies the type of disk. If this disagrees with the file extension of the specified image, the TYPE argument is ignored. Available drive types are: - hd: Hard disk (SCSI or SASI) - mo: Magneto-Optical disk) +Specifies the device type. This type overrides the type derived from the file extension of the specified image. See the rascsi(1) man page for the available device types. For some types there are shortcuts (only the first letter is required): + hd: SCSI hard disk drive + rm: SCSI removable media drive cd: CD-ROM - bridge: Bridge device (This is only applicable to the Sharp X68000) + mo: Magneto-Optical disk + bridge: Bridge device (Only applicable to the Sharp X68000) + daynaport: DaynaPORT network adapter .TP -.BR \-f\fI " " \fIFILE -Path to the disk image file. See the rascsi(1) man page for allowable file types. +.BR \-n\fI " " \fIVENDOR:PRODUCT:REVISION +The vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed. +.TP +.BR \-u\fI " " \fIUNIT +Unit number (0-31). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common) .SH EXAMPLES -Show a listing of all of the SCSI devices and their current status +Show a listing of all of the SCSI devices and their current status. rasctl -l Example output: - +----+----+------+------------------------------------- - | ID | UN | TYPE | DEVICE STATUS - +----+----+------+------------------------------------- - | 0 | 1 | SCHD | /home/pi/harddisk.hda - +----+----+------+------------------------------------- + +----+-----+------+------------------------------------- + | ID | LUN | TYPE | IMAGE FILE + +----+-----+------+------------------------------------- + | 0 | 1 | SCHD | /home/pi/harddisk.hda + +----+-----+------+------------------------------------- Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image "HDIIMAGE0.HDS". rasctl -i 0 -f HDIIMAGE0.HDS .SH SEE ALSO -rascsi(1) scsimon(1) +rascsi(1), scsimon(1), rasdump(1), sasidump(1) Full documentation is available at: diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt index 2b0498a8..138c16a5 100644 --- a/doc/rasctl_man_page.txt +++ b/doc/rasctl_man_page.txt @@ -2,67 +2,160 @@ !! ------ The native file is rasctl.1. Re-run 'make docs' after updating -rascsi(1) General Commands Manual rascsi(1) +rascsi(1) General Commands Manual rascsi(1) NAME rasctl - Sends management commands to the rascsi process SYNOPSIS - rasctl -l | -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] + rasctl -e | -l | -m | -s | -v | -D | -I | -L | -O | -T | -V | -X | [-C + FILENAME:FILESIZE] [-E FILENAME] [-F IMAGE_FOLDER] [-R CUR‐ + RENT_NAME:NEW_NAME] [-c CMD] [-f FILE|PARAM] [-g LOG_LEVEL] [-h HOST] + [-i ID [-n NAME] [-p PORT] [-r RESERVED_IDS] [-t TYPE] [-u UNIT] [-x + CURRENT_NAME:NEW_NAME] DESCRIPTION - rasctl Sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices. + rasctl Sends commands to the rascsi process to make configuration ad‐ + justments at runtime or to check the status of the devices. Either the -i or -l option should be specified at one time. Not both. You do NOT need root privileges to use rasctl. - Note: The command and type arguments are case insensitive. Only the first letter of the command/type are evaluated by the tool. + Note: The command and type arguments are case insensitive. Only the + first letter of the command/type is evaluated by the tool. OPTIONS - -l List all of the devices that are currently being emulated by RaSCSI, as well as their current status. + -C FILENAME:FILESIZE + Create an image file in the default image folder with the speci‐ + fied name and size in bytes. + + -D Detach all devices. + + -E FILENAME + Display information on an image file. + + -F IMAGE_FOLDER + Set the default image folder. + + -I Gets the list of reserved device IDs. + + -L LOG_LEVEL + Set the rascsi log level (trace, debug, info, warn, err, criti‐ + cal, off). + + -h HOST + The rascsi host to connect to, default is 'localhost'. + + -e List all images files in the default image folder. + + -N Lists all available network interfaces provided that they are + up. + + -O Display the available rascsi server log levels and the current + log level. + + -l List all of the devices that are currently being emulated by + RaSCSI, as well as their current status. + + -m List all file extensions recognized by RaSCSI and the device + types they map to. + + -R CURRENT_NAME:NEW_NAME + Rename an image file in the default image folder. + + -p PORT + The rascsi port to connect to, default is 6868. + + -r RESERVED_IDS + Comma-separated list of IDs to reserve. + + -s Display server-side settings like available images or supported + device types. + + -T Display all device types and their properties. + + -v Display the rascsi server version. + + -V Display the rasctl version. + + -X Shut down the rascsi process. + + -d FILENAME + Delete an image file in the default image folder. + + -x CURRENT_NAME:NEW_NAME + Copy an image file in the default image folder. -i ID ID is the SCSI ID that you want to control. (0-7) - -u UNIT - Unit number (0 or 1). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common) + -c CMD Command is the operation being requested. Options are: + a(ttach): Attach disk + d(etach): Detach disk + i(nsert): Insert media (removable media devices only) + e(ject): Eject media (removable media devices only) + p(rotect): Write protect the medium (not for CD-ROMs, which + are always read-only) + u(nprotect): Remove write protection from the medium (not for + CD-ROMs, which are always read-only) + s(how): Display device information - -c CMD Command is the operation being requested. options are: - attach: attach disk - detach: detach disk - insert: insert media (Magneto-Optical and CD only) - eject: eject media (Magneto-Optical and CD only) - protect: Write protect the media (Magneto-Optical only) + eject, protect and unprotect are idempotent. - When the command is omited, rasctl will default to the 'attach' command + -b BLOCK_SIZE + The optional block size. For SCSI drives 512, 1024, 2048 or 4096 + bytes, default size is 512 bytes. For SASI drives 256 or 1024 + bytes, default is 256 bytes. + + -f FILE|PARAM + Device-specific: Either a path to a disk image file, or a param‐ + eter for a non-disk device. See the rascsi(1) man page for per‐ + mitted file types. -t TYPE - Specifies the type of disk. If this disagrees with the file extension of the specified image, the TYPE argument is ignored. Available drive types are: - hd: Hard disk (SCSI or SASI) - mo: Magneto-Optical disk) + Specifies the device type. This type overrides the type derived + from the file extension of the specified image. See the + rascsi(1) man page for the available device types. For some + types there are shortcuts (only the first letter is required): + hd: SCSI hard disk drive + rm: SCSI removable media drive cd: CD-ROM - bridge: Bridge device (This is only applicable to the Sharp X68000) + mo: Magneto-Optical disk + bridge: Bridge device (Only applicable to the Sharp X68000) + daynaport: DaynaPORT network adapter - -f FILE - Path to the disk image file. See the rascsi(1) man page for allowable file types. + -n VENDOR:PRODUCT:REVISION + The vendor, product and revision for the device, to be returned + with the INQUIRY data. A complete set of name components must be + provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up + to 4 characters. Padding with blanks to the maxium length is au‐ + tomatically applied. Once set the name of a device cannot be + changed. + + -u UNIT + Unit number (0-31). This will default to 0. This option is only + used when there are multiple SCSI devices on a shared SCSI con‐ + troller. (This is not common) EXAMPLES - Show a listing of all of the SCSI devices and their current status + Show a listing of all of the SCSI devices and their current status. rasctl -l Example output: - +----+----+------+------------------------------------- - | ID | UN | TYPE | DEVICE STATUS - +----+----+------+------------------------------------- - | 0 | 1 | SCHD | /home/pi/harddisk.hda - +----+----+------+------------------------------------- + +----+-----+------+------------------------------------- + | ID | LUN | TYPE | IMAGE FILE + +----+-----+------+------------------------------------- + | 0 | 1 | SCHD | /home/pi/harddisk.hda + +----+-----+------+------------------------------------- - Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image "HDIIMAGE0.HDS". + Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with + the contents of the file system image "HDIIMAGE0.HDS". rasctl -i 0 -f HDIIMAGE0.HDS SEE ALSO - rascsi(1) scsimon(1) + rascsi(1), scsimon(1), rasdump(1), sasidump(1) - Full documentation is available at: + Full documentation is available at: + - rascsi(1) + rascsi(1) diff --git a/doc/rasdump.1 b/doc/rasdump.1 new file mode 100644 index 00000000..43d38e1d --- /dev/null +++ b/doc/rasdump.1 @@ -0,0 +1,35 @@ +.TH rasdump 1 +.SH NAME +rasdump \- SCSI disk dumping tool for RaSCSI +.SH SYNOPSIS +.B rasdump +\fB\-i\fR \fIID\fR +[\fB\-b\fR \fIBID\fR] +\fB\-f\fR \fIFILE\fR +[\fB\-r\fR] +.SH DESCRIPTION +.B rasdump +Samples the data on physical SCSI storage media, including hard drives and MO drives, and stores it to an image file. It can also restore from a dumped file onto physical SCSI storage media. Can be connected directly, through a STANDARD RaSCSI board, or a FULLSPEC RaSCSI board. + +Set its own ID with the BID option. Defaults to 7 if ommitted. + +.SH OPTIONS +.TP +.BR \-i\fI " "\fIID +SCSI ID of the target device +.TP +.BR \-b\fI " "\fIBID +SCSI ID of the RaSCSI device +.TP +.BR \-f\fI " "\fIFILE +Path to the dump file +.TP +.BR \-r\fI +Restoration mode + +.SH EXAMPLES + +.SH SEE ALSO +rasctl(1), rascsi(1), scsimon(1), sasidump(1) + +Full documentation is available at: diff --git a/doc/sasidump.1 b/doc/sasidump.1 new file mode 100644 index 00000000..ce6df31c --- /dev/null +++ b/doc/sasidump.1 @@ -0,0 +1,41 @@ +.TH sasidump 1 +.SH NAME +sasidump \- SASI disk dumping tool for RaSCSI +.SH SYNOPSIS +.B sasidump +\fB\-i\fR \fIID\fR +[\fB\-u\fR \fIUT\fR] +[\fB\-b\fR \fIBSIZE\fR] +\fB\-c\fR \fICOUNT\fR +\fB\-f\fR \fIFILE\fR +[\fB\-r\fR] +.SH DESCRIPTION +.B sasidump +Samples the data on physical SASI storage media, and stores it to an image file. It can also restore from a dumped file onto physical SASI storage media. + +.SH OPTIONS +.TP +.BR \-i\fI " "\fIID +SASI ID of the target device +.TP +.BR \-u\fI " "\fIUD +Unit ID of the target device +.TP +.BR \-b\fI " "\fIBSIZE +Block size (default is 512) +.TP +.BR \-c\fI " "\fICOUNT +Block count +.TP +.BR \-f\fI " "\fIFILE +Path to the dump file +.TP +.BR \-r\fI +Restoration mode + +.SH EXAMPLES + +.SH SEE ALSO +rasctl(1), rascsi(1), scsimon(1), rasdump(1) + +Full documentation is available at: diff --git a/doc/scsimon.1 b/doc/scsimon.1 index 670c2103..454894b0 100644 --- a/doc/scsimon.1 +++ b/doc/scsimon.1 @@ -9,7 +9,7 @@ Monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is .PP The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched. -Currently, scsimon doesn't accept any agruments. +Currently, scsimon doesn't accept any arguments. To quit scsimon, press Control + C. @@ -22,6 +22,6 @@ Launch scsimon to capture all SCSI traffic available to the RaSCSI hardware: scsimon .SH SEE ALSO -rasctl(1), rascsi(1) +rasctl(1), rascsi(1), rasdump(1), sasidump(1) Full documentation is available at: diff --git a/doc/scsimon_man_page.txt b/doc/scsimon_man_page.txt index 0e7db3de..39c5f469 100644 --- a/doc/scsimon_man_page.txt +++ b/doc/scsimon_man_page.txt @@ -2,21 +2,26 @@ !! ------ The native file is scsimon.1. Re-run 'make docs' after updating -scsimon(1) General Commands Manual scsimon(1) +scsimon(1) General Commands Manual scsimon(1) NAME - scsimon - Acts as a data capture tool for all traffic on the SCSI bus. Data is stored in a Value Change Dump (VCD) file. + scsimon - Acts as a data capture tool for all traffic on the SCSI bus. + Data is stored in a Value Change Dump (VCD) file. SYNOPSIS scsimon DESCRIPTION - scsimon Monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is running. A circular buffer is used so that only the most recent 1,000,000 - transactions are stored. The tool will continue to run until the user presses CTRL-C, or the process receives a SIGINT signal. + scsimon Monitors all of the traffic on the SCSI bus, using a RaSCSI de‐ + vice. The data is cached in memory while the tool is running. A circu‐ + lar buffer is used so that only the most recent 1,000,000 transactions + are stored. The tool will continue to run until the user presses CTRL- + C, or the process receives a SIGINT signal. - The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched. + The logged data is stored in a file called "log.vcd" in the current + working directory from where scsimon was launched. - Currently, scsimon doesn't accept any agruments. + Currently, scsimon doesn't accept any arguments. To quit scsimon, press Control + C. @@ -24,12 +29,14 @@ OPTIONS None EXAMPLES - Launch scsimon to capture all SCSI traffic available to the RaSCSI hardware: + Launch scsimon to capture all SCSI traffic available to the RaSCSI + hardware: scsimon SEE ALSO - rasctl(1), rascsi(1) + rasctl(1), rascsi(1), rasdump(1), sasidump(1) - Full documentation is available at: + Full documentation is available at: + - scsimon(1) + scsimon(1) diff --git a/doc/target.png b/doc/target.png deleted file mode 100644 index b25e1049..00000000 Binary files a/doc/target.png and /dev/null differ diff --git a/doc/x68k.txt b/doc/x68k.txt deleted file mode 100644 index de40ac49..00000000 --- a/doc/x68k.txt +++ /dev/null @@ -1,114 +0,0 @@ ------------------------------------------------------------------------------- - - SCSI Target Emulator RaSCSII (*^..^*) - for Raspberry Pi - - Powered by XM6 TypeG Technology. - Copyright (C) 2016-2020 GIMONS - ------------------------------------------------------------------------------- -□X68000固有の機能について - RaSCSIにはブリッジデバイスという仮想的なSCSIデバイスが実装されておりX68000と - ホストであるRaspberry Piの橋渡しを行うことができます。このブリッジデバイスと - X68000用の専用ドライバを使用して下記の機能を提供します。 - - ・イーサーネット - Neptune-Xと同様のイーサーネット機能を提供します。SCSI接続のイーサーネット - BOXのようにRaSCSIが振舞います。Raspberry PiのTAPデバイスにパケットを中継 - することで実現しています。Ether+と似たものです。 - - ・ホストファイルシステム - X68000のエミュレーターでは標準的な機能であるWindrv相当の機能を提供します。 - Raspberry Pi上のファイルシステムをリモートドライブとしてマウントすること - ができます。 - -□ブジッジデバイスの起動方法 - RaSCSI起動時にファイル名として"BRIDGE"というキーワードを設定するとそのIDに - 対してブジッリデバイスを生成します。 - - ex) - sudo rascsi -ID0 HDIMAGE0.HDS -ID6 BRIDGE - -□専用ドライバ - 配布物に含まれるRASDRIVER.XDFもしくはRASDRIVER.HDSに二つのドライバが含まれ - ています。RaSCSIでRASDRIVER.HDSをマウントし適宜コピーした方が実機への転送が - 簡単でしょう。 - -□イーサーネット接続 - イーサネットドライバ(RASETHER.SYS)がブリッジデバイスと連携してイーサーネット - のパケット送受信を行うことができます。 - - 以下、Raspberry Piの仮想アダプタ(TAP)のIPアドレスを「192.168.68.1」として、 - X68000側を「192.168.68.3」とするケースで説明します。 - - ・X68000の設定 - RASETHER.SYSはNeptune-X用ドライバを改造して作ったものですので使用方法は - 全く同じです。X68000をネット接続するためには他に環境設定を行う必要があり - ます。設定方法は自力で調べていただくようお願いします。 - - 以下実際に使用しているCONFIG.SYSとAUTOEXEC.BATの抜粋です。 - - [CONFIG.SYS抜粋] - PROCESS = 3 10 10 - DEVICE = \NETWORK\RASETHER.SYS - - [AUTOEXEC.BAT抜粋] - SET SYSROOT=A:/NETWORK/ - SET temp=A:\ - SET tmp=A:\ - SET HOME=A:/NETWORK/ETC/ - SET HOST=X68000 - XIP.X - IFCONFIG.X lp0 up - IFCONFIG.X en0 192.168.68.3 netmask 255.255.255.0 up - INETDCONF.X +router 192.168.68.1 -rip - INETDCONF.X - - ・Raspberry Piの設定 - TAPデバイスというものを利用していますのでTAPを有効にしてください。恐らく - 最近のJessieであれば最初から有効なはずです。確認方法は/dev/net/tunという - ファイルが存在すれば有効となっていると判断できます。 - - 仮想アダプタの作成方法は次の様に行います。 - - [/etc/rc.local等から設定] - ip tuntap add ras0 mode tap user root - ip link set ras0 up - ifconfig ras0 inet 192.168.68.1/8 up - route add -net 192.168.68.0 netmask 255.255.255.0 dev ras0 - - 上記によってRaspberry Pi(192.168.68.1)とX68000(192.168.68.3)の間で通信が - 可能になります。 - - インターネット等と接続する場合はRaspberry Pi側でブリッジやルーティングの - 設定が必要になります。ご自身の環境に合わせて設定してください。無線LANの - 場合にブリッジ構成するには色々と課題があるようなのでフォワーディングと - NAT構成等もお勧めです。作者はrc.localで次のような設定で使用しています。 - - echo 1 > /proc/sys/net/ipv4/ip_forward - iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE - -□ホストファイルシステム連携 - X68000エミュレータでよく利用されるWindrvやWindrvXMと同等の機能を提供します。 - 専用のRASDRV.SYSというデバイスドライバを組み込めばRaspberri Piのファイル - システムがX68000側のドライブに見えて操作できるということです。 - - デバイスドライバの登録は簡単です。 - 例えば - - DEVICE = \SYS\RASDRV.SYS - - この場合はデフォルトでRaspberry Piのルートディレクトリをマウントします。 - デバイスドライバ起動時にどのドライブにマウントされたか表示されます。 - ルートを以外をマウントする場合はディレクトリを指定して下さい。/home/pi等を - マウントするには - - DEVICE = \SYS\RASDRV.SYS /home/pi - - と指定します。複数のディレクトリを指定すれば別々のドライブとしてマウントする - ことが可能です。 - - SUSIEをご利用の方はSUSIEより先にRASDRV.SYSを組み込んで下さい。後に組み込むと - 正しく認識できなくなると報告があります。 - -[EOF] diff --git a/doc/x68k_en.txt b/doc/x68k_en.txt deleted file mode 100644 index d536fdef..00000000 --- a/doc/x68k_en.txt +++ /dev/null @@ -1,114 +0,0 @@ --------------------------------------------------- ---------------------------- - - SCSI Target Emulator RaSCSII (*^..^*) - for Raspberry Pi - - Powered by XM6 TypeG Technology. - Copyright (C) 2016-2020 GIMONS - --------------------------------------------------- ---------------------------- -□ Regarding the functions unique to the X68000 - RaSCSI is equipped with a virtual SCSI device called a bridge device, - You can bridge the host Raspberry Pi. With this bridge device - The following functions are provided by using the dedicated driver for X68000. - - ・Ethernet - It provides the same Ethernet function as Neptune-X. SCSI-connected Ethernet - RaSCSI behaves like a BOX. Relay packets to the Raspberry Pi TAP device - It is realized by doing. Similar to Ether+. - - ・Host file system - The emulator of X68000 provides a standard function equivalent to Windrv. - Mount the file system on the Raspberry Pi as a remote drive - I can. - -□ How to activate the Bridge Device - If you set the keyword "BRIDGE" as the file name when RaSCSI starts up, it will be assigned to that ID. - Generate a Buzzilli device for it. - - ex) - sudo rascsi -ID0 HDIMAGE0.HDS -ID6 BRIDGE - -□ Dedicated driver - Two drivers are included in RASDRIVER.XDF or RASDRIVER.HDS included in the distribution - I am. It is better to mount RASDRIVER.HDS with RaSCSI and copy it appropriately to transfer to the actual device - It will be easy. - -□ Ethernet connection - Ethernet driver (RASETHER.SYS) works with bridge device to connect to Ethernet - You can send and receive packets. - - Below, the IP address of the virtual adapter (TAP) of Raspberry Pi is "192.168.68.1", - The case where the X68000 side is set to "192.168.68.3" will be explained. - - -X68000 settings - RASETHER.SYS is made by modifying the driver for Neptune-X. - Exactly the same. In order to connect the X68000 to the internet, it is necessary to set other environment settings. - I will. Please check the setting method by yourself. - - Below is an excerpt of CONFIG.SYS and AUTOEXEC.BAT that are actually used. - - [Excerpt from CONFIG.SYS] - PROCESS = 3 10 10 - DEVICE = \NETWORK\RASETHER.SYS - - [Excerpt from AUTOEXEC.BAT] - SET SYSROOT=A:/NETWORK/ - SET temp=A:\ - SET tmp=A:\ - SET HOME=A:/NETWORK/ETC/ - SET HOST=X68000 - XIP.X - IFCONFIG.X lp0 up - IFCONFIG.X en0 192.168.68.3 netmask 255.255.255.0 up - INETDCONF.X +router 192.168.68.1 -rip - INETDCONF.X - - ・Raspberry Pi settings - I am using a TAP device, so please enable TAP. perhaps - If you're using Jessie these days, it should work from the beginning. The confirmation method is /dev/net/tun - If the file exists, it can be determined that it is valid. - - The method of creating a virtual adapter is as follows. - - [Set from /etc/rc.local etc.] - ip tuntap add ras0 mode tap user root - ip link set ras0 up - ifconfig ras0 inet 192.168.68.1/8 up - route add -net 192.168.68.0 netmask 255.255.255.0 dev ras0 - - By the above, communication between Raspberry Pi (192.168.68.1) and X68000 (192.168.68.3) - It will be possible. - - When connecting to the Internet etc., bridge and routing on the Raspberry Pi side - Settings are required. Please set according to your environment. Wifi - In this case, there seem to be various problems in constructing a bridge. - NAT configuration is also recommended. The author uses it in rc.local with the following settings. - - echo 1 >/proc/sys/net/ipv4/ip_forward - iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE - -□ Host file system cooperation - It provides the same functions as Windrv and WindrvXM that are often used in the X68000 emulator. - Raspberri Pi files if you include a dedicated device driver called RASDRV.SYS - It means that the system can be seen and operated as a drive on the X68000 side. - - Registering a device driver is easy. - For example - - DEVICE = \SYS\RASDRV.SYS - - In this case, mount the root directory of Raspberry Pi by default. - The drive mounted when the device driver is started is displayed. - If you want to mount other than root, please specify the directory. /home/pi etc. - To mount - - DEVICE = \SYS\RASDRV.SYS /home/pi - - Specify. If you specify multiple directories, they will be mounted as separate drives. - It is possible. - - If you are using SUSIE, please install RASDRV.SYS before SUSIE. When incorporated later - There are reports that it can not be recognized correctly. - -[EOF] \ No newline at end of file diff --git a/easyinstall.sh b/easyinstall.sh index 66728bdc..53a36c69 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # BSD 3-Clause License # Author @sonique6784 @@ -20,11 +20,43 @@ logo=""" echo -e $logo } -VIRTUAL_DRIVER_PATH=/home/pi/images +function showMacNetworkWired(){ +logo=""" +                              .-~-.-~~~-.~-.\n + ╔═══════╗                  .(              )\n + ║|¯¯¯¯¯|║                 /               \`.\n + ║|_____|║>--------------<~               .   )\n + ║ .  __ ║                 (              :'-'\n + ╚╦═════╦╝                  ~-.________.:'\n +  ¯¯¯¯¯¯¯\n +""" +echo -e $logo +} + +function showMacNetworkWireless(){ +logo=""" +                              .-~-.-~~~-.~-.\n + ╔═══════╗        .(       .(              )\n + ║|¯¯¯¯¯|║  .(  .(        /               \`.\n + ║|_____|║ .o    o       ~               .   )\n + ║ .  __ ║  '(  '(        (              :'-'\n + ╚╦═════╦╝        '(       ~-.________.:'\n +  ¯¯¯¯¯¯¯\n +""" +echo -e $logo +} + +BASE="$HOME/RASCSI" +VIRTUAL_DRIVER_PATH="$HOME/images" +CFG_PATH="$HOME/.config/rascsi" +WEBINSTDIR="$BASE/src/web" HFS_FORMAT=/usr/bin/hformat HFDISK_BIN=/usr/bin/hfdisk -LIDO_DRIVER=~/RASCSI/lido-driver.img +LIDO_DRIVER=$BASE/lido-driver.img GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +GIT_REMOTE=${GIT_REMOTE:-origin} + +set -e function initialChecks() { currentUser=$(whoami) @@ -33,30 +65,36 @@ function initialChecks() { exit 1 fi - if [ ! -d ~/RASCSI ]; then - echo "You must checkout RASCSI repo into /home/pi/RASCSI" + if [ ! -d "$BASE" ]; then + echo "You must checkout RASCSI repo into $BASE" echo "$ git clone git@github.com:akuker/RASCSI.git" exit 2 fi } +# install all dependency packages for RaSCSI Service function installPackages() { - sudo apt-get update && sudo apt install git libspdlog-dev genisoimage python3 python3-venv nginx bridge-utils -y + sudo apt-get update && sudo apt-get install git libspdlog-dev libpcap-dev genisoimage python3 python3-venv nginx libpcap-dev protobuf-compiler bridge-utils python3-dev libev-dev libevdev2 -y > /etc/sudoers' + else + echo "The sudoers file is already modified for rascsi-web." fi + sudo systemctl daemon-reload sudo systemctl restart rsyslog sudo systemctl enable rascsi # optional - start rascsi at boot sudo systemctl start rascsi + + startRaScsiScreen +} + +# install everything required to run an HTTP server (Nginx + Python Flask App) +function installRaScsiWebInterface() { + if [ -f "$WEBINSTDIR/rascsi_interface_pb2.py" ]; then + rm "$WEBINSTDIR/rascsi_interface_pb2.py" + echo "Deleting old Python protobuf library rascsi_interface_pb2.py" + fi + echo "Compiling the Python protobuf library rascsi_interface_pb2.py..." + protoc -I="$BASE/src/raspberrypi/" --python_out="$WEBINSTDIR" rascsi_interface.proto + + sudo cp -f "$BASE/src/web/service-infra/nginx-default.conf" /etc/nginx/sites-available/default + sudo cp -f "$BASE/src/web/service-infra/502.html" /var/www/html/502.html + + sudo usermod -a -G pi www-data + + sudo systemctl reload nginx || true + + echo "Installing the rascsi-web.service configuration..." + sudo cp -f "$BASE/src/web/service-infra/rascsi-web.service" /etc/systemd/system/rascsi-web.service + + sudo systemctl daemon-reload + sudo systemctl enable rascsi-web + sudo systemctl start rascsi-web +} + +function installRaScsiScreen() { + echo "IMPORTANT: This configuration requires a OLED screen to be installed onto your RaSCSI board." + echo "See wiki for more information: https://github.com/akuker/RASCSI/wiki/OLED-Status-Display-(Optional)" + echo "" + echo "Do you want to use the recommended screen rotation (180 degrees)?" + echo "Press Y/n and Enter, or CTRL-C to exit" + read REPLY + + if [ "$REPLY" == "N" ] || [ "$REPLY" == "n" ]; then + echo "Proceeding with 0 degrees rotation." + ROTATION="0" + else + echo "Proceeding with 180 degrees rotation." + ROTATION="180" + fi + + stopRaScsiScreen + updateRaScsiGit + + sudo apt-get update && sudo apt-get install python3-dev python3-pip python3-venv libjpeg-dev libpng-dev libopenjp2-7-dev i2c-tools raspi-config -y /dev/null; echo $?) if [ "$APACHE_STATUS" -eq 0 ] ; then echo "Stopping old Apache2 RaSCSI Web..." @@ -80,61 +227,63 @@ function stopOldWebInterface() { fi } -# install everything required to run an HTTP server (Nginx + Python Flask App) -function installRaScsiWebInterface() { - stopOldWebInterface - installPackages - - sudo cp -f ~/RASCSI/src/web/service-infra/nginx-default.conf /etc/nginx/sites-available/default - sudo cp -f ~/RASCSI/src/web/service-infra/502.html /var/www/html/502.html - - mkdir -p $VIRTUAL_DRIVER_PATH - chmod -R 775 $VIRTUAL_DRIVER_PATH - groups www-data - sudo usermod -a -G pi www-data - groups www-data - - sudo systemctl reload nginx - - sudo cp ~/RASCSI/src/web/service-infra/rascsi-web.service /etc/systemd/system/rascsi-web.service - sudo systemctl daemon-reload - sudo systemctl enable rascsi-web - sudo systemctl start rascsi-web -} - function updateRaScsiGit() { - echo "Updating checked out branch $GIT_BRANCH" - cd ~/RASCSI - git fetch origin - git stash - git rebase origin/$GIT_BRANCH - git stash apply + cd "$BASE" || exit 1 + stashed=0 + if [[ $(git diff --stat) != '' ]]; then + echo "There are local changes to the RaSCSI code; we will stash and reapply them." + git -c user.name="${GIT_COMMITTER_NAME-rascsi}" -c user.email="${GIT_COMMITTER_EMAIL-rascsi@rascsi.com}" stash + stashed=1 + fi + + if [[ `git for-each-ref --format='%(upstream:short)' "$(git symbolic-ref -q HEAD)"` != "" ]]; then + echo "Updating checked out git branch $GIT_REMOTE/$GIT_BRANCH" + git pull --ff-only + else + echo "Detected a local git working branch; skipping the remote update step." + fi + + if [ $stashed -eq 1 ]; then + echo "Reapplying local changes..." + git stash apply + fi } -function updateRaScsi() { - updateRaScsiGit - installPackages - sudo systemctl stop rascsi - - cd ~/RASCSI/src/raspberrypi - - make clean - make all CONNECT_TYPE=FULLSPEC - sudo make install CONNECT_TYPE=FULLSPEC - sudo systemctl start rascsi +function stopRaScsi() { + if [[ `systemctl list-units | grep -c rascsi.service` -ge 1 ]]; then + sudo systemctl stop rascsi.service + fi } -function updateRaScsiWebInterface() { - stopOldWebInterface - updateRaScsiGit - sudo cp -f ~/RASCSI/src/web/service-infra/nginx-default.conf /etc/nginx/sites-available/default - sudo cp -f ~/RASCSI/src/web/service-infra/502.html /var/www/html/502.html - sudo systemctl restart rascsi-web - sudo systemctl restart nginx +function stopRaScsiWeb() { + if [[ `systemctl list-units | grep -c rascsi-web.service` -ge 1 ]]; then + sudo systemctl stop rascsi-web.service + fi +} + +function stopRaScsiScreen() { + if [[ `systemctl list-units | grep -c monitor_rascsi.service` -ge 1 ]]; then + sudo systemctl stop monitor_rascsi.service + fi +} + +function startRaScsiScreen() { + if [[ -f "/etc/systemd/system/monitor_rascsi.service" ]]; then + sudo systemctl start monitor_rascsi.service + showRaScsiScreenStatus + fi } function showRaScsiStatus() { - sudo systemctl status rascsi + sudo systemctl status rascsi | tee +} + +function showRaScsiWebStatus() { + sudo systemctl status rascsi-web | tee +} + +function showRaScsiScreenStatus() { + sudo systemctl status monitor_rascsi | tee } function createDrive600MB() { @@ -151,7 +300,7 @@ function createDriveCustom() { read driveName done - createDrive $driveSize "$driveName" + createDrive "$driveSize" "$driveName" } function formatDrive() { @@ -160,30 +309,46 @@ function formatDrive() { if [ ! -x $HFS_FORMAT ]; then # Install hfsutils to have hformat to format HFS - sudo apt-get install hfsutils + sudo apt-get install hfsutils --assume-yes > /etc/dhcpcd.conf' + echo "Modified /etc/dhcpcd.conf" + + # default config file is made for eth0, this will set the right net interface + sudo bash -c 'sed s/eth0/'"$LAN_INTERFACE"'/g '"$BASE"'/src/raspberrypi/os_integration/rascsi_bridge > /etc/network/interfaces.d/rascsi_bridge' + echo "Modified /etc/network/interfaces.d/rascsi_bridge" + + echo "Configuration completed!" + echo "Please make sure you attach a DaynaPORT network adapter to your RaSCSI configuration." + echo "Either use the Web UI, or do this on the command line (assuming SCSI ID 6):" + echo "rasctl -i 6 -c attach -t scdp -f $LAN_INTERFACE" + echo "" + echo "We need to reboot your Pi" + echo "Press Enter to reboot or CTRL-C to exit" + read + + echo "Rebooting..." + sleep 3 + sudo reboot +} + +function setupWirelessNetworking() { + NETWORK="10.10.20" + IP=$NETWORK.2 # Macintosh or Device IP + NETWORK_MASK="255.255.255.0" + CIDR="24" + ROUTER_IP=$NETWORK.1 + ROUTING_ADDRESS=$NETWORK.0/$CIDR + WLAN_INTERFACE="wlan0" + + echo "$WLAN_INTERFACE will be configured for network forwarding with static IP assignment." + echo "Configure your Macintosh or other device with the following:" + echo "IP Address (static): $IP" + echo "Router Address: $ROUTER_IP" + echo "Subnet Mask: $NETWORK_MASK" + echo "DNS Server: Any public DNS server" + echo "" + echo "Do you want to proceed with network configuration using the default settings? Y/n" + read REPLY + + if [ "$REPLY" == "N" ] || [ "$REPLY" == "n" ]; then + echo "Available wireless interfaces on this system:" + echo `ip -o addr show scope link | awk '{split($0, a); print $2}' | grep wlan` + echo "Please type the wireless interface you want to use and press Enter:" + read -r WLAN_INTERFACE + echo "Base IP address (ex. 10.10.20):" + read -r NETWORK + echo "CIDR for Subnet Mask (ex. '24' for 255.255.255.0):" + read -r CIDR + ROUTER_IP=$NETWORK.1 + ROUTING_ADDRESS=$NETWORK.0/$CIDR + fi + + + if [ "$(grep -c "^net.ipv4.ip_forward=1" /etc/sysctl.conf)" -ge 1 ]; then + echo "WARNING: Network forwarding may already have been configured. Proceeding will overwrite the configuration." + echo "Press enter to continue or CTRL-C to exit" + read REPLY + else + sudo bash -c 'echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf' + echo "Modified /etc/sysctl.conf" + fi + + sudo iptables --flush + sudo iptables -t nat -F + sudo iptables -X + sudo iptables -Z + sudo iptables -P INPUT ACCEPT + sudo iptables -P OUTPUT ACCEPT + sudo iptables -P FORWARD ACCEPT + sudo iptables -t nat -A POSTROUTING -o "$WLAN_INTERFACE" -s "$ROUTING_ADDRESS" -j MASQUERADE + + # Check if iptables-persistent is installed + if [ `apt-cache policy iptables-persistent | grep Installed | grep -c "(none)"` -eq 0 ]; then + echo "iptables-persistent is already installed" + sudo iptables-save --file /etc/iptables/rules.v4 + else + sudo apt-get install iptables-persistent --assume-yes > ./config/AppleVolumes.default.tmpl + + echo "ATALKD_RUN=yes" >> ./config/netatalk.conf + echo "\"RaSCSI-Pi\" -transall -uamlist uams_guest.so,uams_clrtxt.so,uams_dhx.so -defaultvol /etc/netatalk/AppleVolumes.default -systemvol /etc/netatalk/AppleVolumes.system -nosavepassword -nouservol -guestname \"nobody\" -setuplog \"default log_maxdebug /var/log/afpd.log\"" >> ./config/afpd.conf.tmpl + + ( sudo apt-get update && sudo apt-get install libssl-dev libdb-dev libcups2-dev autotools-dev automake libtool --assume-yes ) 1: + if str(argv[1]) == "0": + rotation = 0 + elif str(argv[1]) == "180": + rotation = 2 + else: + exit("Only 0 and 180 are valid arguments for screen rotation.") +else: + print("Defaulting to 180 degrees screen rotation.") + rotation = 2 + # Clear display. +oled.rotation = rotation oled.fill(0) oled.show() @@ -74,57 +123,219 @@ draw.rectangle((0,0,WIDTH,HEIGHT), outline=0, fill=0) # Draw some shapes. # First define some constants to allow easy resizing of shapes. -padding = -2 +padding = 0 top = padding bottom = HEIGHT-padding # Move left to right keeping track of the current x position for drawing shapes. x = 0 # Load default font. -font = ImageFont.load_default() +#font = ImageFont.load_default() # Alternatively load a TTF font. Make sure the .ttf font file is in the same directory as the python script! +# When using other fonds, you may need to adjust padding, font size, and line spacing # Some other nice fonts to try: http://www.dafont.com/bitmap.php -# font = ImageFont.truetype('Minecraftia.ttf', 8) +font = ImageFont.truetype('type_writer.ttf', 8) -while True: - # Draw a black filled box to clear the image. - draw.rectangle((0,0,WIDTH,HEIGHT), outline=0, fill=0) - - cmd = "rasctl -l" - rascsi_list = subprocess.check_output(cmd, shell=True).decode(sys.stdout.encoding) +def device_list(): + """ + Sends a DEVICES_INFO command to the server. + Returns a list of dicts with info on all attached devices. + """ + command = proto.PbCommand() + command.operation = proto.PbOperation.DEVICES_INFO + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) - y_pos = top - # Draw all of the meaningful data to the 'image' - # - # Example rascstl -l output: - # pi@rascsi:~ $ rasctl -l - # - # +----+----+------+------------------------------------- - # | ID | UN | TYPE | DEVICE STATUS - # +----+----+------+------------------------------------- - # | 1 | 0 | SCHD | /home/pi/harddisk.hda - # | 6 | 0 | SCCD | NO MEDIA - # +----+----+------+------------------------------------- - # pi@rascsi:~ $ - for line in rascsi_list.split('\n'): - # Skip empty strings, divider lines and the header line... - if (len(line) == 0) or line.startswith("+---") or line.startswith("| ID | UN"): - continue - else: - if line.startswith("| "): - fields = line.split('|') - output = str.strip(fields[1]) + " " + str.strip(fields[3]) + " " + os.path.basename(str.strip(fields[4])) - else: - output = "No image mounted!" - draw.text((x, y_pos), output, font=font, fill=255) - y_pos += 8 + device_list = [] + n = 0 - # If there is still room on the screen, we'll display the time. If there's not room it will just be clipped - draw.text((x, y_pos), datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S"), font=font, fill=255) + while n < len(result.devices_info.devices): + did = result.devices_info.devices[n].id + dtype = proto.PbDeviceType.Name(result.devices_info.devices[n].type) + dstat = result.devices_info.devices[n].status + dprop = result.devices_info.devices[n].properties - # Display image. - oled.image(image) + # Building the status string + dstat_msg = [] + if dstat.protected == True and dprop.protectable == True: + dstat_msg.append("Write-Protected") + if dstat.removed == True and dprop.removable == True: + dstat_msg.append("No Media") + if dstat.locked == True and dprop.lockable == True: + dstat_msg.append("Locked") + + dfile = path.basename(result.devices_info.devices[n].file.name) + dven = result.devices_info.devices[n].vendor + dprod = result.devices_info.devices[n].product + + device_list.append( + { + "id": did, + "device_type": dtype, + "status": ", ".join(dstat_msg), + "file": dfile, + "vendor": dven, + "product": dprod, + } + ) + n += 1 + + return device_list + +def get_ip_and_host(): + host = socket.gethostname() + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + # mock ip address; doesn't have to be reachable + s.connect(('10.255.255.255', 1)) + ip = s.getsockname()[0] + except Exception: + ip = '127.0.0.1' + finally: + s.close() + return ip, host + +def send_pb_command(payload): + """ + Takes a str containing a serialized protobuf as argument. + Establishes a socket connection with RaSCSI. + """ + # Host and port number where rascsi is listening for socket connections + HOST = 'localhost' + PORT = 6868 + + counter = 0 + tries = 100 + error_msg = "" + + while counter < tries: + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((HOST, PORT)) + return send_over_socket(s, payload) + except socket.error as error: + counter += 1 + print("The RaSCSI service is not responding - attempt " + \ + str(counter) + "/" + str(tries)) + error_msg = str(error) + + exit(error_msg) + + +def send_over_socket(s, payload): + """ + Takes a socket object and str payload with serialized protobuf. + Sends payload to RaSCSI over socket and captures the response. + Tries to extract and interpret the protobuf header to get response size. + Reads data from socket in 2048 bytes chunks until all data is received. + """ + + # Sending the magic word "RASCSI" to authenticate with the server + s.send(b"RASCSI") + # Prepending a little endian 32bit header with the message size + s.send(pack("= 4: + # Extracting the response header to get the length of the response message + response_length = unpack(" 5: + output.rotate(-1) + + # Display image. + oled.image(image) + oled.show() + sleep(1000/delay_time_ms) + + snapshot = formatted_output() + + if h.interrupted: + stop_splash() + exit("Shutting down the OLED display...") diff --git a/src/oled_monitor/requirements.txt b/src/oled_monitor/requirements.txt index 57c38c4c..be07f39f 100644 --- a/src/oled_monitor/requirements.txt +++ b/src/oled_monitor/requirements.txt @@ -1,14 +1,15 @@ -Adafruit-Blinka==6.3.2 -adafruit-circuitpython-busdevice==5.0.6 -adafruit-circuitpython-framebuf==1.4.6 -adafruit-circuitpython-ssd1306==2.11.1 -Adafruit-PlatformDetect==3.2.0 -Adafruit-PureIO==1.1.8 -Pillow==8.2.0 +Adafruit-Blinka==6.15.0 +adafruit-circuitpython-busdevice==5.1.0 +adafruit-circuitpython-framebuf==1.4.7 +adafruit-circuitpython-ssd1306==2.12.2 +Adafruit-PlatformDetect==3.13.0 +Adafruit-PureIO==1.1.9 +Pillow==8.4.0 pkg-resources==0.0.0 -pyftdi==0.52.9 +pyftdi==0.53.3 pyserial==3.5 -pyusb==1.1.1 -rpi-ws281x==4.2.5 +pyusb==1.2.1 +rpi-ws281x==4.3.0 RPi.GPIO==0.7.0 sysv-ipc==1.1.0 +protobuf==3.17.3 diff --git a/src/oled_monitor/splash_start.bmp b/src/oled_monitor/splash_start.bmp new file mode 100644 index 00000000..bd749374 Binary files /dev/null and b/src/oled_monitor/splash_start.bmp differ diff --git a/src/oled_monitor/splash_stop.bmp b/src/oled_monitor/splash_stop.bmp new file mode 100644 index 00000000..3dde1c7f Binary files /dev/null and b/src/oled_monitor/splash_stop.bmp differ diff --git a/src/oled_monitor/start.sh b/src/oled_monitor/start.sh index f0a37013..e48b63df 100755 --- a/src/oled_monitor/start.sh +++ b/src/oled_monitor/start.sh @@ -2,7 +2,7 @@ set -e # set -x # Uncomment to Debug -cd $(dirname $0) +cd "$(dirname "$0")" # verify packages installed ERROR=0 if ! command -v dpkg -l i2c-tools &> /dev/null ; then @@ -15,6 +15,11 @@ if ! command -v python3 &> /dev/null ; then echo "Run 'sudo apt install python3' to fix." ERROR=1 fi +if ! python3 -m venv --help &> /dev/null ; then + echo "venv could not be found" + echo "Run 'sudo apt install python3-venv' to fix." + ERROR=1 +fi # Dep to build Pillow if ! dpkg -l python3-dev &> /dev/null; then echo "python3-dev could not be found" @@ -31,10 +36,9 @@ if ! dpkg -l libpng-dev &> /dev/null; then echo "Run 'sudo apt install libpng-dev' to fix." ERROR=1 fi -# Dep to build Pollow -if ! python3 -m venv --help &> /dev/null ; then - echo "venv could not be found" - echo "Run 'sudo apt install python3-venv' to fix." +if ! dpkg -l libopenjp2-7-dev &> /dev/null; then + echo "libopenjp2-7-dev could not be found" + echo "Run 'sudo apt install libopenjp2-7-dev' to fix." ERROR=1 fi if [ $ERROR = 1 ] ; then @@ -43,6 +47,12 @@ if [ $ERROR = 1 ] ; then exit 1 fi +if pgrep -f "python3 rascsi_oled_monitor.py" &> /dev/null; then + echo "Detected active rascsi_oled_monitor.py process" + echo "Terminating before launching a new one." + sudo pkill -f "python3 rascsi_oled_monitor.py" +fi + if ! i2cdetect -y 1 &> /dev/null ; then echo "i2cdetect -y 1 did not find a screen." exit 2 @@ -53,6 +63,7 @@ if ! test -e venv; then echo "Activating venv" source venv/bin/activate echo "Installing requirements.txt" + pip install wheel pip install -r requirements.txt git rev-parse HEAD > current fi @@ -65,12 +76,34 @@ if ! test -e current; then else if [ "$(cat current)" != "$(git rev-parse HEAD)" ]; then echo "New version detected, updating requirements.txt" - echo " This may take some time..." - pip install wheel pip install -r requirements.txt git rev-parse HEAD > current fi fi -echo "Starting OLED Screen..." -python3 rascsi_oled_monitor.py +# parse arguments +while [ "$1" != "" ]; do + PARAM=$(echo "$1" | awk -F= '{print $1}') + VALUE=$(echo "$1" | awk -F= '{print $2}') + case $PARAM in + -r | --rotation) + ROTATION=$VALUE + ;; + *) + echo "ERROR: unknown parameter \"$PARAM\"" + exit 1 + ;; + esac + case $VALUE in + 0 | 180 ) + ;; + *) + echo "ERROR: invalid option \"$VALUE\"" + exit 1 + ;; + esac + shift +done + +echo "Starting OLED Screen with $ROTATION degrees rotation..." +python3 rascsi_oled_monitor.py "${ROTATION}" diff --git a/src/oled_monitor/type_writer.ttf b/src/oled_monitor/type_writer.ttf new file mode 100644 index 00000000..a8e88ccd Binary files /dev/null and b/src/oled_monitor/type_writer.ttf differ diff --git a/src/raspberrypi/.gitignore b/src/raspberrypi/.gitignore index b3172ad3..d81380e1 100644 --- a/src/raspberrypi/.gitignore +++ b/src/raspberrypi/.gitignore @@ -12,3 +12,8 @@ sasidump rasdump obj bin +/rascsi_interface.pb.cpp +/rascsi_interface.pb.h +.project +.cproject +.settings diff --git a/src/raspberrypi/.vscode/launch.json b/src/raspberrypi/.vscode/launch.json index 4ac5ab4b..e3cd4037 100644 --- a/src/raspberrypi/.vscode/launch.json +++ b/src/raspberrypi/.vscode/launch.json @@ -8,13 +8,15 @@ "name": "(gdb) Launch", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/bin/rascsi", + "program": "${workspaceFolder}/bin/fullspec/rascsi", "args": [], "stopAtEntry": true, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", + "targetArchitecture": "arm", + "miDebuggerPath": "${workspaceFolder}/launch_sudo.sh", "setupCommands": [ { "description": "Enable pretty-printing for gdb", @@ -24,4 +26,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/raspberrypi/.vscode/tasks.json b/src/raspberrypi/.vscode/tasks.json index eeedfe8a..6105ee09 100644 --- a/src/raspberrypi/.vscode/tasks.json +++ b/src/raspberrypi/.vscode/tasks.json @@ -5,7 +5,7 @@ "type": "shell", "label": "g++ build active file", "command": "make", - "args": ["all", "DEBUG=1"], + "args": ["all", "DEBUG=1", "-j4"], "options": { "cwd": "${workspaceFolder}/" }, @@ -16,4 +16,4 @@ } } ] - } \ No newline at end of file + } diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index 34d421a4..20b5f27a 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -1,18 +1,14 @@ .DEFAULT_GOAL: all ## Optional build flags: -## ARCH=arm : Specify which target platform you're compiling -## for. This will default to arm, which is typical -## on a Raspberry Pi. ## CROSS_COMPILE=arm-linux-gnueabihf- : Specify which compiler ## toolchain to use. This will default to arm-linux- ## gnueabihf-, which is typical on a Raspberry Pi. ## To cross compile on a x86_64 system set these to: ## ARM=x86_64 CROSS_COMPILE=x86_64-linux-gnu-cpp -ARCH ?= arm CROSS_COMPILE ?= arm-linux-gnueabihf- -CC = $(CROSS_COMPILE)gcc +CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ ## DEBUG=1 : A Debug build includes the debugger symbols @@ -21,8 +17,8 @@ CXX = $(CROSS_COMPILE)g++ DEBUG ?= 0 ifeq ($(DEBUG), 1) # Debug CFLAGS - CFLAGS += -DDISK_LOG -O0 -g -Wall -DDEBUG - CXXFLAGS += -DDISK_LOG -O0 -g -Wall -DDEBUG + CFLAGS += -O0 -g -Wall -DDEBUG + CXXFLAGS += -O0 -g -Wall -DDEBUG BUILD_TYPE = Debug else # Release CFLAGS @@ -30,19 +26,23 @@ else CXXFLAGS += -O3 -Wall -Werror -DNDEBUG BUILD_TYPE = Release endif -CFLAGS += -iquote . -MD -MP -CXXFLAGS += -std=c++14 -iquote . -MD -MP +CFLAGS += -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP +CXXFLAGS += -std=c++17 -Wno-psabi -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP -## CONNECT_TYPE=STANDARD : Specify the type of RaSCSI board type +## EXTRA_FLAGS : Can be used to pass special purpose flags +CFLAGS += $(EXTRA_FLAGS) +CXXFLAGS += $(EXTRA_FLAGS) + +## CONNECT_TYPE=FULLSPEC : Specify the type of RaSCSI board type ## that you are using. The typical options are -## STANDARD or FULLSPEC. The default is STANDARD +## STANDARD or FULLSPEC. The default is FULLSPEC ## * THIS IS TYPICALLY THE ONLY COMPILE OPTION YOU ## * NEED TO SPECIFY # If its not specified, build for FULLSPEC configuration CONNECT_TYPE ?= FULLSPEC ifdef CONNECT_TYPE -CFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE) +CFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE) CXXFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE) endif @@ -64,10 +64,18 @@ OS_FILES = ./os_integration OBJDIR := ./obj/$(shell echo $(CONNECT_TYPE) | tr '[:upper:]' '[:lower:]') BINDIR := ./bin/$(shell echo $(CONNECT_TYPE) | tr '[:upper:]' '[:lower:]') -#BIN_ALL = $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON) -# Temporarily remove the RASDUMP and RASDUMP tools, since they're not needed -# for my specific use case. If you need them - add them back in! -BIN_ALL = $(BINDIR)/$(RASCSI) $(BINDIR)/$(RASCTL) $(BINDIR)/$(SCSIMON) +BIN_ALL = \ + $(BINDIR)/$(RASCSI) \ + $(BINDIR)/$(RASCTL) \ + $(BINDIR)/$(SCSIMON) \ + $(BINDIR)/$(RASDUMP) \ + $(BINDIR)/$(SASIDUMP) + +SRC_PROTOC = \ + rascsi_interface.proto + +SRC_PROTOBUF = \ + rascsi_interface.pb.cpp SRC_RASCSI = \ rascsi.cpp \ @@ -75,29 +83,31 @@ SRC_RASCSI = \ gpiobus.cpp \ filepath.cpp \ fileio.cpp\ - rascsi_version.cpp -# os.cpp -# rasctl_command.cpp -# rascsi_mgr.cpp -# command_thread.cpp + rascsi_version.cpp \ + rascsi_image.cpp \ + rascsi_response.cpp \ + rasutil.cpp \ + protobuf_util.cpp SRC_RASCSI += $(shell find ./controllers -name '*.cpp') SRC_RASCSI += $(shell find ./devices -name '*.cpp') +SRC_RASCSI += $(SRC_PROTOBUF) SRC_SCSIMON = \ scsimon.cpp \ scsi.cpp \ gpiobus.cpp \ filepath.cpp \ - fileio.cpp\ + fileio.cpp \ rascsi_version.cpp -SRC_SCSIMON += $(shell find ./controllers -name '*.cpp') -SRC_SCSIMON += $(shell find ./devices -name '*.cpp') - SRC_RASCTL = \ rasctl.cpp\ - rascsi_version.cpp -# rasctl_command.cpp + rasctl_commands.cpp \ + rasctl_display.cpp \ + rascsi_version.cpp \ + rasutil.cpp \ + protobuf_util.cpp +SRC_RASCTL += $(SRC_PROTOBUF) SRC_RASDUMP = \ rasdump.cpp \ @@ -125,9 +135,9 @@ OBJ_RASCSI := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI:%.cpp=%.o))) OBJ_RASCTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL:%.cpp=%.o))) OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o))) OBJ_SASIDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SASIDUMP:%.cpp=%.o))) -OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o))) -#OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON) -OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON) +OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o))) + +GEN_PROTOBUF := $(SRC_PROTOBUF) rascsi_interface.pb.h # The following will include all of the auto-generated dependency files (*.d) @@ -142,6 +152,11 @@ $(OBJDIR) $(BINDIR): $(OBJDIR)/%.o: %.cpp | $(OBJDIR) $(CXX) $(CXXFLAGS) -c $< -o $@ +$(SRC_PROTOBUF): $(SRC_PROTOC) + echo "-- Generating protobuf-based source files" + protoc --cpp_out=. $(SRC_PROTOC) + mv rascsi_interface.pb.cc $@ + ## Build Targets: ## all : Rebuild all of the executable files and re-generate ## the text versions of the manpages @@ -153,11 +168,11 @@ ALL: all docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt -$(BINDIR)/$(RASCSI): $(OBJ_RASCSI) | $(BINDIR) - $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI) -lpthread +$(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI) | $(BINDIR) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI) -lpthread -lz -lpcap -lprotobuf -lstdc++fs -$(BINDIR)/$(RASCTL): $(OBJ_RASCTL) | $(BINDIR) - $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) +$(BINDIR)/$(RASCTL): $(SRC_PROTOBUF) $(OBJ_RASCTL) | $(BINDIR) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) -lprotobuf -lstdc++fs $(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) | $(BINDIR) $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP) @@ -172,7 +187,7 @@ $(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR) ## compiler files and executable files .PHONY: clean clean: - rm -rf $(OBJDIR) $(BINDIR) + rm -rf $(OBJDIR) $(BINDIR) $(GEN_PROTOBUF) ## run : Launches RaSCSI using some pre-defined drive ## images. Useful for debugging when you're building @@ -194,7 +209,20 @@ run: ## * sudo systemctl enable rascsi ## * sudo systemctl start rascsi .PHONY: install -install: $(MAN_PAGE_DIR)/rascsi.1 $(MAN_PAGE_DIR)/rasctl.1 $(MAN_PAGE_DIR)/scsimon.1 $(USR_LOCAL_BIN)/$(RASCTL) $(USR_LOCAL_BIN)/$(RASCSI) $(USR_LOCAL_BIN)/$(SCSIMON) $(SYSTEMD_CONF) $(RSYSLOG_CONF) $(RSYSLOG_LOG) +install: \ + $(MAN_PAGE_DIR)/rascsi.1 \ + $(MAN_PAGE_DIR)/rasctl.1 \ + $(MAN_PAGE_DIR)/scsimon.1 \ + $(MAN_PAGE_DIR)/rasdump.1 \ + $(MAN_PAGE_DIR)/sasidump.1 \ + $(USR_LOCAL_BIN)/$(RASCTL) \ + $(USR_LOCAL_BIN)/$(RASCSI) \ + $(USR_LOCAL_BIN)/$(SCSIMON) \ + $(USR_LOCAL_BIN)/$(RASDUMP) \ + $(USR_LOCAL_BIN)/$(SASIDUMP) \ + $(SYSTEMD_CONF) \ + $(RSYSLOG_CONF) \ + $(RSYSLOG_LOG) @echo "-- Done installing!" $(USR_LOCAL_BIN)% : $(BINDIR)/% diff --git a/src/raspberrypi/controllers/sasidev_ctrl.cpp b/src/raspberrypi/controllers/sasidev_ctrl.cpp index 5e50e67a..89276f0d 100644 --- a/src/raspberrypi/controllers/sasidev_ctrl.cpp +++ b/src/raspberrypi/controllers/sasidev_ctrl.cpp @@ -17,6 +17,8 @@ #include "filepath.h" #include "gpiobus.h" #include "devices/scsi_host_bridge.h" +#include "devices/scsi_daynaport.h" +#include //=========================================================================== // @@ -29,39 +31,29 @@ // Constructor // //--------------------------------------------------------------------------- -#ifdef RASCSI SASIDEV::SASIDEV() -#else -SASIDEV::SASIDEV(Device *dev) -#endif // RASCSI { - int i; - - #ifndef RASCSI - // Remember host device - host = dev; - #endif // RASCSI - // Work initialization ctrl.phase = BUS::busfree; - ctrl.id = -1; + ctrl.m_scsi_id = UNKNOWN_SCSI_ID; ctrl.bus = NULL; memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd)); ctrl.status = 0x00; ctrl.message = 0x00; - #ifdef RASCSI ctrl.execstart = 0; - #endif // RASCSI - ctrl.bufsize = 0x800; + // The initial buffer size will default to either the default buffer size OR + // the size of an Ethernet message, whichever is larger. + ctrl.bufsize = std::max(DEFAULT_BUFFER_SIZE, ETH_FRAME_LEN + 16 + ETH_FCS_LEN); ctrl.buffer = (BYTE *)malloc(ctrl.bufsize); memset(ctrl.buffer, 0x00, ctrl.bufsize); ctrl.blocks = 0; ctrl.next = 0; ctrl.offset = 0; ctrl.length = 0; + ctrl.lun = -1; // Logical unit initialization - for (i = 0; i < UnitMax; i++) { + for (int i = 0; i < UnitMax; i++) { ctrl.unit[i] = NULL; } } @@ -85,20 +77,14 @@ SASIDEV::~SASIDEV() // Device reset // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Reset() +void SASIDEV::Reset() { - int i; - - ASSERT(this); - // Work initialization memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd)); ctrl.phase = BUS::busfree; ctrl.status = 0x00; ctrl.message = 0x00; - #ifdef RASCSI ctrl.execstart = 0; - #endif // RASCSI memset(ctrl.buffer, 0x00, ctrl.bufsize); ctrl.blocks = 0; ctrl.next = 0; @@ -106,126 +92,31 @@ void FASTCALL SASIDEV::Reset() ctrl.length = 0; // Unit initialization - for (i = 0; i < UnitMax; i++) { + for (int i = 0; i < UnitMax; i++) { if (ctrl.unit[i]) { ctrl.unit[i]->Reset(); } } } -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Save -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SASIDEV::Save(Fileio *fio, int /*ver*/) -{ - DWORD sz; - - ASSERT(this); - ASSERT(fio); - - // Save size - sz = 2120; - if (!fio->Write(&sz, sizeof(sz))) { - return FALSE; - } - - // Save entity - PROP_EXPORT(fio, ctrl.phase); - PROP_EXPORT(fio, ctrl.id); - PROP_EXPORT(fio, ctrl.cmd); - PROP_EXPORT(fio, ctrl.status); - PROP_EXPORT(fio, ctrl.message); - if (!fio->Write(ctrl.buffer, 0x800)) { - return FALSE; - } - PROP_EXPORT(fio, ctrl.blocks); - PROP_EXPORT(fio, ctrl.next); - PROP_EXPORT(fio, ctrl.offset); - PROP_EXPORT(fio, ctrl.length); - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Load -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SASIDEV::Load(Fileio *fio, int ver) -{ - DWORD sz; - - ASSERT(this); - ASSERT(fio); - - // Not saved before version 3.11 - if (ver <= 0x0311) { - return TRUE; - } - - // Load size and check if the size matches - if (!fio->Read(&sz, sizeof(sz))) { - return FALSE; - } - if (sz != 2120) { - return FALSE; - } - - // Load the entity - PROP_IMPORT(fio, ctrl.phase); - PROP_IMPORT(fio, ctrl.id); - PROP_IMPORT(fio, ctrl.cmd); - PROP_IMPORT(fio, ctrl.status); - PROP_IMPORT(fio, ctrl.message); - if (!fio->Read(ctrl.buffer, 0x800)) { - return FALSE; - } - PROP_IMPORT(fio, ctrl.blocks); - PROP_IMPORT(fio, ctrl.next); - PROP_IMPORT(fio, ctrl.offset); - PROP_IMPORT(fio, ctrl.length); - - return TRUE; -} -#endif // RASCSI - //--------------------------------------------------------------------------- // // Connect the controller // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Connect(int id, BUS *bus) +void SASIDEV::Connect(int id, BUS *bus) { - ASSERT(this); - - ctrl.id = id; + ctrl.m_scsi_id = id; ctrl.bus = bus; } -//--------------------------------------------------------------------------- -// -// Get the logical unit -// -//--------------------------------------------------------------------------- -Disk* FASTCALL SASIDEV::GetUnit(int no) -{ - ASSERT(this); - ASSERT(no < UnitMax); - - return ctrl.unit[no]; -} - //--------------------------------------------------------------------------- // // Set the logical unit // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::SetUnit(int no, Disk *dev) +void SASIDEV::SetUnit(int no, Disk *dev) { - ASSERT(this); ASSERT(no < UnitMax); ctrl.unit[no] = dev; @@ -233,52 +124,18 @@ void FASTCALL SASIDEV::SetUnit(int no, Disk *dev) //--------------------------------------------------------------------------- // -// Check to see if this has a valid logical unit +// Check to see if this has a valid LUN // //--------------------------------------------------------------------------- -BOOL FASTCALL SASIDEV::HasUnit() +bool SASIDEV::HasUnit() { - int i; - - ASSERT(this); - - for (i = 0; i < UnitMax; i++) { + for (int i = 0; i < UnitMax; i++) { if (ctrl.unit[i]) { - return TRUE; + return true; } } - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// Get internal data -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::GetCTRL(ctrl_t *buffer) -{ - ASSERT(this); - ASSERT(buffer); - - // reference the internal structure - *buffer = ctrl; -} - -//--------------------------------------------------------------------------- -// -// Get a busy unit -// -//--------------------------------------------------------------------------- -Disk* FASTCALL SASIDEV::GetBusyUnit() -{ - DWORD lun; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - return ctrl.unit[lun]; + return false; } //--------------------------------------------------------------------------- @@ -286,12 +143,10 @@ Disk* FASTCALL SASIDEV::GetBusyUnit() // Run // //--------------------------------------------------------------------------- -BUS::phase_t FASTCALL SASIDEV::Process() +BUS::phase_t SASIDEV::Process() { - ASSERT(this); - // Do nothing if not connected - if (ctrl.id < 0 || ctrl.bus == NULL) { + if (ctrl.m_scsi_id < 0 || ctrl.bus == NULL) { return ctrl.phase; } @@ -301,9 +156,7 @@ BUS::phase_t FASTCALL SASIDEV::Process() // For the monitor tool, we shouldn't need to reset. We're just logging information // Reset if (ctrl.bus->GetRST()) { - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - RESET signal received"); - #endif // DISK_LOG + LOGINFO("RESET signal received"); // Reset the controller Reset(); @@ -364,16 +217,11 @@ BUS::phase_t FASTCALL SASIDEV::Process() // Bus free phase // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::BusFree() +void SASIDEV::BusFree() { - ASSERT(this); - // Phase change if (ctrl.phase != BUS::busfree) { - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Bus free phase"); - #endif // DISK_LOG + LOGTRACE("%s Bus free phase", __PRETTY_FUNCTION__); // Phase Setting ctrl.phase = BUS::busfree; @@ -383,11 +231,14 @@ void FASTCALL SASIDEV::BusFree() ctrl.bus->SetMSG(FALSE); ctrl.bus->SetCD(FALSE); ctrl.bus->SetIO(FALSE); - ctrl.bus->SetBSY(FALSE); + ctrl.bus->SetBSY(false); // Initialize status and message ctrl.status = 0x00; ctrl.message = 0x00; + + ctrl.lun = -1; + return; } @@ -402,34 +253,28 @@ void FASTCALL SASIDEV::BusFree() // Selection phase // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Selection() +void SASIDEV::Selection() { - DWORD id; - - ASSERT(this); - // Phase change if (ctrl.phase != BUS::selection) { // Invalid if IDs do not match - id = 1 << ctrl.id; + DWORD id = 1 << ctrl.m_scsi_id; if ((ctrl.bus->GetDAT() & id) == 0) { return; } - // Return if there is no unit + // Return if there is no valid LUN if (!HasUnit()) { return; } - #if defined(DISK_LOG) - Log(Log::Normal,"SASI - Selection Phase ID=%d (with device)", ctrl.id); - #endif // DISK_LOG + LOGTRACE("%s Selection Phase ID=%d (with device)", __PRETTY_FUNCTION__, (int)ctrl.m_scsi_id); // Phase change ctrl.phase = BUS::selection; // Raiase BSY and respond - ctrl.bus->SetBSY(TRUE); + ctrl.bus->SetBSY(true); return; } @@ -441,24 +286,14 @@ void FASTCALL SASIDEV::Selection() //--------------------------------------------------------------------------- // -// Command phase +// Command phase (used by SASI and SCSI) // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Command() +void SASIDEV::Command() { - #ifdef RASCSI - int count; - int i; - #endif // RASCSI - - ASSERT(this); - // Phase change if (ctrl.phase != BUS::command) { - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Command Phase"); - #endif // DISK_LOG + LOGTRACE("%s Command Phase", __PRETTY_FUNCTION__); // Phase Setting ctrl.phase = BUS::command; @@ -473,20 +308,14 @@ void FASTCALL SASIDEV::Command() ctrl.length = 6; ctrl.blocks = 1; - #ifdef RASCSI - // Command reception handshake (10 bytes are automatically received at the first command) - count = ctrl.bus->CommandHandShake(ctrl.buffer); - // If no byte can be received move to the status phase - if (count == 0) { + int count = ctrl.bus->CommandHandShake(ctrl.buffer); + if (!count) { Error(); return; } - // Check 10-byte CDB - if (ctrl.buffer[0] >= 0x20 && ctrl.buffer[0] <= 0x7D) { - ctrl.length = 10; - } + ctrl.length = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]); // If not able to receive all, move to the status phase if (count != (int)ctrl.length) { @@ -495,8 +324,9 @@ void FASTCALL SASIDEV::Command() } // Command data transfer - for (i = 0; i < (int)ctrl.length; i++) { + for (int i = 0; i < (int)ctrl.length; i++) { ctrl.cmd[i] = (DWORD)ctrl.buffer[i]; + LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i]); } // Clear length and block @@ -505,26 +335,7 @@ void FASTCALL SASIDEV::Command() // Execution Phase Execute(); - #else - // Request the command - ctrl.bus->SetREQ(TRUE); - return; - #endif // RASCSI } - #ifndef RASCSI - // Requesting - if (ctrl.bus->GetREQ()) { - // Sent by the initiator - if (ctrl.bus->GetACK()) { - Receive(); - } - } else { - // Request the initator to - if (!ctrl.bus->GetACK()) { - ReceiveNext(); - } - } - #endif // RASCSI } //--------------------------------------------------------------------------- @@ -532,13 +343,9 @@ void FASTCALL SASIDEV::Command() // Execution Phase // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Execute() +void SASIDEV::Execute() { - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Execution Phase Command %02X", ctrl.cmd[0]); - #endif // DISK_LOG + LOGTRACE("%s Execution Phase Command %02X", __PRETTY_FUNCTION__, (WORD)ctrl.cmd[0]); // Phase Setting ctrl.phase = BUS::execute; @@ -546,82 +353,97 @@ void FASTCALL SASIDEV::Execute() // Initialization for data transfer ctrl.offset = 0; ctrl.blocks = 1; - #ifdef RASCSI ctrl.execstart = SysTimer::GetTimerLow(); - #endif // RASCSI + + // Discard pending sense data from the previous command if the current command is not REQUEST SENSE + if ((SASIDEV::sasi_command)ctrl.cmd[0] != SASIDEV::eCmdRequestSense) { + ctrl.status = 0; + ctrl.device->SetStatusCode(0); + } // Process by command - switch (ctrl.cmd[0]) { + // TODO This code does not belong here. Each device type needs such a dispatcher, which the controller has to call. + switch ((SASIDEV::sasi_command)ctrl.cmd[0]) { // TEST UNIT READY - case 0x00: + case SASIDEV::eCmdTestUnitReady: CmdTestUnitReady(); return; // REZERO UNIT - case 0x01: + case SASIDEV::eCmdRezero: CmdRezero(); return; // REQUEST SENSE - case 0x03: + case SASIDEV::eCmdRequestSense: CmdRequestSense(); return; - // FORMAT UNIT - case 0x04: - CmdFormat(); - return; - - // FORMAT UNIT - case 0x06: + // FORMAT + case SASIDEV::eCmdFormat: CmdFormat(); return; // REASSIGN BLOCKS - case 0x07: - CmdReassign(); + case SASIDEV::eCmdReassign: + CmdReassignBlocks(); return; // READ(6) - case 0x08: + case SASIDEV::eCmdRead6: CmdRead6(); return; // WRITE(6) - case 0x0a: + case SASIDEV::eCmdWrite6: CmdWrite6(); return; // SEEK(6) - case 0x0b: + case SASIDEV::eCmdSeek6: CmdSeek6(); return; - // ASSIGN(SASIのみ) - case 0x0e: + // ASSIGN (SASI only) + // This doesn't exist in the SCSI Spec, but was in the original RaSCSI code. + // leaving it here for now.... + case SASIDEV::eCmdSasiCmdAssign: CmdAssign(); return; // RESERVE UNIT(16) - case 0x16: + case SASIDEV::eCmdReserve6: CmdReserveUnit(); return; // RELEASE UNIT(17) - case 0x17: + case eCmdRelease6: CmdReleaseUnit(); return; - // SPECIFY(SASIのみ) - case 0xc2: + // SPECIFY (SASI only) + // This doesn't exist in the SCSI Spec, but was in the original RaSCSI code. + // leaving it here for now.... + case SASIDEV::eCmdInvalid: CmdSpecify(); return; + default: + break; } // Unsupported command - Log(Log::Warning, "SASI - Unsupported command $%02X", ctrl.cmd[0]); - CmdInvalid(); + LOGTRACE("%s ID %d received unsupported command: $%02X", __PRETTY_FUNCTION__, GetSCSIID(), (BYTE)ctrl.cmd[0]); + + // Logical Unit + DWORD lun = GetEffectiveLun(); + if (ctrl.unit[lun]) { + // Command processing on drive + ctrl.unit[lun]->SetStatusCode(STATUS_INVALIDCMD); + } + + // Failure (Error) + Error(); } //--------------------------------------------------------------------------- @@ -629,23 +451,14 @@ void FASTCALL SASIDEV::Execute() // Status phase // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Status() +void SASIDEV::Status() { - #ifdef RASCSI - DWORD min_exec_time; - DWORD time; - #endif // RASCSI - - ASSERT(this); - // Phase change if (ctrl.phase != BUS::status) { - - #ifdef RASCSI // Minimum execution time if (ctrl.execstart > 0) { - min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; - time = SysTimer::GetTimerLow() - ctrl.execstart; + DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; + DWORD time = SysTimer::GetTimerLow() - ctrl.execstart; if (time < min_exec_time) { SysTimer::SleepUsec(min_exec_time - time); } @@ -653,11 +466,8 @@ void FASTCALL SASIDEV::Status() } else { SysTimer::SleepUsec(5); } - #endif // RASCSI - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Status phase"); - #endif // DISK_LOG + LOGTRACE("%s Status phase", __PRETTY_FUNCTION__); // Phase Setting ctrl.phase = BUS::status; @@ -673,52 +483,25 @@ void FASTCALL SASIDEV::Status() ctrl.blocks = 1; ctrl.buffer[0] = (BYTE)ctrl.status; - #ifndef RASCSI - // Request status - ctrl.bus->SetDAT(ctrl.buffer[0]); - ctrl.bus->SetREQ(TRUE); + LOGTRACE( "%s Status Phase $%02X",__PRETTY_FUNCTION__, (unsigned int)ctrl.status); - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Status Phase $%02X", ctrl.status); - #endif // DISK_LOG - #endif // RASCSI return; } - #ifdef RASCSI // Send Send(); - #else - // Requesting - if (ctrl.bus->GetREQ()) { - // Initiator received - if (ctrl.bus->GetACK()) { - SendNext(); - } - } else { - // Initiator requests next - if (!ctrl.bus->GetACK()) { - Send(); - } - } - #endif // RASCSI } //--------------------------------------------------------------------------- // -// Message in phase +// Message in phase (used by SASI and SCSI) // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::MsgIn() +void SASIDEV::MsgIn() { - ASSERT(this); - // Phase change if (ctrl.phase != BUS::msgin) { - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Message in phase"); - #endif // DISK_LOG + LOGTRACE("%s Starting Message in phase", __PRETTY_FUNCTION__); // Phase Setting ctrl.phase = BUS::msgin; @@ -732,67 +515,34 @@ void FASTCALL SASIDEV::MsgIn() ASSERT(ctrl.length > 0); ASSERT(ctrl.blocks > 0); ctrl.offset = 0; - - #ifndef RASCSI - // Request message - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); - ctrl.bus->SetREQ(TRUE); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Message in phase $%02X", ctrl.buffer[ctrl.offset]); - #endif // DISK_LOG - #endif // RASCSI return; } - #ifdef RASCSI //Send + LOGTRACE("%s Transitioning to Send()", __PRETTY_FUNCTION__); Send(); - #else - // Requesting - if (ctrl.bus->GetREQ()) { - // Initator received - if (ctrl.bus->GetACK()) { - SendNext(); - } - } else { - // Initiator requests next - if (!ctrl.bus->GetACK()) { - Send(); - } - } - #endif // RASCSI } //--------------------------------------------------------------------------- // -// Data-in Phase +// Data-in Phase (used by SASI and SCSI) // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::DataIn() +void SASIDEV::DataIn() { - #ifdef RASCSI - DWORD min_exec_time; - DWORD time; - #endif // RASCSI - - ASSERT(this); ASSERT(ctrl.length >= 0); // Phase change if (ctrl.phase != BUS::datain) { - - #ifdef RASCSI // Minimum execution time if (ctrl.execstart > 0) { - min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; - time = SysTimer::GetTimerLow() - ctrl.execstart; + DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; + DWORD time = SysTimer::GetTimerLow() - ctrl.execstart; if (time < min_exec_time) { SysTimer::SleepUsec(min_exec_time - time); } ctrl.execstart = 0; } - #endif // RASCSI // If the length is 0, go to the status phase if (ctrl.length == 0) { @@ -800,10 +550,7 @@ void FASTCALL SASIDEV::DataIn() return; } - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Data-in Phase"); - #endif // DISK_LOG - + LOGTRACE("%s Going into Data-in Phase", __PRETTY_FUNCTION__); // Phase Setting ctrl.phase = BUS::datain; @@ -817,64 +564,33 @@ void FASTCALL SASIDEV::DataIn() ASSERT(ctrl.blocks > 0); ctrl.offset = 0; - #ifndef RASCSI - // Assert the DAT signal - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); - - // Request data - ctrl.bus->SetREQ(TRUE); - #endif // RASCSI return; } - #ifdef RASCSI // Send Send(); - #else - // Requesting - if (ctrl.bus->GetREQ()) { - // Initator received - if (ctrl.bus->GetACK()) { - SendNext(); - } - } else { - // Initiator requests next - if (!ctrl.bus->GetACK()) { - Send(); - } - } - #endif // RASCSI } //--------------------------------------------------------------------------- // -// Data out phase +// Data out phase (used by SASI and SCSI) // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::DataOut() +void SASIDEV::DataOut() { - #ifdef RASCSI - DWORD min_exec_time; - DWORD time; - #endif // RASCSI - - ASSERT(this); ASSERT(ctrl.length >= 0); // Phase change if (ctrl.phase != BUS::dataout) { - - #ifdef RASCSI // Minimum execution time if (ctrl.execstart > 0) { - min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; - time = SysTimer::GetTimerLow() - ctrl.execstart; + DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; + DWORD time = SysTimer::GetTimerLow() - ctrl.execstart; if (time < min_exec_time) { SysTimer::SleepUsec(min_exec_time - time); } ctrl.execstart = 0; } - #endif // RASCSI // If the length is 0, go to the status phase if (ctrl.length == 0) { @@ -882,9 +598,7 @@ void FASTCALL SASIDEV::DataOut() return; } - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Data out phase"); - #endif // DISK_LOG + LOGTRACE("%s Data out phase", __PRETTY_FUNCTION__); // Phase Setting ctrl.phase = BUS::dataout; @@ -898,31 +612,11 @@ void FASTCALL SASIDEV::DataOut() ASSERT(ctrl.length > 0); ASSERT(ctrl.blocks > 0); ctrl.offset = 0; - - #ifndef RASCSI - // Request data - ctrl.bus->SetREQ(TRUE); - #endif // RASCSI return; } - #ifdef RASCSI // Receive Receive(); - #else - // Requesting - if (ctrl.bus->GetREQ()) { - // Sent by the initiator - if (ctrl.bus->GetACK()) { - Receive(); - } - } else { - // Request the initator to - if (!ctrl.bus->GetACK()) { - ReceiveNext(); - } - } - #endif // RASCSI } //--------------------------------------------------------------------------- @@ -930,12 +624,8 @@ void FASTCALL SASIDEV::DataOut() // Error // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Error() +void SASIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc) { - DWORD lun; - - ASSERT(this); - // Get bus information ((GPIOBUS*)ctrl.bus)->Aquire(); @@ -955,12 +645,8 @@ void FASTCALL SASIDEV::Error() return; } - #if defined(DISK_LOG) - Log(Log::Warning, "SASI - Error occured (going to status phase)"); - #endif // DISK_LOG - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; + DWORD lun = GetEffectiveLun(); // Set status and message(CHECK CONDITION) ctrl.status = (lun << 5) | 0x02; @@ -974,34 +660,12 @@ void FASTCALL SASIDEV::Error() // TEST UNIT READY // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdTestUnitReady() +void SASIDEV::CmdTestUnitReady() { - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - TEST UNIT READY Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } + LOGTRACE("%s TEST UNIT READY Command ", __PRETTY_FUNCTION__); // Command processing on drive - status = ctrl.unit[lun]->TestUnitReady(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); + ctrl.device->TestUnitReady(this); } //--------------------------------------------------------------------------- @@ -1009,34 +673,12 @@ void FASTCALL SASIDEV::CmdTestUnitReady() // REZERO UNIT // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdRezero() +void SASIDEV::CmdRezero() { - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - REZERO UNIT Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } + LOGTRACE( "%s REZERO UNIT Command ", __PRETTY_FUNCTION__); // Command processing on drive - status = ctrl.unit[lun]->Rezero(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); + ctrl.device->Rezero(this); } //--------------------------------------------------------------------------- @@ -1044,33 +686,12 @@ void FASTCALL SASIDEV::CmdRezero() // REQUEST SENSE // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdRequestSense() +void SASIDEV::CmdRequestSense() { - DWORD lun; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - REQUEST SENSE Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } + LOGTRACE( "%s REQUEST SENSE Command ", __PRETTY_FUNCTION__); // Command processing on drive - ctrl.length = ctrl.unit[lun]->RequestSense(ctrl.cmd, ctrl.buffer); - ASSERT(ctrl.length > 0); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Sense key $%02X", ctrl.buffer[2]); - #endif // DISK_LOG - - // Read phase - DataIn(); + ctrl.device->RequestSense(this); } //--------------------------------------------------------------------------- @@ -1078,34 +699,12 @@ void FASTCALL SASIDEV::CmdRequestSense() // FORMAT UNIT // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdFormat() +void SASIDEV::CmdFormat() { - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - FORMAT UNIT Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } + LOGTRACE( "%s FORMAT UNIT Command ", __PRETTY_FUNCTION__); // Command processing on drive - status = ctrl.unit[lun]->Format(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); + ctrl.device->FormatUnit(this); } //--------------------------------------------------------------------------- @@ -1113,70 +712,48 @@ void FASTCALL SASIDEV::CmdFormat() // REASSIGN BLOCKS // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdReassign() +void SASIDEV::CmdReassignBlocks() { - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - REASSIGN BLOCKS Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } + LOGTRACE("%s REASSIGN BLOCKS Command ", __PRETTY_FUNCTION__); // Command processing on drive - status = ctrl.unit[lun]->Reassign(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); + ctrl.device->ReassignBlocks(this); } -// The following commands RESERVE UNIT and RELEASE UNIT are not properly implemented. -// These commands are used in multi-initator environments which this project is not targeted at. -// For now, we simply reply with an OK. -phrax 2021-03-06 - //--------------------------------------------------------------------------- // // RESERVE UNIT(16) // +// The reserve/release commands are only used in multi-initiator +// environments. RaSCSI doesn't support this use case. However, some old +// versions of Solaris will issue the reserve/release commands. We will +// just respond with an OK status. +// //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdReserveUnit() +void SASIDEV::CmdReserveUnit() { - ASSERT(this); - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - RESERVE UNIT Command"); - #endif // DISK_LOG + LOGTRACE( "%s Reserve(6) Command", __PRETTY_FUNCTION__); // status phase - Status(); + Status(); } //--------------------------------------------------------------------------- // // RELEASE UNIT(17) // +// The reserve/release commands are only used in multi-initiator +// environments. RaSCSI doesn't support this use case. However, some old +// versions of Solaris will issue the reserve/release commands. We will +// just respond with an OK status. +// //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdReleaseUnit() +void SASIDEV::CmdReleaseUnit() { - ASSERT(this); - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - RELEASE UNIT Command"); - #endif // DISK_LOG + LOGTRACE( "%s Release(6) Command", __PRETTY_FUNCTION__); // status phase - Status(); + Status(); } //--------------------------------------------------------------------------- @@ -1184,22 +761,10 @@ void FASTCALL SASIDEV::CmdReleaseUnit() // READ(6) // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdRead6() +void SASIDEV::CmdRead6() { - DWORD lun; - DWORD record; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - // Get record number and block number - record = ctrl.cmd[1] & 0x1f; + DWORD record = ctrl.cmd[1] & 0x1f; record <<= 8; record |= ctrl.cmd[2]; record <<= 8; @@ -1209,18 +774,16 @@ void FASTCALL SASIDEV::CmdRead6() ctrl.blocks = 0x100; } - #if defined(DISK_LOG) - Log(Log::Normal,"SASI - READ(6) command record=%06X blocks=%d", record, ctrl.blocks); - #endif // DISK_LOG + LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl.blocks); // Command processing on drive - ctrl.length = ctrl.unit[lun]->Read(ctrl.buffer, record); + ctrl.length = ctrl.device->Read(ctrl.cmd, ctrl.buffer, record); if (ctrl.length <= 0) { // Failure (Error) Error(); return; } - + // Set next block ctrl.next = record + 1; @@ -1233,22 +796,10 @@ void FASTCALL SASIDEV::CmdRead6() // WRITE(6) // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdWrite6() +void SASIDEV::CmdWrite6() { - DWORD lun; - DWORD record; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - // Get record number and block number - record = ctrl.cmd[1] & 0x1f; + DWORD record = ctrl.cmd[1] & 0x1f; record <<= 8; record |= ctrl.cmd[2]; record <<= 8; @@ -1258,12 +809,10 @@ void FASTCALL SASIDEV::CmdWrite6() ctrl.blocks = 0x100; } - #if defined(DISK_LOG) - Log(Log::Normal,"SASI - WRITE(6) command record=%06X blocks=%d", record, ctrl.blocks); - #endif // DISK_LOG + LOGTRACE("%s WRITE(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (WORD)record, (WORD)ctrl.blocks); // Command processing on drive - ctrl.length = ctrl.unit[lun]->WriteCheck(record); + ctrl.length = ctrl.device->WriteCheck(record); if (ctrl.length <= 0) { // Failure (Error) Error(); @@ -1282,34 +831,12 @@ void FASTCALL SASIDEV::CmdWrite6() // SEEK(6) // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdSeek6() +void SASIDEV::CmdSeek6() { - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - SEEK(6) Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } + LOGTRACE("%s SEEK(6) Command ", __PRETTY_FUNCTION__); // Command processing on drive - status = ctrl.unit[lun]->Seek(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); + ctrl.device->Seek6(this); } //--------------------------------------------------------------------------- @@ -1317,26 +844,12 @@ void FASTCALL SASIDEV::CmdSeek6() // ASSIGN // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdAssign() +void SASIDEV::CmdAssign() { - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - ASSIGN Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } + LOGTRACE("%s ASSIGN Command ", __PRETTY_FUNCTION__); // Command processing on drive - status = ctrl.unit[lun]->Assign(ctrl.cmd); + bool status = ctrl.device->CheckReady(); if (!status) { // Failure (Error) Error(); @@ -1355,26 +868,12 @@ void FASTCALL SASIDEV::CmdAssign() // SPECIFY // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdSpecify() +void SASIDEV::CmdSpecify() { - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - SPECIFY Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } + LOGTRACE("%s SPECIFY Command ", __PRETTY_FUNCTION__); // Command processing on drive - status = ctrl.unit[lun]->Assign(ctrl.cmd); + bool status = ctrl.device->CheckReady(); if (!status) { // Failure (Error) Error(); @@ -1388,32 +887,6 @@ void FASTCALL SASIDEV::CmdSpecify() DataOut(); } -//--------------------------------------------------------------------------- -// -// Unsupported command -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdInvalid() -{ - DWORD lun; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Command not supported"); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (ctrl.unit[lun]) { - // Command processing on drive - ctrl.unit[lun]->InvalidCmd(); - } - - // Failure (Error) - Error(); -} - //=========================================================================== // // Data transfer @@ -1425,25 +898,19 @@ void FASTCALL SASIDEV::CmdInvalid() // Data transmission // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Send() +void SASIDEV::Send() { - #ifdef RASCSI - int len; - #endif // RASCSI - BOOL result; - - ASSERT(this); ASSERT(!ctrl.bus->GetREQ()); ASSERT(ctrl.bus->GetIO()); - #ifdef RASCSI // Check that the length isn't 0 if (ctrl.length != 0) { - len = ctrl.bus->SendHandShake( - &ctrl.buffer[ctrl.offset], ctrl.length); + int len = ctrl.bus->SendHandShake( + &ctrl.buffer[ctrl.offset], ctrl.length, BUS::SEND_NO_DELAY); // If you can not send it all, move on to the status phase if (len != (int)ctrl.length) { + LOGERROR("%s ctrl.length (%d) did not match the amount of data sent (%d)",__PRETTY_FUNCTION__, (int)ctrl.length, len); Error(); return; } @@ -1453,40 +920,27 @@ void FASTCALL SASIDEV::Send() ctrl.length = 0; return; } - #else - // Offset and Length - ASSERT(ctrl.length >= 1); - ctrl.offset++; - ctrl.length--; - - // Immediately after ACK is asserted, if the data - // has been set by SendNext, raise the request - if (ctrl.length != 0) { - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - return; + else{ + LOGINFO("%s ctrl.length was 0", __PRETTY_FUNCTION__); } - #endif // RASCSI // Remove block and initialize the result ctrl.blocks--; - result = TRUE; + bool result = true; // Process after data collection (read/data-in only) if (ctrl.phase == BUS::datain) { if (ctrl.blocks != 0) { // Set next buffer (set offset, length) result = XferIn(ctrl.buffer); - //** printf("xfer in: %d \n",result); - - #ifndef RASCSI - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); - #endif // RASCSI + LOGTRACE("%s xfer in: %d",__PRETTY_FUNCTION__, result); + LOGTRACE("%s processing after data collection", __PRETTY_FUNCTION__); } } // If result FALSE, move to the status phase if (!result) { + LOGERROR("%s Send result was false", __PRETTY_FUNCTION__); Error(); return; } @@ -1495,10 +949,6 @@ void FASTCALL SASIDEV::Send() if (ctrl.blocks != 0){ ASSERT(ctrl.length > 0); ASSERT(ctrl.offset == 0); - #ifndef RASCSI - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - #endif // RASCSI return; } @@ -1532,115 +982,23 @@ void FASTCALL SASIDEV::Send() } } -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Continue sending data -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::SendNext() -{ - ASSERT(this); - - // Req is up - ASSERT(ctrl.bus->GetREQ()); - ASSERT(ctrl.bus->GetIO()); - - // Signal line operated by the target - ctrl.bus->SetREQ(FALSE); - - // If there is data in the buffer, set it first. - if (ctrl.length > 1) { - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset + 1]); - } -} -#endif // RASCSI - -#ifndef RASCSI //--------------------------------------------------------------------------- // // Receive data // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Receive() +void SASIDEV::Receive() { - DWORD data; - - ASSERT(this); - - // Req is up - ASSERT(ctrl.bus->GetREQ()); - ASSERT(!ctrl.bus->GetIO()); - - // Get data - data = (DWORD)ctrl.bus->GetDAT(); - - // Signal line operated by the target - ctrl.bus->SetREQ(FALSE); - - switch (ctrl.phase) { - // Command phase - case BUS::command: - ctrl.cmd[ctrl.offset] = data; - #if defined(DISK_LOG) - Log(Log::Normal, "SASI - Command phase $%02X", data); - #endif // DISK_LOG - - // Set the length again with the first data (offset 0) - if (ctrl.offset == 0) { - if (ctrl.cmd[0] >= 0x20 && ctrl.cmd[0] <= 0x7D) { - // 10 byte CDB - ctrl.length = 10; - } - } - break; - - // Data out phase - case BUS::dataout: - ctrl.buffer[ctrl.offset] = (BYTE)data; - break; - - // Other (impossible) - default: - ASSERT(FALSE); - break; - } -} -#endif // RASCSI - -#ifdef RASCSI -//--------------------------------------------------------------------------- -// -// Receive data -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Receive() -#else -//--------------------------------------------------------------------------- -// -// Continue receiving data -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::ReceiveNext() -#endif // RASCSI -{ - #ifdef RASCSI - int len; - #endif // RASCSI - BOOL result; - - ASSERT(this); - // REQ is low ASSERT(!ctrl.bus->GetREQ()); ASSERT(!ctrl.bus->GetIO()); - #ifdef RASCSI // Length != 0 if received if (ctrl.length != 0) { // Receive - len = ctrl.bus->ReceiveHandShake( + int len = ctrl.bus->ReceiveHandShake( &ctrl.buffer[ctrl.offset], ctrl.length); + LOGDEBUG("%s Received %d bytes", __PRETTY_FUNCTION__, len); // If not able to receive all, move to status phase if (len != (int)ctrl.length) { @@ -1653,32 +1011,23 @@ void FASTCALL SASIDEV::ReceiveNext() ctrl.length = 0; return; } - #else - // Offset and Length - ASSERT(ctrl.length >= 1); - ctrl.offset++; - ctrl.length--; - - // If length != 0, set req again - if (ctrl.length != 0) { - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - return; + else + { + LOGDEBUG("%s ctrl.length was 0", __PRETTY_FUNCTION__); } - #endif // RASCSI // Remove the control block and initialize the result ctrl.blocks--; - result = TRUE; + bool result = true; // Process the data out phase if (ctrl.phase == BUS::dataout) { if (ctrl.blocks == 0) { // End with this buffer - result = XferOut(FALSE); + result = XferOut(false); } else { // Continue to next buffer (set offset, length) - result = XferOut(TRUE); + result = XferOut(true); } } @@ -1692,25 +1041,14 @@ void FASTCALL SASIDEV::ReceiveNext() if (ctrl.blocks != 0){ ASSERT(ctrl.length > 0); ASSERT(ctrl.offset == 0); - #ifndef RASCSI - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - #endif // RASCSI return; } // Move to the next phase switch (ctrl.phase) { - #ifndef RASCSI - // Command phase - case BUS::command: - // Execution Phase - Execute(); - break; - #endif // RASCSI - // Data out phase case BUS::dataout: + LOGTRACE("%s transitioning to FlushUnit()",__PRETTY_FUNCTION__); // Flush FlushUnit(); @@ -1731,33 +1069,30 @@ void FASTCALL SASIDEV::ReceiveNext() // *Reset offset and length // //--------------------------------------------------------------------------- -BOOL FASTCALL SASIDEV::XferIn(BYTE *buf) +bool SASIDEV::XferIn(BYTE *buf) { - DWORD lun; - - ASSERT(this); ASSERT(ctrl.phase == BUS::datain); + LOGTRACE("%s ctrl.cmd[0]=%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]); // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; + DWORD lun = GetEffectiveLun(); if (!ctrl.unit[lun]) { - return FALSE; + return false; } // Limited to read commands switch (ctrl.cmd[0]) { - // READ(6) - case 0x08: - // READ(10) - case 0x28: + case eCmdRead6: + case eCmdRead10: + case eCmdRead16: // Read from disk - ctrl.length = ctrl.unit[lun]->Read(buf, ctrl.next); + ctrl.length = ctrl.unit[lun]->Read(ctrl.cmd, buf, ctrl.next); ctrl.next++; // If there is an error, go to the status phase if (ctrl.length <= 0) { // Cancel data-in - return FALSE; + return false; } // If things are normal, work setting @@ -1767,11 +1102,11 @@ BOOL FASTCALL SASIDEV::XferIn(BYTE *buf) // Other (impossible) default: ASSERT(FALSE); - return FALSE; + return false; } // Succeeded in setting the buffer - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -1780,43 +1115,37 @@ BOOL FASTCALL SASIDEV::XferIn(BYTE *buf) // *If cont=true, reset the offset and length // //--------------------------------------------------------------------------- -BOOL FASTCALL SASIDEV::XferOut(BOOL cont) +bool SASIDEV::XferOut(bool cont) { - DWORD lun; - SCSIBR *bridge; - - ASSERT(this); ASSERT(ctrl.phase == BUS::dataout); // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; + DWORD lun = GetEffectiveLun(); if (!ctrl.unit[lun]) { - return FALSE; + return false; } + Disk *device = ctrl.unit[lun]; - // MODE SELECT or WRITE system switch (ctrl.cmd[0]) { - // MODE SELECT - case 0x15: - // MODE SELECT(10) - case 0x55: - if (!ctrl.unit[lun]->ModeSelect( - ctrl.cmd, ctrl.buffer, ctrl.offset)) { + case SASIDEV::eCmdModeSelect6: + case SASIDEV::eCmdModeSelect10: + if (!device->ModeSelect(ctrl.cmd, ctrl.buffer, ctrl.offset)) { // MODE SELECT failed - return FALSE; + return false; } break; - // WRITE(6) - case 0x0a: - // WRITE(10) - case 0x2a: - // Replace the host bridge with SEND MESSAGE 10 - if (ctrl.unit[lun]->GetID() == MAKEID('S', 'C', 'B', 'R')) { - bridge = (SCSIBR*)ctrl.unit[lun]; - if (!bridge->SendMessage10(ctrl.cmd, ctrl.buffer)) { + case SASIDEV::eCmdWrite6: + case SASIDEV::eCmdWrite10: + case SASIDEV::eCmdWrite16: + case SASIDEV::eCmdVerify10: + case SASIDEV::eCmdVerify16: + // If we're a host bridge, use the host bridge's SendMessage10 function + // TODO This class must not know about SCSIBR + if (device->IsBridge()) { + if (!((SCSIBR*)device)->SendMessage10(ctrl.cmd, ctrl.buffer)) { // write failed - return FALSE; + return false; } // If normal, work setting @@ -1824,12 +1153,23 @@ BOOL FASTCALL SASIDEV::XferOut(BOOL cont) break; } - // WRITE AND VERIFY - case 0x2e: - // Write - if (!ctrl.unit[lun]->Write(ctrl.buffer, ctrl.next - 1)) { + // Special case Write function for DaynaPort + // TODO This class must not know about DaynaPort + if (device->IsDaynaPort()) { + if (!device->Write(ctrl.cmd, ctrl.buffer, ctrl.length)) { + // write failed + return false; + } + + // If normal, work setting + ctrl.offset = 0; + ctrl.blocks = 0; + break; + } + + if (!device->Write(ctrl.cmd, ctrl.buffer, ctrl.next - 1)) { // Write failed - return FALSE; + return false; } // If you do not need the next block, end here @@ -1839,10 +1179,10 @@ BOOL FASTCALL SASIDEV::XferOut(BOOL cont) } // Check the next block - ctrl.length = ctrl.unit[lun]->WriteCheck(ctrl.next - 1); + ctrl.length = device->WriteCheck(ctrl.next - 1); if (ctrl.length <= 0) { // Cannot write - return FALSE; + return false; } // If normal, work setting @@ -1850,16 +1190,21 @@ BOOL FASTCALL SASIDEV::XferOut(BOOL cont) break; // SPECIFY(SASI only) - case 0xc2: + case SASIDEV::eCmdInvalid: + break; + + case SASIDEV::eCmdSetMcastAddr: + LOGTRACE("%s Done with DaynaPort Set Multicast Address", __PRETTY_FUNCTION__); break; default: + LOGWARN("Received an unexpected command (%02X) in %s", (WORD)ctrl.cmd[0] , __PRETTY_FUNCTION__) ASSERT(FALSE); break; } // Buffer saved successfully - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -1867,175 +1212,65 @@ BOOL FASTCALL SASIDEV::XferOut(BOOL cont) // Logical unit flush // //--------------------------------------------------------------------------- -void FASTCALL SASIDEV::FlushUnit() +void SASIDEV::FlushUnit() { - DWORD lun; - - ASSERT(this); ASSERT(ctrl.phase == BUS::dataout); // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; + DWORD lun = GetEffectiveLun(); if (!ctrl.unit[lun]) { return; } + Disk *device = ctrl.unit[lun]; // WRITE system only - switch (ctrl.cmd[0]) { - // WRITE(6) - case 0x0a: - // WRITE(10) - case 0x2a: - // WRITE AND VERIFY - case 0x2e: - // Flush - if (!ctrl.unit[lun]->IsCacheWB()) { - ctrl.unit[lun]->Flush(); - } + switch ((SASIDEV::sasi_command)ctrl.cmd[0]) { + case SASIDEV::eCmdWrite6: + case SASIDEV::eCmdWrite10: + case SASIDEV::eCmdWrite16: + case SASIDEV::eCmdVerify10: + case SASIDEV::eCmdVerify16: break; - // Mode Select (6) - case 0x15: - // MODE SELECT(10) - case 0x55: - // Debug code related to Issue #2 on github, where we get an unhandled Model select when + + case SASIDEV::eCmdModeSelect6: + case SASIDEV::eCmdModeSelect10: + // Debug code related to Issue #2 on github, where we get an unhandled Mode Select when // the mac is rebooted // https://github.com/akuker/RASCSI/issues/2 - Log(Log::Warning, "SASI - Received \'Mode Select\'\n"); - Log(Log::Warning, " Operation Code: [%02X]\n", ctrl.cmd[0]); - Log(Log::Warning, " Logical Unit %01X, PF %01X, SP %01X [%02X]\n", ctrl.cmd[1] >> 5, 1 & (ctrl.cmd[1] >> 4), ctrl.cmd[1] & 1, ctrl.cmd[1]); - Log(Log::Warning, " Reserved: %02X\n", ctrl.cmd[2]); - Log(Log::Warning, " Reserved: %02X\n", ctrl.cmd[3]); - Log(Log::Warning, " Parameter List Len %02X\n", ctrl.cmd[4]); - Log(Log::Warning, " Reserved: %02X\n", ctrl.cmd[5]); - Log(Log::Warning, " Ctrl Len: %08X\n",ctrl.length); + LOGWARN("Received \'Mode Select\'\n"); + LOGWARN(" Operation Code: [%02X]\n", (WORD)ctrl.cmd[0]); + LOGWARN(" Logical Unit %01X, PF %01X, SP %01X [%02X]\n",\ + (WORD)ctrl.cmd[1] >> 5, 1 & ((WORD)ctrl.cmd[1] >> 4), \ + (WORD)ctrl.cmd[1] & 1, (WORD)ctrl.cmd[1]); + LOGWARN(" Reserved: %02X\n", (WORD)ctrl.cmd[2]); + LOGWARN(" Reserved: %02X\n", (WORD)ctrl.cmd[3]); + LOGWARN(" Parameter List Len %02X\n", (WORD)ctrl.cmd[4]); + LOGWARN(" Reserved: %02X\n",(WORD)ctrl.cmd[5]); + LOGWARN(" Ctrl Len: %08X\n",(WORD)ctrl.length); - if (!ctrl.unit[lun]->ModeSelect( + if (!device->ModeSelect( ctrl.cmd, ctrl.buffer, ctrl.offset)) { // MODE SELECT failed - Log(Log::Warning, "SASI - Error occured while processing Mode Select command %02X\n", (unsigned char)ctrl.cmd[0]); + LOGWARN("Error occured while processing Mode Select command %02X\n", (unsigned char)ctrl.cmd[0]); return; } break; + case SASIDEV::eCmdSetMcastAddr: + // TODO: Eventually, we should store off the multicast address configuration data here... + break; + default: - Log(Log::Warning, "SASI - Received an invalid flush command %02X!!!!!\n",ctrl.cmd[0]); - ASSERT(FALSE); + LOGWARN("Received an unexpected flush command %02X!!!!!\n",(WORD)ctrl.cmd[0]); + // The following statement makes debugging a huge pain. You can un-comment it + // if you're not trying to add new devices.... + // ASSERT(FALSE); break; } } -#ifdef DISK_LOG -//--------------------------------------------------------------------------- -// -// Get the current phase as a string -// -//--------------------------------------------------------------------------- -void SASIDEV::GetPhaseStr(char *str) +int SASIDEV::GetEffectiveLun() const { - switch(this->GetPhase()) - { - case BUS::busfree: - strcpy(str,"busfree "); - break; - case BUS::arbitration: - strcpy(str,"arbitration"); - break; - case BUS::selection: - strcpy(str,"selection "); - break; - case BUS::reselection: - strcpy(str,"reselection"); - break; - case BUS::command: - strcpy(str,"command "); - break; - case BUS::execute: - strcpy(str,"execute "); - break; - case BUS::datain: - strcpy(str,"datain "); - break; - case BUS::dataout: - strcpy(str,"dataout "); - break; - case BUS::status: - strcpy(str,"status "); - break; - case BUS::msgin: - strcpy(str,"msgin "); - break; - case BUS::msgout: - strcpy(str,"msgout "); - break; - case BUS::reserved: - strcpy(str,"reserved "); - break; - } + return ctrl.lun != -1 ? ctrl.lun : (ctrl.cmd[1] >> 5) & 0x07; } -#endif -//--------------------------------------------------------------------------- -// -// Log output -// -// TODO: This function needs some cleanup. Its very kludgey -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Log(Log::loglevel level, const char *format, ...) -{ - #if !defined(BAREMETAL) - #ifdef DISK_LOG - char buffer[0x200]; - char buffer2[0x250]; - char buffer3[0x250]; - char phase_str[20]; - #endif - va_list args; - va_start(args, format); - - if(this->GetID() != 6) - { - return; - } - - #ifdef RASCSI - #ifndef DISK_LOG - if (level == Log::Warning) { - return; - } - #endif // DISK_LOG - #endif // RASCSI - - #ifdef DISK_LOG - // format - vsprintf(buffer, format, args); - - // end variable length argument - va_end(args); - - // Add the date/timestamp - // current date/time based on current system - time_t now = time(0); - // convert now to string form - char* dt = ctime(&now); - - strcpy(buffer2, "["); - strcat(buffer2, dt); - // Get rid of the carriage return - buffer2[strlen(buffer2)-1] = '\0'; - strcat(buffer2, "] "); - - // Get the phase - this->GetPhaseStr(phase_str); - sprintf(buffer3, "[%d][%s] ", this->GetID(), phase_str); - strcat(buffer2,buffer3); - strcat(buffer2, buffer); - - // Log output - #ifdef RASCSI - printf("%s\n", buffer2); - #else - host->GetVM()->GetLog()->Format(level, host, buffer); - #endif // RASCSI - #endif // BAREMETAL - #endif // DISK_LOG -} diff --git a/src/raspberrypi/controllers/sasidev_ctrl.h b/src/raspberrypi/controllers/sasidev_ctrl.h index 120f050f..db41ccd5 100644 --- a/src/raspberrypi/controllers/sasidev_ctrl.h +++ b/src/raspberrypi/controllers/sasidev_ctrl.h @@ -15,12 +15,11 @@ //--------------------------------------------------------------------------- #pragma once +#include "../rascsi.h" #include "os.h" #include "scsi.h" #include "fileio.h" -#include "devices/disk.h" #include "log.h" -#include "xm6.h" //=========================================================================== @@ -30,141 +29,171 @@ //=========================================================================== class SASIDEV { -public: - enum { - UnitMax = 8 // Maximum number of logical units +protected: + enum scsi_message_code : BYTE { + eMsgCodeAbort = 0x06, + eMsgCodeAbortTag = 0x0D, + eMsgCodeBusDeviceReset = 0x0C, + eMsgCodeClearQueue = 0x0E, + eMsgCodeCommandComplete = 0x00, + eMsgCodeDisconnect = 0x04, + eMsgCodeIdentify = 0x80, + eMsgCodeIgnoreWideResidue = 0x23, // (Two Bytes) + eMsgCodeInitiateRecovery = 0x0F, + eMsgCodeInitiatorDetectedError = 0x05, + eMsgCodeLinkedCommandComplete = 0x0A, + eMsgCodeLinkedCommandCompleteWithFlag = 0x0B, + eMsgCodeMessageParityError = 0x09, + eMsgCodeMessageReject = 0x07, + eMsgCodeNoOperation = 0x08, + eMsgCodeHeadOfQueueTag = 0x21, + eMsgCodeOrderedQueueTag = 0x22, + eMsgCodeSimpleQueueTag = 0x20, + eMsgCodeReleaseRecovery = 0x10, + eMsgCodeRestorePointers = 0x03, + eMsgCodeSaveDataPointer = 0x02, + eMsgCodeTerminateIOProcess = 0x11 }; - #ifdef RASCSI +private: + enum sasi_command : int { + eCmdTestUnitReady = 0x00, + eCmdRezero = 0x01, + eCmdRequestSense = 0x03, + eCmdFormat = 0x06, + eCmdReassign = 0x07, + eCmdRead6 = 0x08, + eCmdWrite6 = 0x0A, + eCmdSeek6 = 0x0B, + eCmdSetMcastAddr = 0x0D, // DaynaPort specific command + eCmdModeSelect6 = 0x15, + eCmdReserve6 = 0x16, + eCmdRelease6 = 0x17, + eCmdRead10 = 0x28, + eCmdWrite10 = 0x2A, + eCmdVerify10 = 0x2E, + eCmdVerify = 0x2F, + eCmdModeSelect10 = 0x55, + eCmdRead16 = 0x88, + eCmdWrite16 = 0x8A, + eCmdVerify16 = 0x8F, + eCmdInvalid = 0xC2, + eCmdSasiCmdAssign = 0x0E + }; + +public: + enum { + UnitMax = 32 // Maximum number of logical units + }; + + const int UNKNOWN_SCSI_ID = -1; + const int DEFAULT_BUFFER_SIZE = 0x1000; + // TODO Remove this duplicate + const int DAYNAPORT_BUFFER_SIZE = 0x1000000; + // For timing adjustments enum { min_exec_time_sasi = 100, // SASI BOOT/FORMAT 30:NG 35:OK min_exec_time_scsi = 50 }; - #endif // RASCSI // Internal data definition typedef struct { - // 全般 + // General BUS::phase_t phase; // Transition phase - int id; // Controller ID (0-7) - BUS *bus; // Bus + int m_scsi_id; // Controller ID (0-7) + BUS *bus; // Bus // commands - DWORD cmd[10]; // Command data + DWORD cmd[16]; // Command data DWORD status; // Status data DWORD message; // Message data - #ifdef RASCSI // Run DWORD execstart; // Execution start time - #endif // RASCSI // Transfer BYTE *buffer; // Transfer data buffer int bufsize; // Transfer data buffer size - DWORD blocks; // Number of transfer block - DWORD next; // Next record + uint32_t blocks; // Number of transfer block + DWORD next; // Next record DWORD offset; // Transfer offset DWORD length; // Transfer remaining length // Logical unit - Disk *unit[UnitMax]; // Logical Unit + Disk *unit[UnitMax]; + + // The current device + Disk *device; + + // The LUN from the IDENTIFY message + int lun; } ctrl_t; public: // Basic Functions - #ifdef RASCSI SASIDEV(); - #else - - SASIDEV(Device *dev); // Constructor - - #endif //RASCSI virtual ~SASIDEV(); // Destructor - virtual void FASTCALL Reset(); // Device Reset - - #ifndef RASCSI - virtual BOOL FASTCALL Save(Fileio *fio, int ver); // Save - virtual BOOL FASTCALL Load(Fileio *fio, int ver); // Load - #endif //RASCSI + virtual void Reset(); // Device Reset // External API - virtual BUS::phase_t FASTCALL Process(); // Run + virtual BUS::phase_t Process(); // Run // Connect - void FASTCALL Connect(int id, BUS *sbus); // Controller connection - Disk* FASTCALL GetUnit(int no); // Get logical unit - void FASTCALL SetUnit(int no, Disk *dev); // Logical unit setting - BOOL FASTCALL HasUnit(); // Has a valid logical unit + void Connect(int id, BUS *sbus); // Controller connection + Disk* GetUnit(int no); // Get logical unit + void SetUnit(int no, Disk *dev); // Logical unit setting + bool HasUnit(); // Has a valid logical unit // Other - BUS::phase_t FASTCALL GetPhase() {return ctrl.phase;} // Get the phase - #ifdef DISK_LOG + BUS::phase_t GetPhase() {return ctrl.phase;} // Get the phase - // Function to get the current phase as a String. - void FASTCALL GetPhaseStr(char *str); - #endif + int GetSCSIID() {return ctrl.m_scsi_id;} // Get the ID + ctrl_t* GetCtrl() { return &ctrl; } // Get the internal information address + virtual bool IsSASI() const { return true; } // SASI Check + virtual bool IsSCSI() const { return false; } // SCSI check - int FASTCALL GetID() {return ctrl.id;} // Get the ID - void FASTCALL GetCTRL(ctrl_t *buffer); // Get the internal information - ctrl_t* FASTCALL GetWorkAddr() { return &ctrl; } // Get the internal information address - virtual BOOL FASTCALL IsSASI() const {return TRUE;} // SASI Check - virtual BOOL FASTCALL IsSCSI() const {return FALSE;} // SCSI check - Disk* FASTCALL GetBusyUnit(); // Get the busy unit +public: + void DataIn(); // Data in phase + void Status(); // Status phase + void MsgIn(); // Message in phase + void DataOut(); // Data out phase + + int GetEffectiveLun() const; + + virtual void Error(ERROR_CODES::sense_key sense_key = ERROR_CODES::sense_key::NO_SENSE, + ERROR_CODES::asc = ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION); // Common error handling protected: // Phase processing - virtual void FASTCALL BusFree(); // Bus free phase - virtual void FASTCALL Selection(); // Selection phase - virtual void FASTCALL Command(); // Command phase - virtual void FASTCALL Execute(); // Execution phase - void FASTCALL Status(); // Status phase - void FASTCALL MsgIn(); // Message in phase - void FASTCALL DataIn(); // Data in phase - void FASTCALL DataOut(); // Data out phase - virtual void FASTCALL Error(); // Common error handling + virtual void BusFree(); // Bus free phase + virtual void Selection(); // Selection phase + virtual void Command(); // Command phase + virtual void Execute(); // Execution phase - // commands - void FASTCALL CmdTestUnitReady(); // TEST UNIT READY command - void FASTCALL CmdRezero(); // REZERO UNIT command - void FASTCALL CmdRequestSense(); // REQUEST SENSE command - void FASTCALL CmdFormat(); // FORMAT command - void FASTCALL CmdReassign(); // REASSIGN BLOCKS command - void FASTCALL CmdReserveUnit(); // RESERVE UNIT command - void FASTCALL CmdReleaseUnit(); // RELEASE UNIT command - void FASTCALL CmdRead6(); // READ(6) command - void FASTCALL CmdWrite6(); // WRITE(6) command - void FASTCALL CmdSeek6(); // SEEK(6) command - void FASTCALL CmdAssign(); // ASSIGN command - void FASTCALL CmdSpecify(); // SPECIFY command - void FASTCALL CmdInvalid(); // Unsupported command + // Commands + void CmdTestUnitReady(); // TEST UNIT READY command + void CmdRezero(); // REZERO UNIT command + void CmdRequestSense(); // REQUEST SENSE command + void CmdFormat(); // FORMAT command + void CmdReassignBlocks(); // REASSIGN BLOCKS command + void CmdReserveUnit(); // RESERVE UNIT command + void CmdReleaseUnit(); // RELEASE UNIT command + void CmdRead6(); // READ(6) command + void CmdWrite6(); // WRITE(6) command + void CmdSeek6(); // SEEK(6) command + void CmdAssign(); // ASSIGN command + void CmdSpecify(); // SPECIFY command - // データ転送 - virtual void FASTCALL Send(); // Send data + // Data transfer + virtual void Send(); // Send data + virtual void Receive(); // Receive data - #ifndef RASCSI - virtual void FASTCALL SendNext(); // Continue sending data - #endif // RASCSI - - virtual void FASTCALL Receive(); // Receive data - - #ifndef RASCSI - virtual void FASTCALL ReceiveNext(); // Continue receiving data - #endif // RASCSI - - BOOL FASTCALL XferIn(BYTE* buf); // Data transfer IN - BOOL FASTCALL XferOut(BOOL cont); // Data transfer OUT + bool XferIn(BYTE* buf); // Data transfer IN + bool XferOut(bool cont); // Data transfer OUT // Special operations - void FASTCALL FlushUnit(); // Flush the logical unit + void FlushUnit(); // Flush the logical unit - // Log - void FASTCALL Log(Log::loglevel level, const char *format, ...); // Log output - -protected: - #ifndef RASCSI - Device *host; // Host device - #endif // RASCSI ctrl_t ctrl; // Internal data }; diff --git a/src/raspberrypi/controllers/scsidev_ctrl.cpp b/src/raspberrypi/controllers/scsidev_ctrl.cpp index 50a0e2ea..c07d35b6 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.cpp +++ b/src/raspberrypi/controllers/scsidev_ctrl.cpp @@ -13,10 +13,11 @@ // [ SCSI device controller ] // //--------------------------------------------------------------------------- +#include "log.h" #include "controllers/scsidev_ctrl.h" #include "gpiobus.h" -#include "devices/scsi_host_bridge.h" -#include "rascsi_version.h" +#include "devices/scsi_daynaport.h" +#include //=========================================================================== // @@ -29,32 +30,30 @@ // Constructor // //--------------------------------------------------------------------------- -#ifdef RASCSI SCSIDEV::SCSIDEV() : SASIDEV() -#else -SCSIDEV::SCSIDEV(Device *dev) : SASIDEV(dev) -#endif { // Synchronous transfer work initialization scsi.syncenable = FALSE; scsi.syncperiod = 50; scsi.syncoffset = 0; - scsi.atnmsg = FALSE; + scsi.atnmsg = false; scsi.msc = 0; memset(scsi.msb, 0x00, sizeof(scsi.msb)); } +SCSIDEV::~SCSIDEV() +{ +} + //--------------------------------------------------------------------------- // // Device reset // //--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Reset() +void SCSIDEV::Reset() { - ASSERT(this); - // Work initialization - scsi.atnmsg = FALSE; + scsi.atnmsg = false; scsi.msc = 0; memset(scsi.msb, 0x00, sizeof(scsi.msb)); @@ -67,23 +66,19 @@ void FASTCALL SCSIDEV::Reset() // Process // //--------------------------------------------------------------------------- -BUS::phase_t FASTCALL SCSIDEV::Process() +BUS::phase_t SCSIDEV::Process() { - ASSERT(this); - // Do nothing if not connected - if (ctrl.id < 0 || ctrl.bus == NULL) { + if (ctrl.m_scsi_id < 0 || ctrl.bus == NULL) { return ctrl.phase; } // Get bus information ((GPIOBUS*)ctrl.bus)->Aquire(); - // Reset + // Check to see if the reset signal was asserted if (ctrl.bus->GetRST()) { - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - RESET信号受信"); - #endif // DISK_LOG + LOGWARN("RESET signal received!"); // Reset the controller Reset(); @@ -100,7 +95,7 @@ BUS::phase_t FASTCALL SCSIDEV::Process() BusFree(); break; - // Selection phase + // Selection case BUS::selection: Selection(); break; @@ -144,44 +139,36 @@ BUS::phase_t FASTCALL SCSIDEV::Process() return ctrl.phase; } -//--------------------------------------------------------------------------- -// -// Phaes -// -//--------------------------------------------------------------------------- - //--------------------------------------------------------------------------- // // Bus free phase // //--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::BusFree() +void SCSIDEV::BusFree() { - ASSERT(this); - // Phase change if (ctrl.phase != BUS::busfree) { - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - Bus free phase"); - #endif // DISK_LOG + LOGTRACE("%s Bus free phase", __PRETTY_FUNCTION__); // Phase setting ctrl.phase = BUS::busfree; - // Signal line + // Set Signal lines ctrl.bus->SetREQ(FALSE); ctrl.bus->SetMSG(FALSE); ctrl.bus->SetCD(FALSE); ctrl.bus->SetIO(FALSE); - ctrl.bus->SetBSY(FALSE); + ctrl.bus->SetBSY(false); // Initialize status and message ctrl.status = 0x00; ctrl.message = 0x00; // Initialize ATN message reception status - scsi.atnmsg = FALSE; + scsi.atnmsg = false; + + ctrl.lun = -1; + return; } @@ -196,34 +183,28 @@ void FASTCALL SCSIDEV::BusFree() // Selection Phase // //--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Selection() +void SCSIDEV::Selection() { - DWORD id; - - ASSERT(this); - // Phase change if (ctrl.phase != BUS::selection) { // invalid if IDs do not match - id = 1 << ctrl.id; + DWORD id = 1 << ctrl.m_scsi_id; if ((ctrl.bus->GetDAT() & id) == 0) { return; } - // End if there is no valid unit + // Return if there is no valid LUN if (!HasUnit()) { return; } - #if defined(DISK_LOG) - Log(Log::Normal,"SCSI - Selection Phase ID=%d (with device)", ctrl.id); - #endif // DISK_LOG + LOGTRACE("%s Selection Phase ID=%d (with device)", __PRETTY_FUNCTION__, (int)ctrl.m_scsi_id); // Phase setting ctrl.phase = BUS::selection; // Raise BSY and respond - ctrl.bus->SetBSY(TRUE); + ctrl.bus->SetBSY(true); return; } @@ -243,13 +224,9 @@ void FASTCALL SCSIDEV::Selection() // Execution Phase // //--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Execute() +void SCSIDEV::Execute() { - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - Execution phase command $%02X", ctrl.cmd[0]); - #endif // DISK_LOG + LOGTRACE("%s Execution phase command $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]); // Phase Setting ctrl.phase = BUS::execute; @@ -257,182 +234,53 @@ void FASTCALL SCSIDEV::Execute() // Initialization for data transfer ctrl.offset = 0; ctrl.blocks = 1; - #ifdef RASCSI ctrl.execstart = SysTimer::GetTimerLow(); - #endif // RASCSI - - // Process by command - switch (ctrl.cmd[0]) { - // TEST UNIT READY - case 0x00: - CmdTestUnitReady(); - return; - - // REZERO - case 0x01: - CmdRezero(); - return; - - // REQUEST SENSE - case 0x03: - CmdRequestSense(); - return; - - // FORMAT UNIT - case 0x04: - CmdFormat(); - return; - - // REASSIGN BLOCKS - case 0x07: - CmdReassign(); - return; - - // READ(6) - case 0x08: - CmdRead6(); - return; - - // WRITE(6) - case 0x0a: - CmdWrite6(); - return; - - // SEEK(6) - case 0x0b: - CmdSeek6(); - return; - - // INQUIRY - case 0x12: - CmdInquiry(); - return; - - // MODE SELECT - case 0x15: - CmdModeSelect(); - return; - - // RESERVE(6) - case 0x16: - CmdReserve6(); - return; - - // RESERVE(10) - case 0x56: - CmdReserve10(); - return; - - // RELEASE(6) - case 0x17: - CmdRelease6(); - return; - - // RELEASE(10) - case 0x57: - CmdRelease10(); - return; - - // MDOE SENSE - case 0x1a: - CmdModeSense(); - return; - - // START STOP UNIT - case 0x1b: - CmdStartStop(); - return; - - // SEND DIAGNOSTIC - case 0x1d: - CmdSendDiag(); - return; - - // PREVENT/ALLOW MEDIUM REMOVAL - case 0x1e: - CmdRemoval(); - return; - - // READ CAPACITY - case 0x25: - CmdReadCapacity(); - return; - - // READ(10) - case 0x28: - CmdRead10(); - return; - - // WRITE(10) - case 0x2a: - CmdWrite10(); - return; - - // SEEK(10) - case 0x2b: - CmdSeek10(); - return; - - // WRITE and VERIFY - case 0x2e: - CmdWrite10(); - return; - - // VERIFY - case 0x2f: - CmdVerify(); - return; - - // SYNCHRONIZE CACHE - case 0x35: - CmdSynchronizeCache(); - return; - - // READ DEFECT DATA(10) - case 0x37: - CmdReadDefectData10(); - return; - - // READ TOC - case 0x43: - CmdReadToc(); - return; - - // PLAY AUDIO(10) - case 0x45: - CmdPlayAudio10(); - return; - - // PLAY AUDIO MSF - case 0x47: - CmdPlayAudioMSF(); - return; - - // PLAY AUDIO TRACK - case 0x48: - CmdPlayAudioTrack(); - return; - - // MODE SELECT(10) - case 0x55: - CmdModeSelect10(); - return; - - // MDOE SENSE(10) - case 0x5a: - CmdModeSense10(); - return; - - // SPECIFY (SASI only/Suppress warning when using SxSI) - case 0xc2: - CmdInvalid(); - return; + // Discard pending sense data from the previous command if the current command is not REQUEST SENSE + if ((SCSIDEV::scsi_command)ctrl.cmd[0] != eCmdRequestSense) { + ctrl.status = 0; } - // No other support - Log(Log::Normal, "SCSI - Unsupported command received: $%02X", ctrl.cmd[0]); - CmdInvalid(); + LOGDEBUG("++++ CMD ++++ %s Executing command $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]); + + int lun = GetEffectiveLun(); + if (!ctrl.unit[lun]) { + if ((SCSIDEV::scsi_command)ctrl.cmd[0] != eCmdInquiry && (SCSIDEV::scsi_command)ctrl.cmd[0] != eCmdRequestSense) { + LOGDEBUG("Invalid LUN %d for ID %d", lun, GetSCSIID()); + + Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN); + return; + } + // Use LUN 0 for INQUIRY and REQUEST SENSE because LUN0 is assumed to be always available. + // INQUIRY and REQUEST SENSE have a special LUN handling of their own, required by the SCSI standard. + else { + assert(ctrl.unit[0]); + + lun = 0; + } + } + + ctrl.device = ctrl.unit[lun]; + + // Discard pending sense data from the previous command if the current command is not REQUEST SENSE + if ((SCSIDEV::scsi_command)ctrl.cmd[0] != eCmdRequestSense) { + ctrl.device->SetStatusCode(0); + } + + if (!ctrl.device->Dispatch(this)) { + LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetSCSIID(), lun, (BYTE)ctrl.cmd[0]); + + Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_COMMAND_OPERATION_CODE); + } + + // SCSI-2 p.104 4.4.3 Incorrect logical unit handling + if ((SCSIDEV::scsi_command)ctrl.cmd[0] == eCmdInquiry && !ctrl.unit[lun]) { + lun = GetEffectiveLun(); + + LOGDEBUG("Reporting LUN %d for device ID %d as not supported", lun, ctrl.device->GetId()); + + ctrl.buffer[0] = 0x7f; + } } //--------------------------------------------------------------------------- @@ -440,20 +288,17 @@ void FASTCALL SCSIDEV::Execute() // Message out phase // //--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::MsgOut() +void SCSIDEV::MsgOut() { - ASSERT(this); + LOGTRACE("%s ID %d",__PRETTY_FUNCTION__, GetSCSIID()); // Phase change if (ctrl.phase != BUS::msgout) { + LOGTRACE("Message Out Phase"); - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - Message Out Phase"); // Message out phase after selection - #endif // DISK_LOG - - // process the IDENTIFY message + // process the IDENTIFY message if (ctrl.phase == BUS::selection) { - scsi.atnmsg = TRUE; + scsi.atnmsg = true; scsi.msc = 0; memset(scsi.msb, 0x00, sizeof(scsi.msb)); } @@ -471,31 +316,11 @@ void FASTCALL SCSIDEV::MsgOut() ctrl.length = 1; ctrl.blocks = 1; - #ifndef RASCSI - // Request message - ctrl.bus->SetREQ(TRUE); - #endif // RASCSI return; } - #ifdef RASCSI // Receive Receive(); - #else - - // Requesting - if (ctrl.bus->GetREQ()) { - // Sent by the initiator - if (ctrl.bus->GetACK()) { - Receive(); - } - } else { - // Request the initator to - if (!ctrl.bus->GetACK()) { - ReceiveNext(); - } - } - #endif // RASCSI } //--------------------------------------------------------------------------- @@ -503,10 +328,8 @@ void FASTCALL SCSIDEV::MsgOut() // Common Error Handling // //--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Error() +void SCSIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc) { - ASSERT(this); - // Get bus information ((GPIOBUS*)ctrl.bus)->Aquire(); @@ -526,970 +349,53 @@ void FASTCALL SCSIDEV::Error() return; } - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - Error (to status phase)"); - #endif // DISK_LOG + DWORD lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun] || asc == ERROR_CODES::INVALID_LUN) { + lun = 0; + } + + if (sense_key || asc) { + // Set Sense Key and ASC for a subsequent REQUEST SENSE + ctrl.unit[lun]->SetStatusCode((sense_key << 16) | (asc << 8)); + } - // Set status and message(CHECK CONDITION) ctrl.status = 0x02; ctrl.message = 0x00; - // status phase + LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__); + Status(); } -//--------------------------------------------------------------------------- -// -// Command -// -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// -// INQUIRY -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdInquiry() -{ - Disk *disk; - int lun; - DWORD major; - DWORD minor; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - INQUIRY Command"); - #endif // DISK_LOG - - // Find a valid unit - disk = NULL; - for (lun = 0; lun < UnitMax; lun++) { - if (ctrl.unit[lun]) { - disk = ctrl.unit[lun]; - break; - } - } - - // Processed on the disk side (it is originally processed by the controller) - if (disk) { - major = (DWORD)rascsi_major_version; - minor = (DWORD)rascsi_minor_version; - ctrl.length = - ctrl.unit[lun]->Inquiry(ctrl.cmd, ctrl.buffer, major, minor); - } else { - ctrl.length = 0; - } - - if (ctrl.length <= 0) { - // failure (error) - Error(); - return; - } - - // Add synchronous transfer support information - if (scsi.syncenable) { - ctrl.buffer[7] |= (1 << 4); - } - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// MODE SELECT -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdModeSelect() -{ - DWORD lun; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - MODE SELECT Command"); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->SelectCheck(ctrl.cmd); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Data out phase - DataOut(); -} - -// The following commands RESERVE(6), RESERVE(10), RELEASE(6) and RELEASE(10) -// are not properly implemented. These commands are used in multi-initator environments -// which this project is not targeted at. For now, we simply reply with an OK. -// -phrax 2021-03-06 - -//--------------------------------------------------------------------------- -// -// RESERVE(6) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdReserve6() -{ - ASSERT(this); - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - RESERVE(6) Command"); - #endif // DISK_LOG - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// RESERVE(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdReserve10() -{ - ASSERT(this); - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - RESERVE(10) Command"); - #endif // DISK_LOG - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// RELEASE(6) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdRelease6() -{ - ASSERT(this); - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - RELEASE(6) Command"); - #endif // DISK_LOG - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// RELEASE(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdRelease10() -{ - ASSERT(this); - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - RELEASE(10) Command"); - #endif // DISK_LOG - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// MODE SENSE -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdModeSense() -{ - DWORD lun; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - MODE SENSE Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->ModeSense(ctrl.cmd, ctrl.buffer); - ASSERT(ctrl.length >= 0); - if (ctrl.length == 0) { - Log(Log::Warning,"SCSI - Not supported MODE SENSE page $%02X", ctrl.cmd[2]); - - // Failure (Error) - Error(); - return; - } - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// START STOP UNIT -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdStartStop() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - START STOP UNIT Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->StartStop(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// SEND DIAGNOSTIC -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdSendDiag() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - SEND DIAGNOSTIC Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->SendDiag(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// PREVENT/ALLOW MEDIUM REMOVAL -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdRemoval() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - PREVENT/ALLOW MEDIUM REMOVAL Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->Removal(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// READ CAPACITY -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdReadCapacity() -{ - DWORD lun; - int length; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - READ CAPACITY Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - length = ctrl.unit[lun]->ReadCapacity(ctrl.cmd, ctrl.buffer); - ASSERT(length >= 0); - if (length <= 0) { - Error(); - return; - } - - // Length setting - ctrl.length = length; - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// READ(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdRead10() -{ - DWORD lun; - DWORD record; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Receive message if host bridge - if (ctrl.unit[lun]->GetID() == MAKEID('S', 'C', 'B', 'R')) { - CmdGetMessage10(); - return; - } - - // Get record number and block number - record = ctrl.cmd[2]; - record <<= 8; - record |= ctrl.cmd[3]; - record <<= 8; - record |= ctrl.cmd[4]; - record <<= 8; - record |= ctrl.cmd[5]; - ctrl.blocks = ctrl.cmd[7]; - ctrl.blocks <<= 8; - ctrl.blocks |= ctrl.cmd[8]; - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - READ(10) command record=%08X block=%d", record, ctrl.blocks); - #endif // DISK_LOG - - // Do not process 0 blocks - if (ctrl.blocks == 0) { - Status(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->Read(ctrl.buffer, record); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.next = record + 1; - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// WRITE(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdWrite10() -{ - DWORD lun; - DWORD record; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Receive message with host bridge - if (ctrl.unit[lun]->GetID() == MAKEID('S', 'C', 'B', 'R')) { - CmdSendMessage10(); - return; - } - - // Get record number and block number - record = ctrl.cmd[2]; - record <<= 8; - record |= ctrl.cmd[3]; - record <<= 8; - record |= ctrl.cmd[4]; - record <<= 8; - record |= ctrl.cmd[5]; - ctrl.blocks = ctrl.cmd[7]; - ctrl.blocks <<= 8; - ctrl.blocks |= ctrl.cmd[8]; - - #if defined(DISK_LOG) - Log(Log::Normal,"SCSI - WRTIE(10) command record=%08X blocks=%d", record, ctrl.blocks); - #endif // DISK_LOG - - // Do not process 0 blocks - if (ctrl.blocks == 0) { - Status(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->WriteCheck(record); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.next = record + 1; - - // Data out phase - DataOut(); -} - -//--------------------------------------------------------------------------- -// -// SEEK(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdSeek10() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - SEEK(10) Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->Seek(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// VERIFY -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdVerify() -{ - DWORD lun; - BOOL status; - DWORD record; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Get record number and block number - record = ctrl.cmd[2]; - record <<= 8; - record |= ctrl.cmd[3]; - record <<= 8; - record |= ctrl.cmd[4]; - record <<= 8; - record |= ctrl.cmd[5]; - ctrl.blocks = ctrl.cmd[7]; - ctrl.blocks <<= 8; - ctrl.blocks |= ctrl.cmd[8]; - - #if defined(DISK_LOG) - Log(Log::Normal,"SCSI - VERIFY command record=%08X blocks=%d", record, ctrl.blocks); - #endif // DISK_LOG - - // Do not process 0 blocks - if (ctrl.blocks == 0) { - Status(); - return; - } - - // if BytChk=0 - if ((ctrl.cmd[1] & 0x02) == 0) { - // Command processing on drive - status = ctrl.unit[lun]->Seek(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); - return; - } - - // Test loading - ctrl.length = ctrl.unit[lun]->Read(ctrl.buffer, record); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.next = record + 1; - - // Data out phase - DataOut(); -} - -//--------------------------------------------------------------------------- -// -// SYNCHRONIZE CACHE -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdSynchronizeCache() -{ - DWORD lun; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Make it do something (not implemented)... - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// READ DEFECT DATA(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdReadDefectData10() -{ - DWORD lun; - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - READ DEFECT DATA(10) Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->ReadDefectData10(ctrl.cmd, ctrl.buffer); - ASSERT(ctrl.length >= 0); - - if (ctrl.length <= 4) { - Error(); - return; - } - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// READ TOC -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdReadToc() -{ - DWORD lun; - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->ReadToc(ctrl.cmd, ctrl.buffer); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdPlayAudio10() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->PlayAudio(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO MSF -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdPlayAudioMSF() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->PlayAudioMSF(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO TRACK -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdPlayAudioTrack() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->PlayAudioTrack(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// MODE SELECT10 -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdModeSelect10() -{ - DWORD lun; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - MODE SELECT10 Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->SelectCheck10(ctrl.cmd); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Data out phase - DataOut(); -} - -//--------------------------------------------------------------------------- -// -// MODE SENSE(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdModeSense10() -{ - DWORD lun; - - ASSERT(this); - - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - MODE SENSE(10) Command "); - #endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->ModeSense10(ctrl.cmd, ctrl.buffer); - ASSERT(ctrl.length >= 0); - if (ctrl.length == 0) { - Log(Log::Warning,"SCSI - Not supported MODE SENSE(10) page $%02X", ctrl.cmd[2]); - - // Failure (Error) - Error(); - return; - } - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// GET MESSAGE(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdGetMessage10() -{ - DWORD lun; - SCSIBR *bridge; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Error if not a host bridge - if (ctrl.unit[lun]->GetID() != MAKEID('S', 'C', 'B', 'R')) { - Error(); - return; - } - - // Reallocate buffer (because it is not transfer for each block) - if (ctrl.bufsize < 0x1000000) { - free(ctrl.buffer); - ctrl.bufsize = 0x1000000; - ctrl.buffer = (BYTE *)malloc(ctrl.bufsize); - } - - // Process with drive - bridge = (SCSIBR*)ctrl.unit[lun]; - ctrl.length = bridge->GetMessage10(ctrl.cmd, ctrl.buffer); - - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.blocks = 1; - ctrl.next = 1; - - // Data in phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// SEND MESSAGE(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdSendMessage10() -{ - DWORD lun; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Error if not a host bridge - if (ctrl.unit[lun]->GetID() != MAKEID('S', 'C', 'B', 'R')) { - Error(); - return; - } - - // Reallocate buffer (because it is not transfer for each block) - if (ctrl.bufsize < 0x1000000) { - free(ctrl.buffer); - ctrl.bufsize = 0x1000000; - ctrl.buffer = (BYTE *)malloc(ctrl.bufsize); - } - - // Set transfer amount - ctrl.length = ctrl.cmd[6]; - ctrl.length <<= 8; - ctrl.length |= ctrl.cmd[7]; - ctrl.length <<= 8; - ctrl.length |= ctrl.cmd[8]; - - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.blocks = 1; - ctrl.next = 1; - - // Light phase - DataOut(); -} - -//=========================================================================== -// -// Data Transfer -// -//=========================================================================== - //--------------------------------------------------------------------------- // // Send data // //--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Send() +void SCSIDEV::Send() { - #ifdef RASCSI - int len; - #endif // RASCSI - BOOL result; - - ASSERT(this); ASSERT(!ctrl.bus->GetREQ()); ASSERT(ctrl.bus->GetIO()); - #ifdef RASCSI //if Length! = 0, send if (ctrl.length != 0) { - len = ctrl.bus->SendHandShake( - &ctrl.buffer[ctrl.offset], ctrl.length); + ostringstream s; + s << __PRETTY_FUNCTION__ << " sending handhake with offset " << ctrl.offset << ", length " << ctrl.length; + LOGTRACE("%s", s.str().c_str()); + + // TODO Get rid of Daynaport specific code in this class + // The Daynaport needs to have a delay after the size/flags field + // of the read response. In the MacOS driver, it looks like the + // driver is doing two "READ" system calls. + int len; + if (ctrl.unit[0] && ctrl.unit[0]->IsDaynaPort()) { + len = ((GPIOBUS*)ctrl.bus)->SendHandShake( + &ctrl.buffer[ctrl.offset], ctrl.length, SCSIDaynaPort::DAYNAPORT_READ_HEADER_SZ); + } + else + { + len = ctrl.bus->SendHandShake(&ctrl.buffer[ctrl.offset], ctrl.length, BUS::SEND_NO_DELAY); + } // If you cannot send all, move to status phase if (len != (int)ctrl.length) { @@ -1502,33 +408,19 @@ void FASTCALL SCSIDEV::Send() ctrl.length = 0; return; } - #else - // offset and length - ASSERT(ctrl.length >= 1); - ctrl.offset++; - ctrl.length--; - - // Immediately after ACK is asserted, if the data has been - // set by SendNext, raise the request - if (ctrl.length != 0) { - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - return; - } - #endif // RASCSI // Block subtraction, result initialization ctrl.blocks--; - result = TRUE; + bool result = true; // Processing after data collection (read/data-in only) if (ctrl.phase == BUS::datain) { if (ctrl.blocks != 0) { // set next buffer (set offset, length) result = XferIn(ctrl.buffer); - #ifndef RASCSI - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); - #endif // RASCSI + ostringstream s; + s << __PRETTY_FUNCTION__ << " Processing after data collection. Blocks: " << ctrl.blocks; + LOGTRACE("%s", s.str().c_str()); } } @@ -1540,23 +432,23 @@ void FASTCALL SCSIDEV::Send() // Continue sending if block !=0 if (ctrl.blocks != 0){ + ostringstream s; + s << __PRETTY_FUNCTION__ << " Continuing to send. Blocks: " << ctrl.blocks; + LOGTRACE("%s", s.str().c_str()); ASSERT(ctrl.length > 0); ASSERT(ctrl.offset == 0); - #ifndef RASCSI - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - #endif // RASCSI return; } // Move to next phase + LOGTRACE("%s Move to next phase %s (%d)", __PRETTY_FUNCTION__, BUS::GetPhaseStrRaw(ctrl.phase), ctrl.phase); switch (ctrl.phase) { // Message in phase case BUS::msgin: // Completed sending response to extended message of IDENTIFY message if (scsi.atnmsg) { // flag off - scsi.atnmsg = FALSE; + scsi.atnmsg = false; // command phase Command(); @@ -1588,167 +480,57 @@ void FASTCALL SCSIDEV::Send() } } -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Continue data transmission..... -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::SendNext() -{ - ASSERT(this); - - // REQ is up - ASSERT(ctrl.bus->GetREQ()); - ASSERT(ctrl.bus->GetIO()); - - // Signal line operated by the target - ctrl.bus->SetREQ(FALSE); - - // If there is data in the buffer, set it first - if (ctrl.length > 1) { - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset + 1]); - } -} -#endif // RASCSI - -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Receive data -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Receive() -{ - DWORD data; - - ASSERT(this); - - // Req is up - ASSERT(ctrl.bus->GetREQ()); - ASSERT(!ctrl.bus->GetIO()); - - // Get data - data = (DWORD)ctrl.bus->GetDAT(); - - // Signal line operated by the target - ctrl.bus->SetREQ(FALSE); - - switch (ctrl.phase) { - // Command phase - case BUS::command: - ctrl.cmd[ctrl.offset] = data; - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - Command phase $%02X", data); - #endif // DISK_LOG - - // Set the length again with the first data (offset 0) - if (ctrl.offset == 0) { - if (ctrl.cmd[0] >= 0x20) { - // 10バイトCDB - ctrl.length = 10; - } - } - break; - - // Message out phase - case BUS::msgout: - ctrl.message = data; - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - Message out phase $%02X", data); - #endif // DISK_LOG - break; - - // Data out phase - case BUS::dataout: - ctrl.buffer[ctrl.offset] = (BYTE)data; - break; - - // Other (impossible) - default: - ASSERT(FALSE); - break; - } -} -#endif // RASCSI - -#ifdef RASCSI //--------------------------------------------------------------------------- // // Receive Data // //--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Receive() -#else - -//--------------------------------------------------------------------------- -// -// Continue receiving data -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::ReceiveNext() -#endif // RASCSI +void SCSIDEV::Receive() { - #ifdef RASCSI int len; - #endif // RASCSI - BOOL result; - int i; BYTE data; - ASSERT(this); + LOGTRACE("%s",__PRETTY_FUNCTION__); // REQ is low ASSERT(!ctrl.bus->GetREQ()); ASSERT(!ctrl.bus->GetIO()); - #ifdef RASCSI // Length != 0 if received if (ctrl.length != 0) { + LOGTRACE("%s length was != 0", __PRETTY_FUNCTION__); // Receive - len = ctrl.bus->ReceiveHandShake( - &ctrl.buffer[ctrl.offset], ctrl.length); + len = ctrl.bus->ReceiveHandShake(&ctrl.buffer[ctrl.offset], ctrl.length); // If not able to receive all, move to status phase if (len != (int)ctrl.length) { + LOGERROR("%s Not able to receive all data. Going to error",__PRETTY_FUNCTION__); Error(); return; } // Offset and Length ctrl.offset += ctrl.length; - ctrl.length = 0;; + ctrl.length = 0; return; } - #else - // Offset and Length - ASSERT(ctrl.length >= 1); - ctrl.offset++; - ctrl.length--; - - // If length!=0, set req again - if (ctrl.length != 0) { - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - return; - } - #endif // RASCSI // Block subtraction, result initialization ctrl.blocks--; - result = TRUE; + bool result = true; // Processing after receiving data (by phase) + LOGTRACE("%s ctrl.phase: %d (%s)",__PRETTY_FUNCTION__, (int)ctrl.phase, BUS::GetPhaseStrRaw(ctrl.phase)); switch (ctrl.phase) { // Data out phase case BUS::dataout: if (ctrl.blocks == 0) { // End with this buffer - result = XferOut(FALSE); + result = XferOut(false); } else { // Continue to next buffer (set offset, length) - result = XferOut(TRUE); + result = XferOut(true); } break; @@ -1779,10 +561,6 @@ void FASTCALL SCSIDEV::ReceiveNext() if (ctrl.blocks != 0){ ASSERT(ctrl.length > 0); ASSERT(ctrl.offset == 0); - #ifndef RASCSI - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - #endif // RASCSI return; } @@ -1790,20 +568,12 @@ void FASTCALL SCSIDEV::ReceiveNext() switch (ctrl.phase) { // Command phase case BUS::command: - #ifdef RASCSI - // Command data transfer - len = 6; - if (ctrl.buffer[0] >= 0x20 && ctrl.buffer[0] <= 0x7D) { - // 10 byte CDB - len = 10; - } - for (i = 0; i < len; i++) { + len = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]); + + for (int i = 0; i < len; i++) { ctrl.cmd[i] = (DWORD)ctrl.buffer[i]; - #if defined(DISK_LOG) - Log(Log::Normal, "SCSI - Command $%02X", ctrl.cmd[i]); - #endif // DISK_LOG + LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i]); } - #endif // RASCSI // Execution Phase Execute(); @@ -1817,34 +587,26 @@ void FASTCALL SCSIDEV::ReceiveNext() ctrl.offset = 0; ctrl.length = 1; ctrl.blocks = 1; - #ifndef RASCSI - // Request message - ctrl.bus->SetREQ(TRUE); - #endif // RASCSI return; } // Parsing messages sent by ATN if (scsi.atnmsg) { - i = 0; + int i = 0; while (i < scsi.msc) { // Message type data = scsi.msb[i]; // ABORT if (data == 0x06) { - #if defined(DISK_LOG) - Log(Log::Normal,"SCSI - Message code ABORT $%02X", data); - #endif // DISK_LOG + LOGTRACE("Message code ABORT $%02X", (int)data); BusFree(); return; } // BUS DEVICE RESET if (data == 0x0C) { - #if defined(DISK_LOG) - Log(Log::Normal,"SCSI - Message code BUS DEVICE RESET $%02X", data); - #endif // DISK_LOG + LOGTRACE("Message code BUS DEVICE RESET $%02X", (int)data); scsi.syncoffset = 0; BusFree(); return; @@ -1852,16 +614,13 @@ void FASTCALL SCSIDEV::ReceiveNext() // IDENTIFY if (data >= 0x80) { - #if defined(DISK_LOG) - Log(Log::Normal,"SCSI - Message code IDENTIFY $%02X", data); - #endif // DISK_LOG + ctrl.lun = data & 0x1F; + LOGTRACE("Message code IDENTIFY $%02X, LUN %d selected", data, ctrl.lun); } // Extended Message if (data == 0x01) { - #if defined(DISK_LOG) - Log(Log::Normal,"SCSI - Message code EXTENDED MESSAGE $%02X", data); - #endif // DISK_LOG + LOGTRACE("Message code EXTENDED MESSAGE $%02X", (int)data); // Check only when synchronous transfer is possible if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) { @@ -1902,7 +661,7 @@ void FASTCALL SCSIDEV::ReceiveNext() } // Initialize ATN message reception status - scsi.atnmsg = FALSE; + scsi.atnmsg = false; // Command phase Command(); @@ -1929,9 +688,8 @@ void FASTCALL SCSIDEV::ReceiveNext() // Transfer MSG // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSIDEV::XferMsg(DWORD msg) +bool SCSIDEV::XferMsg(DWORD msg) { - ASSERT(this); ASSERT(ctrl.phase == BUS::msgout); // Save message out data @@ -1941,5 +699,5 @@ BOOL FASTCALL SCSIDEV::XferMsg(DWORD msg) scsi.msc %= 256; } - return TRUE; + return true; } diff --git a/src/raspberrypi/controllers/scsidev_ctrl.h b/src/raspberrypi/controllers/scsidev_ctrl.h index 8beec1df..c0ce8db6 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.h +++ b/src/raspberrypi/controllers/scsidev_ctrl.h @@ -14,7 +14,9 @@ // //--------------------------------------------------------------------------- #pragma once + #include "controllers/sasidev_ctrl.h" +#include //=========================================================================== // @@ -23,7 +25,50 @@ //=========================================================================== class SCSIDEV : public SASIDEV { + public: + enum scsi_command : int { + eCmdTestUnitReady = 0x00, + eCmdRezero = 0x01, + eCmdRequestSense = 0x03, + eCmdFormat = 0x04, + eCmdReassign = 0x07, + eCmdRead6 = 0x08, + eCmdRetrieveStats = 0x09, // DaynaPort specific command + eCmdWrite6 = 0x0A, + eCmdSeek6 = 0x0B, + eCmdSetIfaceMode = 0x0C, // DaynaPort specific command + eCmdSetMcastAddr = 0x0D, // DaynaPort specific command + eCmdEnableInterface = 0x0E, // DaynaPort specific command + eCmdInquiry = 0x12, + eCmdModeSelect6 = 0x15, + eCmdReserve6 = 0x16, + eCmdRelease6 = 0x17, + eCmdModeSense6 = 0x1A, + eCmdStartStop = 0x1B, + eCmdSendDiag = 0x1D, + eCmdRemoval = 0x1E, + eCmdReadCapacity10 = 0x25, + eCmdRead10 = 0x28, + eCmdWrite10 = 0x2A, + eCmdSeek10 = 0x2B, + eCmdVerify10 = 0x2F, + eCmdSynchronizeCache10 = 0x35, + eCmdReadDefectData10 = 0x37, + eCmdReadToc = 0x43, + eCmdGetEventStatusNotification = 0x4a, + eCmdModeSelect10 = 0x55, + eCmdReserve10 = 0x56, + eCmdRelease10 = 0x57, + eCmdModeSense10 = 0x5A, + eCmdRead16 = 0x88, + eCmdWrite16 = 0x8A, + eCmdVerify16 = 0x8F, + eCmdSynchronizeCache16 = 0x91, + eCmdReadCapacity16 = 0x9E, + eCmdReportLuns = 0xA0 + }; + // Internal data definition typedef struct { // Synchronous transfer @@ -33,75 +78,45 @@ public: int syncack; // Number of synchronous transfer ACKs // ATN message - BOOL atnmsg; + bool atnmsg; int msc; BYTE msb[256]; } scsi_t; public: // Basic Functions -#ifdef RASCSI SCSIDEV(); -#else - SCSIDEV(Device *dev); // Constructor -#endif // RASCSI + ~SCSIDEV(); - void FASTCALL Reset(); // Device Reset + void Reset(); // Device Reset - // 外部API - BUS::phase_t FASTCALL Process(); // Run - - void FASTCALL SyncTransfer(BOOL enable) { scsi.syncenable = enable; } // Synchronouse transfer enable setting + // External API + BUS::phase_t Process(); // Run // Other - BOOL FASTCALL IsSASI() const {return FALSE;} // SASI Check - BOOL FASTCALL IsSCSI() const {return TRUE;} // SCSI check + bool IsSASI() const { return false; } // SASI Check + bool IsSCSI() const { return true; } // SCSI check + + void Error(ERROR_CODES::sense_key sense_key = ERROR_CODES::sense_key::NO_SENSE, + ERROR_CODES::asc asc = ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION) override; // Common error handling private: + // Phase - void FASTCALL BusFree(); // Bus free phase - void FASTCALL Selection(); // Selection phase - void FASTCALL Execute(); // Execution phase - void FASTCALL MsgOut(); // Message out phase - void FASTCALL Error(); // Common erorr handling + void BusFree(); // Bus free phase + void Selection(); // Selection phase + void Execute(); // Execution phase + void MsgOut(); // Message out phase // commands - void FASTCALL CmdInquiry(); // INQUIRY command - void FASTCALL CmdModeSelect(); // MODE SELECT command - void FASTCALL CmdReserve6(); // RESERVE(6) command - void FASTCALL CmdReserve10(); // RESERVE(10) command - void FASTCALL CmdRelease6(); // RELEASE(6) command - void FASTCALL CmdRelease10(); // RELEASE(10) command - void FASTCALL CmdModeSense(); // MODE SENSE command - void FASTCALL CmdStartStop(); // START STOP UNIT command - void FASTCALL CmdSendDiag(); // SEND DIAGNOSTIC command - void FASTCALL CmdRemoval(); // PREVENT/ALLOW MEDIUM REMOVAL command - void FASTCALL CmdReadCapacity(); // READ CAPACITY command - void FASTCALL CmdRead10(); // READ(10) command - void FASTCALL CmdWrite10(); // WRITE(10) command - void FASTCALL CmdSeek10(); // SEEK(10) command - void FASTCALL CmdVerify(); // VERIFY command - void FASTCALL CmdSynchronizeCache(); // SYNCHRONIZE CACHE command - void FASTCALL CmdReadDefectData10(); // READ DEFECT DATA(10) command - void FASTCALL CmdReadToc(); // READ TOC command - void FASTCALL CmdPlayAudio10(); // PLAY AUDIO(10) command - void FASTCALL CmdPlayAudioMSF(); // PLAY AUDIO MSF command - void FASTCALL CmdPlayAudioTrack(); // PLAY AUDIO TRACK INDEX command - void FASTCALL CmdModeSelect10(); // MODE SELECT(10) command - void FASTCALL CmdModeSense10(); // MODE SENSE(10) command - void FASTCALL CmdGetMessage10(); // GET MESSAGE(10) command - void FASTCALL CmdSendMessage10(); // SEND MESSAGE(10) command + void CmdGetEventStatusNotification(); + void CmdModeSelect10(); // MODE SELECT(10) command + void CmdModeSense10(); // MODE SENSE(10) command - // データ転送 - void FASTCALL Send(); // Send data - #ifndef RASCSI - void FASTCALL SendNext(); // Continue sending data - #endif // RASCSI - void FASTCALL Receive(); // Receive data - #ifndef RASCSI - void FASTCALL ReceiveNext(); // Continue receiving data - #endif // RASCSI - BOOL FASTCALL XferMsg(DWORD msg); // Data transfer message + // Data transfer + void Send(); // Send data + void Receive(); // Receive data + bool XferMsg(DWORD msg); // Data transfer message scsi_t scsi; // Internal data }; diff --git a/src/raspberrypi/devices/cfilesystem.cpp b/src/raspberrypi/devices/cfilesystem.cpp index 357ab502..c1b3e06a 100644 --- a/src/raspberrypi/devices/cfilesystem.cpp +++ b/src/raspberrypi/devices/cfilesystem.cpp @@ -19,57 +19,26 @@ //--------------------------------------------------------------------------- #include "os.h" -#include "xm6.h" #include "log.h" #include "filepath.h" #include "cfilesystem.h" - -#ifdef BAREMETAL -//--------------------------------------------------------------------------- -// -// FatFs用タイムスタンプ -// -//--------------------------------------------------------------------------- -#define FF_NORTC_HOUR 6 -#define FF_NORTC_MINUTE 8 -#define FF_NORTC_SECOND 0 - -static DWORD fattime = ( - (DWORD)(FF_NORTC_YEAR - 1980) << 25 | - (DWORD)FF_NORTC_MON << 21 | - (DWORD)FF_NORTC_MDAY << 16 | - (DWORD)(FF_NORTC_HOUR << 11) | - (DWORD)(FF_NORTC_MINUTE << 5) | - (DWORD)(FF_NORTC_SECOND) -); - -DWORD get_fattime(void) -{ - return fattime; -} - -void set_fattime(DWORD n) -{ - fattime = n; -} -#endif // BAREMETAL +#include "../rascsi.h" //--------------------------------------------------------------------------- // -// 漢字コード変換 +// Kanji code conversion // //--------------------------------------------------------------------------- #define IC_BUF_SIZE 1024 static char convert_buf[IC_BUF_SIZE]; -#ifndef BAREMETAL #ifndef __NetBSD__ -// POSIX.1準拠iconv(3)を使用 +// Using POSIX.1 compliant iconv(3) #define CONVERT(src, dest, inbuf, outbuf, outsize) \ convert(src, dest, (char *)inbuf, outbuf, outsize) static void convert(char const *src, char const *dest, char *inbuf, char *outbuf, size_t outsize) #else -// NetBSD版iconv(3)を使用: 第2引数はconst char ** +// Using NetBSD version of iconv(3): The second argument is 'const char **' #define CONVERT(src, dest, inbuf, outbuf, outsize) \ convert(src, dest, inbuf, outbuf, outsize) static void convert(char const *src, char const *dest, @@ -77,45 +46,28 @@ static void convert(char const *src, char const *dest, #endif { #ifndef __APPLE__ - iconv_t cd; - size_t in; - size_t out; - size_t ret; - *outbuf = '\0'; - in = strlen(inbuf); - out = outsize - 1; + size_t in = strlen(inbuf); + size_t out = outsize - 1; - cd = iconv_open(dest, src); + iconv_t cd = iconv_open(dest, src); if (cd == (iconv_t)-1) { return; } - ret = iconv(cd, &inbuf, &in, &outbuf, &out); + size_t ret = iconv(cd, &inbuf, &in, &outbuf, &out); if (ret == (size_t)-1) { return; } iconv_close(cd); *outbuf = '\0'; -#endif //ifndef __macintosh__ +#endif //ifndef __APPLE__ } -#else -// Newlibの中にiconvが含まれてなかったので無変換 -// ベアメタルのFatFS上はSJISでもOKだと思うよ -#define CONVERT(src, dest, inbuf, outbuf, outsize) \ - convert(src, dest, (char *)inbuf, outbuf, outsize) -static void convert(char const *src, char const *dest, - char *inbuf, char *outbuf, size_t outsize) -{ - strcpy(outbuf, inbuf); - strcpy(convert_buf, inbuf); -} -#endif // BAREMETAL //--------------------------------------------------------------------------- // -// SJIS->UTF8変換 +// SJIS->UTF8 conversion // //--------------------------------------------------------------------------- static char* SJIS2UTF8(const char *sjis, char *utf8, size_t bufsize) @@ -126,7 +78,7 @@ static char* SJIS2UTF8(const char *sjis, char *utf8, size_t bufsize) //--------------------------------------------------------------------------- // -// UTF8->SJIS変換 +// UTF8->SJIS conversion // //--------------------------------------------------------------------------- static char* UTF82SJIS(const char *utf8, char *sjis, size_t bufsize) @@ -137,7 +89,7 @@ static char* UTF82SJIS(const char *utf8, char *sjis, size_t bufsize) //--------------------------------------------------------------------------- // -// SJIS->UTF8変換(簡易版) +// SJIS->UTF8 conversion (simplified versoin) // //--------------------------------------------------------------------------- static char* S2U(const char *sjis) @@ -148,7 +100,7 @@ static char* S2U(const char *sjis) //--------------------------------------------------------------------------- // -// UTF8->SJIS変換(簡易版) +// UTF8->SJIS conversion (simplified version) // //--------------------------------------------------------------------------- static char* U2S(const char *utf8) @@ -159,15 +111,14 @@ static char* U2S(const char *utf8) //--------------------------------------------------------------------------- // -/// パス名取得 +/// Get path name /// -/// Human68k用namests構造体から、Human68kパス名を取得する。 -/// 書き込み先バッファは66バイト必要。 +/// From the structure used in Human68k namests, get the Human68k path name. +/// A 66 byte buffer is required for writing. // //--------------------------------------------------------------------------- void Human68k::namests_t::GetCopyPath(BYTE* szPath) const { - ASSERT(this); ASSERT(szPath); BYTE* p = szPath; @@ -186,94 +137,87 @@ void Human68k::namests_t::GetCopyPath(BYTE* szPath) const //--------------------------------------------------------------------------- // -/// ファイル名取得 +/// Get file name /// -/// Human68k用namests構造体から、Human68kファイル名を取得する。 -/// 書き込み先バッファは23バイト必要。 +/// From the structure used in Human68k namests, get the Human68k file name. +/// A 23 byte buffer is required for writing. // //--------------------------------------------------------------------------- void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const { - ASSERT(this); ASSERT(szFilename); size_t i; BYTE* p = szFilename; - // ファイル名本体転送 + // Transfer the base file name for (i = 0; i < 8; i++) { BYTE c = name[i]; if (c == ' ') { - // ファイル名中にスペースが出現した場合、以降のエントリが続いているかどうか確認 - /// @todo 8+3文字とTewntyOne互換モードで動作を変えるべき - // add[0] が有効な文字なら続ける + // Check that the file name continues after a space is detected + /// TODO: Should change this function to be compatible with 8+3 chars and TwentyOne + // Continue if add[0] is a valid character if (add[0] != '\0') goto next_name; - // name[i] より後に空白以外の文字が存在するなら続ける + // Continue if a non-space character exists after name[i] for (size_t j = i + 1; j < 8; j++) { if (name[j] != ' ') goto next_name; } - // ファイル名終端なら転送終了 + // Exit if the file name ends break; } next_name: *p++ = c; } - // 全ての文字を読み込むと、ここで i >= 8 となる + // At this point, the number of read characters becomes i >= 8 - // ファイル名本体が8文字以上なら追加部分も加える + // If the body of the file name exceeds 8 characters, add the extraneous part if (i >= 8) { - // ファイル名追加部分転送 + // Transfer the extraneous part for (i = 0; i < 10; i++) { BYTE c = add[i]; if (c == '\0') break; *p++ = c; } - // 全ての文字を読み込むと、ここで i >= 10 となる + // At this point, the number of read characters becomes i >= 10 } - // 拡張子が存在する場合は転送 + // Transfer the file extension if it exists if (ext[0] != ' ' || ext[1] != ' ' || ext[2] != ' ') { *p++ = '.'; for (i = 0; i < 3; i++) { BYTE c = ext[i]; if (c == ' ') { - // 拡張子中にスペースが出現した場合、以降のエントリが続いているかどうか確認 - /// @todo 8+3文字とTewntyOne互換モードで動作を変えるべき - // ext[i] より後に空白以外の文字が存在するなら続ける + // Check that the file extension continues after a space is detected + /// TODO: Should change this function to be compatible with 8+3 chars and TwentyOne + // Continue if a non-space character exists after ext[i] for (size_t j = i + 1; j < 3; j++) { if (ext[j] != ' ') goto next_ext; } - // 拡張子終端なら転送終了 + // If the extension ends, the transfer ends break; } next_ext: *p++ = c; } - // 全ての文字を読み込むと、ここで i >= 3 となる + // When all the characters are read, here i >= 3 } - // 番兵追加 + // Add sentinel *p = '\0'; } //=========================================================================== // -// ホスト側ドライブ +// Host side drive // //=========================================================================== -//--------------------------------------------------------------------------- -// -/// デフォルトコンストラクタ -// -//--------------------------------------------------------------------------- CHostDrv::CHostDrv() { - // 初期化 m_bWriteProtect = FALSE; m_bEnable = FALSE; m_capCache.sectors = 0; @@ -283,11 +227,6 @@ CHostDrv::CHostDrv() m_nRing = 0; } -//--------------------------------------------------------------------------- -// -/// デストラクタ final -// -//--------------------------------------------------------------------------- CHostDrv::~CHostDrv() { CHostPath* p; @@ -297,7 +236,7 @@ CHostDrv::~CHostDrv() m_nRing--; } - // 実体が存在しないことを確認 (念のため) + // Confirm that the entity does not exist (just in case) ASSERT(m_cRing.Next() == &m_cRing); ASSERT(m_cRing.Prev() == &m_cRing); ASSERT(m_nRing == 0); @@ -305,12 +244,11 @@ CHostDrv::~CHostDrv() //--------------------------------------------------------------------------- // -/// 初期化 (デバイス起動とロード) +// Initialization (device boot and load) // //--------------------------------------------------------------------------- void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag) { - ASSERT(this); ASSERT(szBase); ASSERT(strlen(szBase) < FILEPATH_MAX); ASSERT(m_bWriteProtect == FALSE); @@ -319,18 +257,18 @@ void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag) ASSERT(m_bVolumeCache == FALSE); ASSERT(m_szVolumeCache[0] == _T('\0')); - // 実体が存在しないことを確認 (念のため) + // Confirm that the entity does not exist (just in case) ASSERT(m_cRing.Next() == &m_cRing); ASSERT(m_cRing.Prev() == &m_cRing); ASSERT(m_nRing == 0); - // パラメータを受け取る + // Receive parameters if (nFlag & FSFLAG_WRITE_PROTECT) m_bWriteProtect = TRUE; strcpy(m_szBase, szBase); - // ベースパスの最後のパス区切りマークを削除する - /// @warning Unicode利用時は修正が必要 + // Remove the last path delimiter in the base path + // @warning needs to be modified when using Unicode TCHAR* pClear = NULL; TCHAR* p = m_szBase; for (;;) { @@ -342,7 +280,7 @@ void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag) } else { pClear = NULL; } - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // To be precise: 0x81~0x9F 0xE0~0xEF p++; if (*p == _T('\0')) break; @@ -352,60 +290,52 @@ void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag) if (pClear) *pClear = _T('\0'); - // 状態更新 + // Status update m_bEnable = TRUE; } //--------------------------------------------------------------------------- // -/// メディアチェック +// Media check // //--------------------------------------------------------------------------- BOOL CHostDrv::isMediaOffline() { - ASSERT(this); - - // オフライン状態チェック + // Offline status check return m_bEnable == FALSE; } //--------------------------------------------------------------------------- // -/// メディアバイトの取得 +// Get media bytes // //--------------------------------------------------------------------------- BYTE CHostDrv::GetMediaByte() const { - ASSERT(this); - return Human68k::MEDIA_REMOTE; } //--------------------------------------------------------------------------- // -/// ドライブ状態の取得 +// Get drive status // //--------------------------------------------------------------------------- DWORD CHostDrv::GetStatus() const { - ASSERT(this); - return 0x40 | (m_bEnable ? (m_bWriteProtect ? 0x08 : 0) | 0x02 : 0); } //--------------------------------------------------------------------------- // -/// メディア状態設定 +// Media status settings // //--------------------------------------------------------------------------- void CHostDrv::SetEnable(BOOL bEnable) { - ASSERT(this); - m_bEnable = bEnable; if (bEnable == FALSE) { - // キャッシュ消去 + // Clear cache m_capCache.sectors = 0; m_bVolumeCache = FALSE; m_szVolumeCache[0] = _T('\0'); @@ -414,14 +344,12 @@ void CHostDrv::SetEnable(BOOL bEnable) //--------------------------------------------------------------------------- // -/// メディア交換チェック +// Media change check // //--------------------------------------------------------------------------- BOOL CHostDrv::CheckMedia() { - ASSERT(this); - - // 状態更新 + // Status update Update(); if (m_bEnable == FALSE) CleanCache(); @@ -431,81 +359,71 @@ BOOL CHostDrv::CheckMedia() //--------------------------------------------------------------------------- // -/// メディア状態更新 +// Media status update // //--------------------------------------------------------------------------- void CHostDrv::Update() { - ASSERT(this); - - // メディア挿入とみなす + // Considered as media insertion BOOL bEnable = TRUE; - // メディア状態反映 + // Media status reflected SetEnable(bEnable); } //--------------------------------------------------------------------------- // -/// イジェクト +// Eject // //--------------------------------------------------------------------------- void CHostDrv::Eject() { - ASSERT(this); - - // メディア排出 + // Media discharge CleanCache(); SetEnable(FALSE); - // 状態更新 + // Status update Update(); } //--------------------------------------------------------------------------- // -/// ボリュームラベルの取得 +// Get volume label // //--------------------------------------------------------------------------- void CHostDrv::GetVolume(TCHAR* szLabel) { - ASSERT(this); ASSERT(szLabel); ASSERT(m_bEnable); - // ボリュームラベルの取得 -#ifdef RASCSI + // Get volume label strcpy(m_szVolumeCache, "RASDRV "); if (m_szBase[0]) { strcat(m_szVolumeCache, m_szBase); } else { strcat(m_szVolumeCache, "/"); } -#else - m_szVolumeCache[0] = _T('\0'); -#endif - // キャッシュ更新 + // Cache update m_bVolumeCache = TRUE; - // 内容を転送 + // Transfer content strcpy(szLabel, m_szVolumeCache); } //--------------------------------------------------------------------------- // -/// キャッシュからボリュームラベルを取得 +/// Get volume label from cache /// -/// キャッシュされているボリュームラベル情報を転送する。 -/// キャッシュ内容が有効ならTRUEを、無効ならFALSEを返す。 +/// Transfer the cached volume label information. +/// If the cache contents are valid return TRUE, if invalid return FALSE. // //--------------------------------------------------------------------------- BOOL CHostDrv::GetVolumeCache(TCHAR* szLabel) const { - ASSERT(this); ASSERT(szLabel); - // 内容を転送 + // Transfer contents strcpy(szLabel, m_szVolumeCache); return m_bVolumeCache; @@ -513,12 +431,11 @@ BOOL CHostDrv::GetVolumeCache(TCHAR* szLabel) const //--------------------------------------------------------------------------- // -/// 容量の取得 +/// Get Capacity // //--------------------------------------------------------------------------- DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity) { - ASSERT(this); ASSERT(pCapacity); ASSERT(m_bEnable); @@ -531,18 +448,18 @@ DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity) clusters = 0xFFFF; sectors = 64; - // パラメータ範囲想定 + // Estimated parameter range ASSERT(freearea <= 0xFFFF); ASSERT(clusters <= 0xFFFF); ASSERT(sectors <= 64); - // キャッシュ更新 + // Update cache m_capCache.freearea = (WORD)freearea; m_capCache.clusters = (WORD)clusters; m_capCache.sectors = (WORD)sectors; m_capCache.bytes = 512; - // 内容を転送 + // Transfer contents memcpy(pCapacity, &m_capCache, sizeof(m_capCache)); return nFree; @@ -550,18 +467,17 @@ DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity) //--------------------------------------------------------------------------- // -/// キャッシュから容量を取得 +/// Get capacity from the cache /// -/// キャッシュされている容量情報を転送する。 -/// キャッシュ内容が有効ならTRUEを、無効ならFALSEを返す。 +/// Transfer the capacity data stored in cache. +/// If the contents of the cache is valid return TRUE, is invalid return FALSE. // //--------------------------------------------------------------------------- BOOL CHostDrv::GetCapacityCache(Human68k::capacity_t* pCapacity) const { - ASSERT(this); ASSERT(pCapacity); - // 内容を転送 + // Transfer contents memcpy(pCapacity, &m_capCache, sizeof(m_capCache)); return m_capCache.sectors != 0; @@ -569,13 +485,11 @@ BOOL CHostDrv::GetCapacityCache(Human68k::capacity_t* pCapacity) const //--------------------------------------------------------------------------- // -/// 全てのキャッシュを更新する +/// Update all cache // //--------------------------------------------------------------------------- void CHostDrv::CleanCache() { - ASSERT(this); - Lock(); for (CHostPath* p = (CHostPath*)m_cRing.Next(); p != &m_cRing;) { p->Release(); @@ -586,12 +500,11 @@ void CHostDrv::CleanCache() //--------------------------------------------------------------------------- // -/// 指定されたパスのキャッシュを更新する +/// Update the cache for the specified path // //--------------------------------------------------------------------------- void CHostDrv::CleanCache(const BYTE* szHumanPath) { - ASSERT(this); ASSERT(szHumanPath); Lock(); @@ -605,12 +518,11 @@ void CHostDrv::CleanCache(const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// 指定されたパス以下のキャッシュを全て更新する +/// Update the cache below and including the specified path // //--------------------------------------------------------------------------- void CHostDrv::CleanCacheChild(const BYTE* szHumanPath) { - ASSERT(this); ASSERT(szHumanPath); Lock(); @@ -625,12 +537,11 @@ void CHostDrv::CleanCacheChild(const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// 指定されたパスのキャッシュを削除する +/// Delete the cache for the specified path // //--------------------------------------------------------------------------- void CHostDrv::DeleteCache(const BYTE* szHumanPath) { - ASSERT(this); ASSERT(szHumanPath); Lock(); @@ -645,19 +556,18 @@ void CHostDrv::DeleteCache(const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// 指定されたパスがキャッシュされているか検索する +/// Check if the specified path is cached /// -/// 所有するキャシュバッファの中から完全一致で検索し、見つかればその名称を返す。 -/// ファイル名を除外しておくこと。 -/// 必ず上位で排他制御を行なうこと。 +/// Check if whether it is a perfect match with the cache buffer, and return the name if found. +/// File names are excempted. +/// Make sure to lock from the top. // //--------------------------------------------------------------------------- CHostPath* CHostDrv::FindCache(const BYTE* szHuman) { - ASSERT(this); ASSERT(szHuman); - // 所持している全てのファイル名の中から完全一致するものを検索 + // Find something that matches perfectly with either of the stored file names for (CHostPath* p = (CHostPath*)m_cRing.Next(); p != &m_cRing;) { if (p->isSameHuman(szHuman)) return p; @@ -669,34 +579,33 @@ CHostPath* CHostDrv::FindCache(const BYTE* szHuman) //--------------------------------------------------------------------------- // -/// キャッシュ情報を元に、ホスト側の名称を獲得する +/// Get the host side name from cached data. /// -/// パスがキャッシュにあるか確認。なければエラー。 -/// 見つかったキャッシュの更新チェック。更新が必要ならエラー。 -/// 必ず上位で排他制御を行なうこと。 +/// Confirm if the path is cached. If not, throw an error. +/// Carry out an update check on found cache. If an update is needed, throw an error. +/// Make sure to lock from the top. // //--------------------------------------------------------------------------- CHostPath* CHostDrv::CopyCache(CHostFiles* pFiles) { - ASSERT(this); ASSERT(pFiles); ASSERT(strlen((const char*)pFiles->GetHumanPath()) < HUMAN68K_PATH_MAX); - // キャッシュ検索 + // Find in cache CHostPath* pPath = FindCache(pFiles->GetHumanPath()); if (pPath == NULL) { - return NULL; // エラー: キャッシュなし + return NULL; // Error: No cache } - // リング先頭へ移動 + // Move to the beginning of the ring pPath->Insert(&m_cRing); - // キャッシュ更新チェック + // Cache update check if (pPath->isRefresh()) { - return NULL; // エラー: キャッシュ更新が必要 + return NULL; // Error: Cache update is required } - // ホスト側のパス名を保存 + // Store the host side path pFiles->SetResult(pPath->GetHost()); return pPath; @@ -704,34 +613,33 @@ CHostPath* CHostDrv::CopyCache(CHostFiles* pFiles) //--------------------------------------------------------------------------- // -/// ホスト側の名称の構築に必要な情報をすべて取得する +/// Get all the data required for a host side name structure /// -/// ファイル名は省略可能。(普通は指定しない) -/// 必ず上位で排他制御を行なうこと。 -/// ベースパス末尾にパス区切り文字をつけないよう注意。 -/// ファイルアクセスが多発する可能性があるときは、VMスレッドの動作を開始させる。 +/// File names can be abbreviated. (Normally not selected) +/// Make sure to lock from the top. +/// Be careful not to append the path separator char to the end of the base path. +/// Initiate VM threads when there is a chance of multiple file accesses. /// -/// 使いかた: -/// CopyCache()してエラーの場合はMakeCache()する。必ず正しいホスト側のパスが取得できる。 +/// How to use: +/// When CopyCache() throws an error execute MakeCache(). It ensures you get a correct host side path. /// -/// ファイル名とパス名をすべて分離する。 -/// 上位ディレクトリから順に、キャッシュされているかどうか確認。 -/// キャッシュされていれば破棄チェック。破棄した場合未キャッシュ扱いとなる。 -/// キャッシュされていなければキャッシュを構築。 -/// 順番にすべてのディレクトリ・ファイル名に対して行ない終了。 -/// エラーが発生した場合はNULLとなる。 +/// Split all file names and path names. +/// Confirm that it's cached from the top level directory down. +/// If it's cached, do a destruction check. If it was destroyed, treat it as uncached. +/// If it isn't cached, build cache. +/// Exit after processing all directory and file names in order. +/// Make it NULL is an error is thrown. // //--------------------------------------------------------------------------- CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) { - ASSERT(this); ASSERT(pFiles); ASSERT(strlen((const char*)pFiles->GetHumanPath()) < HUMAN68K_PATH_MAX); ASSERT(m_szBase); ASSERT(strlen(m_szBase) < FILEPATH_MAX); - BYTE szHumanPath[HUMAN68K_PATH_MAX]; // ルートから順にパス名が入る + BYTE szHumanPath[HUMAN68K_PATH_MAX]; // Path names are entered in order from the route szHumanPath[0] = '\0'; size_t nHumanPath = 0; @@ -742,35 +650,35 @@ CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) CHostPath* pPath; const BYTE* p = pFiles->GetHumanPath(); for (;;) { - // パス区切りを追加 + // Add path separators if (nHumanPath + 1 >= HUMAN68K_PATH_MAX) - return NULL; // エラー: Human68kパスが長すぎる + return NULL; // Error: The Human68k path is too long szHumanPath[nHumanPath++] = '/'; szHumanPath[nHumanPath] = '\0'; if (nHostPath + 1 >= FILEPATH_MAX) - return NULL; // エラー: ホスト側のパスが長すぎる + return NULL; // Error: The host side path is too long szHostPath[nHostPath++] = _T('/'); szHostPath[nHostPath] = _T('\0'); - // ファイルいっこいれる - BYTE szHumanFilename[24]; // ファイル名部分 + // Insert one file + BYTE szHumanFilename[24]; // File name part p = SeparateCopyFilename(p, szHumanFilename); if (p == NULL) - return NULL; // エラー: ファイル名読み込み失敗 + return NULL; // Error: Failed to read file name size_t n = strlen((const char*)szHumanFilename); if (nHumanPath + n >= HUMAN68K_PATH_MAX) - return NULL; // エラー: Human68kパスが長すぎる + return NULL; // Error: The Human68k path is too long - // 該当パスがキャッシュされているか? + // Is the relevant path cached? pPath = FindCache(szHumanPath); if (pPath == NULL) { - // キャッシュ最大数チェック + // Check for max number of cache if (m_nRing >= XM6_HOST_DIRENTRY_CACHE_MAX) { - // 最も古いキャッシュを破棄して再利用 + // Destroy the oldest cache and reuse it pPath = (CHostPath*)m_cRing.Prev(); - pPath->Clean(); // 全ファイル解放 更新チェック用ハンドルも解放 + pPath->Clean(); // Release all files. Release update check handlers. } else { - // 新規登録 + // Register new pPath = new CHostPath; ASSERT(pPath); m_nRing++; @@ -778,43 +686,42 @@ CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) pPath->SetHuman(szHumanPath); pPath->SetHost(szHostPath); - // 状態更新 + // Update status pPath->Refresh(); } - // キャッシュ更新チェック + // Cache update check if (pPath->isRefresh()) { - // 更新 Update(); - // 状態更新 + // Update status pPath->Refresh(); } - // リング先頭へ + // Into the beginning of the ring pPath->Insert(&m_cRing); - // ファイル名がなければここで終了 + // Exit if there is not file name if (n == 0) break; - // 次のパスを検索 - // パスの途中ならディレクトリかどうか確認 + // Find the next path + // Confirm if directory from the middle of the path const CHostFilename* pFilename; if (*p != '\0') pFilename = pPath->FindFilename(szHumanFilename, Human68k::AT_DIRECTORY); else pFilename = pPath->FindFilename(szHumanFilename); if (pFilename == NULL) - return NULL; // エラー: 途中のパス名/ファイル名が見つからない + return NULL; // Error: Could not find path or file names in the middle - // パス名を連結 + // Link path name strcpy((char*)szHumanPath + nHumanPath, (const char*)szHumanFilename); nHumanPath += n; n = strlen(pFilename->GetHost()); if (nHostPath + n >= FILEPATH_MAX) - return NULL; // エラー: ホスト側のパスが長すぎる + return NULL; // Error: Host side path is too long strcpy(szHostPath + nHostPath, pFilename->GetHost()); nHostPath += n; @@ -823,7 +730,7 @@ CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) break; } - // ホスト側のパス名を保存 + // Store the host side path name pFiles->SetResult(szHostPath); return pPath; @@ -831,53 +738,50 @@ CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) //--------------------------------------------------------------------------- // -/// ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) +/// Find host side name (path name + file name (can be abbeviated) + attribute) /// -/// あらかじめ全てのHuman68k用パラメータを設定しておくこと。 +/// Set all Human68k parameters once more. // //--------------------------------------------------------------------------- BOOL CHostDrv::Find(CHostFiles* pFiles) { - ASSERT(this); ASSERT(pFiles); - // 排他制御開始 Lock(); - // パス名獲得およびキャッシュ構築 + // Get path name and build cache CHostPath* pPath = CopyCache(pFiles); if (pPath == NULL) { pPath = MakeCache(pFiles); if (pPath == NULL) { Unlock(); CleanCache(); - return FALSE; // エラー: キャッシュ構築失敗 + return FALSE; // Error: Failed to build cache } } - // ホスト側のパス名を保存 + // Store host side path pFiles->SetResult(pPath->GetHost()); - // パス名のみなら終了 + // Exit if only path name if (pFiles->isPathOnly()) { Unlock(); - return TRUE; // 正常終了: パス名のみ + return TRUE; // Normal exit: only path name } - // ファイル名検索 + // Find file name const CHostFilename* pFilename = pFiles->Find(pPath); if (pFilename == NULL) { Unlock(); - return FALSE; // エラー: ファイル名が獲得できません + return FALSE; // Error: Could not get file name } - // Human68k側の検索結果保存 + // Store the Human68k side search results pFiles->SetEntry(pFilename); - // ホスト側のフルパス名保存 + // Store the host side full path name pFiles->AddResult(pFilename->GetHost()); - // 排他制御終了 Unlock(); return TRUE; @@ -885,27 +789,24 @@ BOOL CHostDrv::Find(CHostFiles* pFiles) //=========================================================================== // -// ディレクトリエントリ ファイル名 +// Directory entry: File name // //=========================================================================== -//--------------------------------------------------------------------------- -// -/// デフォルトコンストラクタ -// -//--------------------------------------------------------------------------- CHostFilename::CHostFilename() { + m_bCorrect = FALSE; + m_pszHumanExt = FALSE; + m_pszHumanLast = FALSE; } //--------------------------------------------------------------------------- // -/// ホスト側の名称を設定 +/// Set host side name // //--------------------------------------------------------------------------- void CHostFilename::SetHost(const TCHAR* szHost) { - ASSERT(this); ASSERT(szHost); ASSERT(strlen(szHost) < FILEPATH_MAX); @@ -914,7 +815,7 @@ void CHostFilename::SetHost(const TCHAR* szHost) //--------------------------------------------------------------------------- // -/// Human68k側のファイル名要素をコピー +/// Copy the Human68k file name elements // //--------------------------------------------------------------------------- BYTE* CHostFilename::CopyName(BYTE* pWrite, const BYTE* pFirst, const BYTE* pLast) // static @@ -932,27 +833,26 @@ BYTE* CHostFilename::CopyName(BYTE* pWrite, const BYTE* pFirst, const BYTE* pLas //--------------------------------------------------------------------------- // -/// Human68k側の名称を変換 +/// Convert the Human68k side name /// -/// あらかじめSetHost()を実行しておくこと。 -/// 18+3の命名規則に従った名前変換を行なう。 -/// ファイル名先頭および末尾の空白は、Human68kで扱えないため自動的に削除される。 -/// ディレクトリエントリの名前部分を、ファイル名変換時の拡張子の位置情報を使って生成する。 -/// その後、ファイル名の異常判定を行なう。(スペース8文字だけのファイル名など) -/// ファイル名の重複判定は行なわないので注意。これらの判定は上位クラスで行なう。 -/// TwentyOne version 1.36c modified +14 patchlevel9以降の拡張子規則に対応させる。 +/// Once more, execute SetHost(). +/// Carry out name conversion to the 18+3 standard. +/// Automatically delete spaces in the beginning and end of the names, since Human68k can't handle them. +/// The directory entry name segment is created at the time of conversion using knowledge of the location of the file name extension. +/// Afterwards, a file name validity check is performed. (Ex. file names consisting of 8 spaces only.) +/// No file name duplication check is performed so be careful. Such validation is carried out in classes higher up. +/// Adhers to the naming standards of: TwentyOne version 1.36c modified +14 patchlevel9 or later // //--------------------------------------------------------------------------- void CHostFilename::ConvertHuman(int nCount) { char szHost[FILEPATH_MAX]; - ASSERT(this); - // 特殊ディレクトリ名の場合は変換しない + // Don't do conversion for special directory names if (m_szHost[0] == _T('.') && (m_szHost[1] == _T('\0') || (m_szHost[1] == _T('.') && m_szHost[2] == _T('\0')))) { - strcpy((char*)m_szHuman, m_szHost); /// @warning Unicode時要修正 → 済 + strcpy((char*)m_szHuman, m_szHost); m_bCorrect = TRUE; m_pszHumanLast = m_szHuman + strlen((const char*)m_szHuman); @@ -960,17 +860,17 @@ void CHostFilename::ConvertHuman(int nCount) return; } - size_t nMax = 18; // ベース部分(ベース名と拡張子名)のバイト数 + size_t nMax = 18; // Number of bytes for the base segment (base name and extension) DWORD nOption = CFileSys::GetFileOption(); if (nOption & WINDRV_OPT_CONVERT_LENGTH) nMax = 8; - // ベース名部分の補正準備 + // Preparations to adjust the base name segment BYTE szNumber[8]; BYTE* pNumber = NULL; if (nCount >= 0) { pNumber = &szNumber[8]; - for (DWORD i = 0; i < 5; i++) { // 最大5+1桁まで (ベース名先頭2バイトは必ず残す) + for (DWORD i = 0; i < 5; i++) { // Max 5+1 digits (always leave the first 2 bytes of the base name) int n = nCount % 36; nMax--; pNumber--; @@ -987,8 +887,7 @@ void CHostFilename::ConvertHuman(int nCount) *pNumber = c; } - // 文字変換 - /// @warning Unicode未対応。いずれUnicodeの世界に飮まれた時はここで変換を行なう → 済 + // Char conversion BYTE szHuman[FILEPATH_MAX]; const BYTE* pFirst = szHuman; const BYTE* pLast; @@ -1009,7 +908,7 @@ void CHostFilename::ConvertHuman(int nCount) if (nOption & WINDRV_OPT_CONVERT_SPACE) c = '_'; else if (pWrite == szHuman) - continue; // 先頭の空白は無視 + continue; // Ignore spaces in the beginning break; case '=': case '+': @@ -1032,7 +931,7 @@ void CHostFilename::ConvertHuman(int nCount) c = '_'; break; case '.': - if (pRead - 1 == pPeriod) { // Human68k拡張子は例外とする + if (pRead - 1 == pPeriod) { // Make exception for Human68k extensions pExt = pWrite; break; } @@ -1057,76 +956,76 @@ void CHostFilename::ConvertHuman(int nCount) pLast = pWrite - 1; } - // 拡張子補正 + // Adjust extensions if (pExt) { - // 末尾の空白を削除する + // Delete spaces at the end while (pExt < pLast - 1 && *(pLast - 1) == ' ') { pLast--; BYTE* p = (BYTE*)pLast; *p = '\0'; } - // 変換後に実体がなくなった場合は削除 + // Delete if the file name disappeared after conversion if (pExt + 1 >= pLast) { pLast = pExt; BYTE* p = (BYTE*)pLast; - *p = '\0'; // 念のため + *p = '\0'; // Just in case } } else { pExt = pLast; } - // 登場人物紹介 + // Introducing the cast of characters // - // pFirst: 俺はリーダー。ファイル名先頭 - // pCut: 通称フェイス。最初のピリオドの出現位置 その後ベース名終端位置となる - // pSecond: よぉおまちどう。俺様こそマードック。拡張子名の開始位置。だから何。 - // pExt: B・A・バラカス。Human68k拡張子の天才だ。でも、3文字より長い名前は勘弁な。 - // 最後のピリオドの出現位置 該当しなければpLastと同じ値 + // pFirst: I'm the glorious leader. The start of the file name. + // pCut: A.k.a. Phase. Location of the initial period. Afterwards becomes the end of the base name. + // pSecond: Hello there! I'm the incredible Murdock. The start of the file name extension. What's it to you? + // pExt: B.A. Baracus. The Human68k extension genius. But don't you dare giving me more than 3 chars, fool. + // The location of the final period. If not applicable, gets the same value as pLast. // // ↓pFirst ↓pStop ↓pSecond ← ↓pExt // T h i s _ i s _ a . V e r y . L o n g . F i l e n a m e . t x t \0 - // ↑pCut ← ↑pCut初期位置 ↑pLast + // ↑pCut ← ↑pCut initial location ↑pLast // - // 上記の場合、変換後は This.Long.Filename.txt となる + // The above example becomes "This.Long.Filename.txt" after conversion - // 1文字目判定 + // Evaluate first char const BYTE* pCut = pFirst; - const BYTE* pStop = pExt - nMax; // 拡張子名は最大17バイトとする(ベース名を残す) + const BYTE* pStop = pExt - nMax; // Allow for up to 17 bytes for extension (leave base name) if (pFirst < pExt) { - pCut++; // 必ず1バイトはベース名を使う + pCut++; // 1 byte always uses the base name BYTE c = *pFirst; - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF - pCut++; // ベース名 最小2バイト - pStop++; // 拡張子名 最大16バイト + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // Specifically 0x81~0x9F 0xE0~0xEF + pCut++; // Base name. At least 2 bytes. + pStop++; // File extension. Max 16 bytes. } } if (pStop < pFirst) pStop = pFirst; - // ベース名判定 - pCut = (BYTE*)strchr((const char*)pCut, '.'); // SJIS2バイト目は必ず0x40以上なので問題ない + // Evaluate base name + pCut = (BYTE*)strchr((const char*)pCut, '.'); // The 2nd byte of Shift-JIS is always 0x40 or higher, so this is ok if (pCut == NULL) pCut = pLast; if ((size_t)(pCut - pFirst) > nMax) - pCut = pFirst + nMax; // 後ほどSJIS2バイト判定/補正を行なう ここで判定してはいけない + pCut = pFirst + nMax; // Execute Shift-JIS 2 byte evaluation/adjustment later. Not allowed to do it here. - // 拡張子名判定 + // Evaluate extension const BYTE* pSecond = pExt; const BYTE* p; for (p = pExt - 1; pStop < p; p--) { if (*p == '.') - pSecond = p; // SJIS2バイト目は必ず0x40以上なので問題ない + pSecond = p; // The 2nd byte of Shift-JIS is always 0x40 or higher, so this is ok } - // ベース名を短縮 - size_t nExt = pExt - pSecond; // 拡張子名部分の長さ + // Shorten base name + size_t nExt = pExt - pSecond; // Length of extension segment if ((size_t)(pCut - pFirst) + nExt > nMax) pCut = pFirst + nMax - nExt; - // 2バイト文字の途中ならさらに短縮 + // If in the middle of a 2 byte char, shorten even further for (p = pFirst; p < pCut; p++) { BYTE c = *p; - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // Specifically 0x81~0x9F 0xE0~0xEF p++; if (p >= pCut) { pCut--; @@ -1135,31 +1034,31 @@ void CHostFilename::ConvertHuman(int nCount) } } - // 名前の結合 + // Joining the name BYTE* pWrite = m_szHuman; - pWrite = CopyName(pWrite, pFirst, pCut); // ベース名を転送 + pWrite = CopyName(pWrite, pFirst, pCut); // Transfer the base name if (pNumber) - pWrite = CopyName(pWrite, pNumber, &szNumber[8]); // 補正文字を転送 - pWrite = CopyName(pWrite, pSecond, pExt); // 拡張子名を転送 - m_pszHumanExt = pWrite; // 拡張子位置保存 - pWrite = CopyName(pWrite, pExt, pLast); // Human68k拡張子を転送 - m_pszHumanLast = pWrite; // 終端位置保存 + pWrite = CopyName(pWrite, pNumber, &szNumber[8]); // Transfer the adjustment char + pWrite = CopyName(pWrite, pSecond, pExt); // Transfer the extension name + m_pszHumanExt = pWrite; // Store the extention position + pWrite = CopyName(pWrite, pExt, pLast); // Transfer the Human68k extension + m_pszHumanLast = pWrite; // Store the end position *pWrite = '\0'; - // 変換結果の確認 + // Confirm the conversion results m_bCorrect = TRUE; - // ファイル名本体が存在しなければ不合格 + // Fail if the base file name does not exist if (m_pszHumanExt <= m_szHuman) m_bCorrect = FALSE; - // ファイル名本体が1文字以上でかつ空白で終了していれば不合格 - // ファイル名本体が8文字以上の場合、理論上は空白での終了が表現可 - // 能だが、Human68kでは正しく扱えないため、これも不合格とする + // Fail if the base file name is more than 1 char and ends with a space + // While it is theoretically valid to have a base file name exceed 8 chars, + // Human68k is unable to handle it, so failing this case too. else if (m_pszHumanExt[-1] == ' ') m_bCorrect = FALSE; - // 変換結果が特殊ディレクトリ名と同じなら不合格 + // Fail if the conversion result is the same as a special directory name if (m_szHuman[0] == '.' && (m_szHuman[1] == '\0' || (m_szHuman[1] == '.' && m_szHuman[2] == '\0'))) m_bCorrect = FALSE; @@ -1167,14 +1066,13 @@ void CHostFilename::ConvertHuman(int nCount) //--------------------------------------------------------------------------- // -/// Human68k側の名称を複製 +/// Human68k side name duplication /// -/// ファイル名部分の情報を複製し、ConvertHuman()相当の初期化動作を行なう。 +/// Duplicates the file name segment data, then executes the correspoding initialization with ConvertHuman(). // //--------------------------------------------------------------------------- void CHostFilename::CopyHuman(const BYTE* szHuman) { - ASSERT(this); ASSERT(szHuman); ASSERT(strlen((const char*)szHuman) < 23); @@ -1186,16 +1084,15 @@ void CHostFilename::CopyHuman(const BYTE* szHuman) //--------------------------------------------------------------------------- // -/// Human68kディレクトリエントリを設定 +/// Set Human68k directory entry /// -/// ConvertHuman()で設定済みのファイル名をディレクトリエントリに反映する。 +/// Apply the set file name to the directory entry with ConvertHuman(). // //--------------------------------------------------------------------------- void CHostFilename::SetEntryName() { - ASSERT(this); - // ファイル名設定 + // Set file name BYTE* p = m_szHuman; size_t i; for (i = 0; i < 8; i++) { @@ -1224,24 +1121,22 @@ void CHostFilename::SetEntryName() //--------------------------------------------------------------------------- // -/// Human68k側の名称が加工されたか調査 +/// Investigate if the Human68k side name has been processed // //--------------------------------------------------------------------------- BOOL CHostFilename::isReduce() const { - ASSERT(this); - return strcmp((LPTSTR)m_szHost, (const char*)m_szHuman) != 0; /// @warning Unicode時要修正 → 済 + return strcmp((char *)m_szHost, (const char*)m_szHuman) != 0; } //--------------------------------------------------------------------------- // -/// Human68kディレクトリエントリの属性判定 +/// Evaluate Human68k directory entry attribute // //--------------------------------------------------------------------------- BOOL CHostFilename::CheckAttribute(DWORD nHumanAttribute) const { - ASSERT(this); BYTE nAttribute = m_dirHuman.attr; if ((nAttribute & (Human68k::AT_ARCHIVE | Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == 0) @@ -1252,26 +1147,26 @@ BOOL CHostFilename::CheckAttribute(DWORD nHumanAttribute) const //--------------------------------------------------------------------------- // -/// Human68kファイル名から拡張子を分離 +/// Split the extension from Human68k file name // //--------------------------------------------------------------------------- const BYTE* CHostFilename::SeparateExt(const BYTE* szHuman) // static { - // ファイル名の長さを獲得 + // Obtain the file name length size_t nLength = strlen((const char*)szHuman); const BYTE* pFirst = szHuman; const BYTE* pLast = pFirst + nLength; - // Human68k拡張子の位置を確認 - const BYTE* pExt = (BYTE*)strrchr((const char*)pFirst, '.'); // SJIS2バイト目は必ず0x40以上なので問題ない + // Confirm the position of the Human68k extension + const BYTE* pExt = (BYTE*)strrchr((const char*)pFirst, '.'); // The 2nd byte of Shift-JIS is always 0x40 or higher, so this is ok if (pExt == NULL) pExt = pLast; - // ファイル名が20~22文字かつ19文字目が'.'かつ'.'で終了というパターンを特別扱いする + // Special handling of the pattern where the file name is 20~22 chars, and the 19th char is '.' or ends with '.' if (20 <= nLength && nLength <= 22 && pFirst[18] == '.' && pFirst[nLength - 1] == '.') pExt = pFirst + 18; - // 拡張子の文字数を計算 (-1:なし 0:ピリオドだけ 1~3:Human68k拡張子 4以上:拡張子名) + // Calculate the number of chars in the extension (-1:None 0:Only period 1~3:Human68k extension 4 or above:extension name) size_t nExt = pLast - pExt - 1; - // '.' が文字列先頭以外に存在して、かつ1~3文字の場合のみ拡張子とみなす + // Consider it an extension only when '.' is anywhere except the beginning of the string, and between 1~3 chars long if (pExt == pFirst || nExt < 1 || nExt > 3) pExt = pLast; @@ -1280,32 +1175,24 @@ const BYTE* CHostFilename::SeparateExt(const BYTE* szHuman) // static //=========================================================================== // -// ディレクトリエントリ パス名 +// Directory entry: path name // //=========================================================================== -DWORD CHostPath::g_nId; ///< 識別ID生成用カウンタ +DWORD CHostPath::g_nId; ///< Identifier creation counter -//--------------------------------------------------------------------------- -// -/// デフォルトコンストラクタ -// -//--------------------------------------------------------------------------- CHostPath::CHostPath() { m_bRefresh = TRUE; + m_nId = 0; + m_tBackup = FALSE; #ifdef _DEBUG - // 必ず値が更新されるので初期化不要 (デバッグ時の初期動作確認用) + // Initialization is not required because this value always gets updated (used for debugging or initialization operation) m_nId = 0; #endif // _DEBUG } -//--------------------------------------------------------------------------- -// -/// デストラクタ final -// -//--------------------------------------------------------------------------- CHostPath::~CHostPath() { Clean(); @@ -1313,11 +1200,11 @@ CHostPath::~CHostPath() //--------------------------------------------------------------------------- // -/// ファイル名領域確保 +/// File name memory allocation /// -/// ほとんどのケースでは、ホスト側ファイル名の長さはバッファ最大長に -/// 比べて非常に短い。さらにファイル名は大量に生成される可能性がある。 -/// そのため文字数に応じた可変長で確保する。 +/// In most cases, the length of the host side file name is way shorter +/// than the size of the buffer. In addition, file names may be created in huge volumes. +/// Therefore, allocate variable lengths that correspond to the number of chars. // //--------------------------------------------------------------------------- CHostPath::ring_t* CHostPath::Alloc(size_t nLength) // static @@ -1328,14 +1215,14 @@ CHostPath::ring_t* CHostPath::Alloc(size_t nLength) // static ring_t* p = (ring_t*)malloc(n); ASSERT(p); - p->r.Init(); // 榛名は大丈夫です! + p->r.Init(); // This is nothing to worry about! return p; } //--------------------------------------------------------------------------- // -// ファイル名領域解放 +// Release file name allocations // //--------------------------------------------------------------------------- void CHostPath::Free(ring_t* pRing) // static @@ -1348,16 +1235,15 @@ void CHostPath::Free(ring_t* pRing) // static //--------------------------------------------------------------------------- // -/// 再利用のための初期化 +/// Initialize for reuse // //--------------------------------------------------------------------------- void CHostPath::Clean() { - ASSERT(this); Release(); - // 全ファイル名を解放 + // Release all file names ring_t* p; while ((p = (ring_t*)m_cRing.Next()) != (ring_t*)&m_cRing) { Free(p); @@ -1366,12 +1252,11 @@ void CHostPath::Clean() //--------------------------------------------------------------------------- // -/// Human68k側の名称を直接指定する +/// Specify Human68k side names directly // //--------------------------------------------------------------------------- void CHostPath::SetHuman(const BYTE* szHuman) { - ASSERT(this); ASSERT(szHuman); ASSERT(strlen((const char*)szHuman) < HUMAN68K_PATH_MAX); @@ -1380,12 +1265,11 @@ void CHostPath::SetHuman(const BYTE* szHuman) //--------------------------------------------------------------------------- // -/// ホスト側の名称を直接指定する +/// Specify host side names directly // //--------------------------------------------------------------------------- void CHostPath::SetHost(const TCHAR* szHost) { - ASSERT(this); ASSERT(szHost); ASSERT(strlen(szHost) < FILEPATH_MAX); @@ -1394,7 +1278,7 @@ void CHostPath::SetHost(const TCHAR* szHost) //--------------------------------------------------------------------------- // -/// 文字列比較 (ワイルドカード対応) +/// Compare arrays (supports wildcards) // //--------------------------------------------------------------------------- int CHostPath::Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFirst, const BYTE* pBufLast) @@ -1404,59 +1288,59 @@ int CHostPath::Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFi ASSERT(pBufFirst); ASSERT(pBufLast); - // 文字比較 + // Compare chars BOOL bSkip0 = FALSE; BOOL bSkip1 = FALSE; for (const BYTE* p = pFirst; p < pLast; p++) { - // 1文字読み込み + // Read 1 char BYTE c = *p; BYTE d = '\0'; if (pBufFirst < pBufLast) d = *pBufFirst++; - // 比較のための文字補正 + // Ajust char for comparison if (bSkip0 == FALSE) { - if (bSkip1 == FALSE) { // cもdも1バイト目 - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + if (bSkip1 == FALSE) { // First byte for both c and d + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // Specifically 0x81~0x9F 0xE0~0xEF bSkip0 = TRUE; } - if ((0x80 <= d && d <= 0x9F) || 0xE0 <= d) { // 厳密には 0x81~0x9F 0xE0~0xEF + if ((0x80 <= d && d <= 0x9F) || 0xE0 <= d) { // Specifically 0x81~0x9F 0xE0~0xEF bSkip1 = TRUE; } if (c == d) - continue; // 高確率で判定完了する + continue; // Finishes the evaluation here with high probability if ((CFileSys::GetFileOption() & WINDRV_OPT_ALPHABET) == 0) { if ('A' <= c && c <= 'Z') - c += 'a' - 'A'; // 小文字化 + c += 'a' - 'A'; // To lower case if ('A' <= d && d <= 'Z') - d += 'a' - 'A'; // 小文字化 + d += 'a' - 'A'; // To lower case } - // バックスラッシュをスラッシュに統一して比較する + // Unify slashes and backslashes for comparison if (c == '\\') { c = '/'; } if (d == '\\') { d = '/'; } - } else { // cだけが1バイト目 - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + } else { // Only c is first byte + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // Specifically 0x81~0x9F 0xE0~0xEF bSkip0 = TRUE; } bSkip1 = FALSE; } } else { - if (bSkip1 == FALSE) { // dだけが1バイト目 + if (bSkip1 == FALSE) { // Only d is first byte bSkip0 = FALSE; - if ((0x80 <= d && d <= 0x9F) || 0xE0 <= d) { // 厳密には 0x81~0x9F 0xE0~0xEF + if ((0x80 <= d && d <= 0x9F) || 0xE0 <= d) { // Specifically 0x81~0x9F 0xE0~0xEF bSkip1 = TRUE; } - } else { // cもdも2バイト目 + } else { // Second byte for both c and d bSkip0 = FALSE; bSkip1 = FALSE; } } - // 比較 + // Compare if (c == d) continue; if (c == '?') @@ -1471,81 +1355,72 @@ int CHostPath::Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFi //--------------------------------------------------------------------------- // -/// Human68k側の名称を比較する +/// Compare Human68k side name // //--------------------------------------------------------------------------- BOOL CHostPath::isSameHuman(const BYTE* szHuman) const { - ASSERT(this); ASSERT(szHuman); - // 文字数計算 + // Calulate number of chars size_t nLength = strlen((const char*)m_szHuman); size_t n = strlen((const char*)szHuman); - // 文字数チェック + // Check number of chars if (nLength != n) return FALSE; - // Human68kパス名の比較 + // Compare Human68k path name return Compare(m_szHuman, m_szHuman + nLength, szHuman, szHuman + n) == 0; } -//--------------------------------------------------------------------------- -// -/// Human68k側の名称を比較する -// -//--------------------------------------------------------------------------- BOOL CHostPath::isSameChild(const BYTE* szHuman) const { - ASSERT(this); ASSERT(szHuman); - // 文字数計算 + // Calulate number of chars size_t nLength = strlen((const char*)m_szHuman); size_t n = strlen((const char*)szHuman); - // 文字数チェック + // Check number of chars if (nLength < n) return FALSE; - // Human68kパス名の比較 + // Compare Human68k path name return Compare(m_szHuman, m_szHuman + n, szHuman, szHuman + n) == 0; } //--------------------------------------------------------------------------- // -/// ファイル名を検索 +/// Find file name /// -/// 所有するキャシュバッファの中から検索し、見つかればその名称を返す。 -/// パス名を除外しておくこと。 -/// 必ず上位で排他制御を行なうこと。 +/// Check if whether it is a perfect match with the cache buffer, and return the name if found. +/// Path names are excempted. +/// Make sure to lock from the top. // //--------------------------------------------------------------------------- const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, DWORD nHumanAttribute) const { - ASSERT(this); ASSERT(szHuman); - // 文字数計算 + // Calulate number of chars const BYTE* pFirst = szHuman; size_t nLength = strlen((const char*)pFirst); const BYTE* pLast = pFirst + nLength; - // 所持している全てのファイル名の中から完全一致するものを検索 + // Find something that matches perfectly with either of the stored file names const ring_t* p = (ring_t*)m_cRing.Next(); for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { - // 属性チェック if (p->f.CheckAttribute(nHumanAttribute) == 0) continue; - // 文字数計算 + // Calulate number of chars const BYTE* pBufFirst = p->f.GetHuman(); const BYTE* pBufLast = p->f.GetHumanLast(); size_t nBufLength = pBufLast - pBufFirst; - // 文字数チェック + // Check number of chars if (nLength != nBufLength) continue; - // ファイル名チェック + // File name check if (Compare(pFirst, pLast, pBufFirst, pBufLast) == 0) return &p->f; } @@ -1555,36 +1430,35 @@ const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, DWORD nHumanAt //--------------------------------------------------------------------------- // -/// ファイル名を検索 (ワイルドカード対応) +/// Find file name (with wildcard support) /// -/// 所有するバッファの中から検索し、見つかればその名称を返す。 -/// パス名を除外しておくこと。 -/// 必ず上位で排他制御を行なうこと。 +/// Check if whether it is a perfect match with the cache buffer, and return the name if found. +/// Path names are excempted. +/// Make sure to lock from the top. // //--------------------------------------------------------------------------- const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const { - ASSERT(this); ASSERT(szHuman); ASSERT(pFind); - // 検索ファイル名を本体とHuman68k拡張子に分ける + // Split the base file name and Human68k file extension const BYTE* pFirst = szHuman; const BYTE* pLast = pFirst + strlen((const char*)pFirst); const BYTE* pExt = CHostFilename::SeparateExt(pFirst); - // 開始地点へ移動 + // Move to the start position const ring_t* p = (ring_t*)m_cRing.Next(); if (pFind->count > 0) { if (pFind->id == m_nId) { - // ディレクトリエントリが同一なら、前回の位置から即継続 + // If the same directory entry, continue right away from the previous position p = pFind->pos; } else { - // 開始地点をディレクトリエントリ内容から検索する + // Find the start position in the directory entry contents DWORD n = 0; for (;; p = (ring_t*)p->r.Next()) { if (p == (ring_t*)&m_cRing) { - // 同一エントリが見つからなかった場合、回数から推定 (念のため) + // Extrapolate from the count when the same entry isn't found (just in case) p = (ring_t*)m_cRing.Next(); n = 0; for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { @@ -1595,7 +1469,7 @@ const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD break; } if (p->f.isSameEntry(&pFind->entry)) { - // 同一エントリを発見 + // Same entry is found pFind->count = n; break; } @@ -1604,28 +1478,27 @@ const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD } } - // ファイル検索 + // Find files for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { pFind->count++; - // 属性チェック if (p->f.CheckAttribute(nHumanAttribute) == 0) continue; - // ファイル名を本体とHuman68k拡張子に分ける + // Split the base file name and Human68k file extension const BYTE* pBufFirst = p->f.GetHuman(); const BYTE* pBufLast = p->f.GetHumanLast(); const BYTE* pBufExt = p->f.GetHumanExt(); - // 本体比較 + // Compare base file name if (Compare(pFirst, pExt, pBufFirst, pBufExt)) continue; - // Human68k拡張子比較 - // 拡張子.???の場合は、Human68k拡張子のピリオドなしにもマッチさせる + // Compare Human68k extension + // In the case of a '.???' extension, match the Human68k extension without period. if (strcmp((const char*)pExt, ".???") == 0 || Compare(pExt, pLast, pBufExt, pBufLast) == 0) { - // 次の候補のディレクトリエントリ内容を記録 + // Store the contents of the next candidate's directory entry const ring_t* pNext = (ring_t*)p->r.Next(); pFind->id = m_nId; pFind->pos = pNext; @@ -1645,154 +1518,18 @@ const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD //--------------------------------------------------------------------------- // -/// ファイル変更が行なわれたか確認 +/// Confirm that the file update has been carried out // //--------------------------------------------------------------------------- BOOL CHostPath::isRefresh() { - ASSERT(this); return m_bRefresh; } -#ifdef BAREMETAL //--------------------------------------------------------------------------- // -/// scandirエミュレーション -// -//--------------------------------------------------------------------------- -struct dirent { - char d_name[_MAX_FNAME]; -}; - -int scandir(const char *dirname, -dirent ***ret_namelist, - int(*select)(const dirent *), - int(*compar)(const dirent **, const dirent **)) -{ - FRESULT fr; - DIR dir; - FILINFO fno; - char dirpath[256]; - int len; - dirent went; - int used; - int allocated; - dirent **namelist = NULL; - int i; - dirent *ent; - - // NULLチェック - strcpy(dirpath, dirname); - if (dirpath[0] == '\0') { - return -1; - } - - // '/'はOKだがそれ以外で最後が'/'だとディレクトリと認識されない) - if (dirpath[0] != '/' || dirpath[1] != '\0') { - len = strlen(dirpath); - if (dirpath[len - 1] == '/') { - dirpath[len - 1] = '\0'; - } - } - - // ディレクトリオープン - fr = f_opendir(&dir, dirpath); - if (fr != FR_OK) { - return -1; - } - - // リストを初期値で確保(とりあえず32) - used = 0; - allocated = 32; - namelist = (dirent **)malloc(allocated * sizeof(dirent *)); - if (!namelist) { - goto error; - } - - // 配下のファイルまたはディレクトリを処理 - i = 0; - while (TRUE) { - if (i == 0) { - // "."をFILINFOに見せかけて追加 - strcpy(fno.fname, "."); - i++; - } else if (i == 1) { - // ".."をFILINFOに見せかけて追加 - strcpy(fno.fname, ".."); - i++; - } else if (f_readdir(&dir, &fno) != FR_OK) { - break; - } - - // このケースがあるか不明 - if (fno.fname[0] == 0) { - break; - } - - // direntに見せかける - strcpy(went.d_name, fno.fname); - - // 対象外のフィルタ処理 - if (select != NULL && !select(&went)) { - continue; - } - - // ファイル名の長さに調整したdirentの領域を確保 - len = offsetof(dirent, d_name) + strlen(fno.fname) + 1; - if ((ent = (dirent *)malloc(len)) == NULL) { - goto error; - } - - // ワーク用direntから返却用にコピー - memcpy(ent, &went, len); - - // 使用量が越えそうならリストを再確保 - if (used >= allocated) { - allocated *= 2; - namelist = (dirent **)realloc(namelist, allocated * sizeof(dirent *)); - if (!namelist) { - goto error; - } - } - - // リストに追加 - namelist[used++] = ent; - } - - // ディレクトリクローズ - f_closedir(&dir); - - // ソート処理 - if (compar) { - qsort( - namelist, used, sizeof(dirent *), - (int(*)(const void *, const void *)) compar); - } - - // リストとエントリ数を返却 - *ret_namelist = namelist; - return used; - -error: - // ディレクトリクローズ - f_closedir(&dir); - - // 途中まで確保したバッファをクローズ - if (namelist) { - while (used > 0) { - free(namelist[used - 1]); - used--; - } - free(namelist); - } - return -1; -} -#endif // BAREMETAL - -//--------------------------------------------------------------------------- -// -/// ASCIIソート関数 +/// ASCII sort function // //--------------------------------------------------------------------------- int AsciiSort(const dirent **a, const dirent **b) @@ -1802,32 +1539,31 @@ int AsciiSort(const dirent **a, const dirent **b) //--------------------------------------------------------------------------- // -/// ファイル再構成 +/// Reconstruct the file /// -/// ここで初めて、ホスト側のファイルシステムの観測が行なわれる。 -/// 必ず上位で排他制御を行なうこと。 +/// Here we carry out the first host side file system observation. +/// Always lock from the top. // //--------------------------------------------------------------------------- void CHostPath::Refresh() { - ASSERT(this); ASSERT(strlen(m_szHost) + 22 < FILEPATH_MAX); - // タイムスタンプ保存 + // Store time stamp Backup(); TCHAR szPath[FILEPATH_MAX]; strcpy(szPath, m_szHost); - // 更新フラグ変更 + // Update refresh flag m_bRefresh = FALSE; - // 以前のキャッシュ内容を保存 + // Store previous cache contents CRing cRingBackup; m_cRing.InsertRing(&cRingBackup); - // ファイル名登録 - /// @todo ファイル重複処理をホスト側APIを経由せずに全て自前で処理する。 + // Register file name + /// TODO: Process file duplication by ourselves rather than using the host API. BOOL bUpdate = FALSE; struct dirent **pd = NULL; int nument = 0; @@ -1843,7 +1579,7 @@ void CHostPath::Refresh() maxent = nument; } - // 最上位ディレクトリならカレントとパレントを対象外とする + // When at the top level directory, exclude current and parent struct dirent* pe = pd[i]; if (m_szHuman[0] == '/' && m_szHuman[1] == 0) { if (strcmp(pe->d_name, ".") == 0 || strcmp(pe->d_name, "..") == 0) { @@ -1851,73 +1587,67 @@ void CHostPath::Refresh() } } - // ファイル名を獲得 + // Get file name strcpy(szFilename, U2S(pe->d_name)); - // ファイル名領域確保 + // Allocate file name memory ring_t* pRing = Alloc(strlen(szFilename)); CHostFilename* pFilename = &pRing->f; pFilename->SetHost(szFilename); - // 以前のキャッシュ内容に該当するファイル名があればそのHuman68k名称を優先する + // If there is a relevant file name in the previous cache, prioritize that for the Human68k name ring_t* pCache = (ring_t*)cRingBackup.Next(); for (;;) { if (pCache == (ring_t*)&cRingBackup) { - pCache = NULL; // 該当するエントリなし - bUpdate = TRUE; // 新規エントリと確定 + pCache = NULL; // No relevant entry + bUpdate = TRUE; // Confirm new entry pFilename->ConvertHuman(); break; } if (strcmp(pFilename->GetHost(), pCache->f.GetHost()) == 0) { - pFilename->CopyHuman(pCache->f.GetHuman()); // Human68k名称のコピー + pFilename->CopyHuman(pCache->f.GetHuman()); // Copy Human68k name break; } pCache = (ring_t*)pCache->r.Next(); } - // 新規エントリの場合はファイル名重複をチェックする - // ホスト側のファイル名から変更があったか、Human68kで表現できないファイル名の場合は - // 以下のチェックを全てパスするファイル名を新たに生成する - // ・正しいファイル名であること - // ・過去のエントリに同名のものが存在しないこと - // ・同名の実ファイル名が存在しないこと - if (pFilename->isReduce() || !pFilename->isCorrect()) { // ファイル名変更が必要か確認 + // If there is a new entry, carry out file name duplication check. + // If the host side file name changed, or if Human68k cannot express the file name, + // generate a new file name that passes all the below checks: + // - File name correctness + // - No duplicated names in previous entries + // - No entity with the same name exists + if (pFilename->isReduce() || !pFilename->isCorrect()) { // Confirm that file name update is required for (DWORD n = 0; n < XM6_HOST_FILENAME_PATTERN_MAX; n++) { - // 正しいファイル名かどうか確認 + // Confirm file name validity if (pFilename->isCorrect()) { - // 過去のエントリと一致するか確認 + // Confirm match with previous entry const CHostFilename* pCheck = FindFilename(pFilename->GetHuman()); if (pCheck == NULL) { - // 一致するものがなければ、実ファイルが存在するか確認 + // If no match, confirm existence of real file strcpy(szPath, m_szHost); - strcat(szPath, (const char*)pFilename->GetHuman()); /// @warning Unicode時要修正 → 済 -#ifndef BAREMETAL + strcat(szPath, (const char*)pFilename->GetHuman()); struct stat sb; if (stat(S2U(szPath), &sb)) -#else - if (f_stat(S2U(szPath), NULL) != FR_OK) -#endif // BAREMETAL - break; // 利用可能パターンを発見 + break; // Discover available patterns } } - // 新しい名前を生成 + // Generate new name pFilename->ConvertHuman(n); } } - // ディレクトリエントリ名称 + // Directory entry name pFilename->SetEntryName(); - // 情報取得 + // Get data strcpy(szPath, m_szHost); strcat(szPath, U2S(pe->d_name)); -#ifndef BAREMETAL struct stat sb; if (stat(S2U(szPath), &sb)) continue; - // 属性 BYTE nHumanAttribute = Human68k::AT_ARCHIVE; if (S_ISDIR(sb.st_mode)) nHumanAttribute = Human68k::AT_DIRECTORY; @@ -1925,11 +1655,9 @@ void CHostPath::Refresh() nHumanAttribute |= Human68k::AT_READONLY; pFilename->SetEntryAttribute(nHumanAttribute); - // サイズ DWORD nHumanSize = (DWORD)sb.st_size; pFilename->SetEntrySize(nHumanSize); - // 日付時刻 WORD nHumanDate = 0; WORD nHumanTime = 0; struct tm* pt = localtime(&sb.st_mtime); @@ -1939,47 +1667,25 @@ void CHostPath::Refresh() } pFilename->SetEntryDate(nHumanDate); pFilename->SetEntryTime(nHumanTime); -#else - FILINFO fno; - if (f_stat(S2U(szPath), &fno) != FR_OK) - continue; - // 属性 - BYTE nHumanAttribute = Human68k::AT_ARCHIVE; - if (fno.fattrib & AM_DIR) - nHumanAttribute = Human68k::AT_DIRECTORY; - if (fno.fattrib & AM_RDO) - nHumanAttribute |= Human68k::AT_READONLY; - pFilename->SetEntryAttribute(nHumanAttribute); - - // サイズ - DWORD nHumanSize = (DWORD)fno.fsize; - pFilename->SetEntrySize(nHumanSize); - - // 日付時刻 - pFilename->SetEntryDate(fno.fdate); - pFilename->SetEntryTime(fno.ftime); -#endif // BAREMETAL - - // クラスタ番号設定 pFilename->SetEntryCluster(0); - // 以前のキャッシュ内容と比較 + // Compare with previous cached contents if (pCache) { if (pCache->f.isSameEntry(pFilename->GetEntry())) { - Free(pRing); // 今回作成したエントリは破棄し - pRing = pCache; // 以前のキャッシュ内容を使う + Free(pRing); // Destroy entry that was created here + pRing = pCache; // Use previous cache } else { - Free(pCache); // 次回の検索対象から除外 - bUpdate = TRUE; // 一致しなければ更新あり + Free(pCache); // Remove from the next search target + bUpdate = TRUE; // Flag for update if no match } } - // リング末尾へ追加 + // Add to end of ring pRing->r.InsertTail(&m_cRing); } - // ディレクトリエントリを解放 + // Release directory entry if (pd) { for (int i = 0; i < nument; i++) { free(pd[i]); @@ -1987,14 +1693,14 @@ void CHostPath::Refresh() free(pd); } - // 残存するキャッシュ内容を削除 + // Delete remaining cache ring_t* p; while ((p = (ring_t*)cRingBackup.Next()) != (ring_t*)&cRingBackup) { - bUpdate = TRUE; // 削除によってエントリ数の減少が判明 + bUpdate = TRUE; // Confirms the decrease in entries due to deletion Free(p); } - // 更新が行なわれたら識別IDを変更 + // Update the identifier if the update has been carried out if (bUpdate) m_nId = ++g_nId; // ASSERT(m_nId); @@ -2002,13 +1708,11 @@ void CHostPath::Refresh() //--------------------------------------------------------------------------- // -/// ホスト側のタイムスタンプを保存 +/// Store the host side time stamp // //--------------------------------------------------------------------------- void CHostPath::Backup() { -#ifndef BAREMETAL - ASSERT(this); ASSERT(m_szHost); ASSERT(strlen(m_szHost) < FILEPATH_MAX); @@ -2017,7 +1721,7 @@ void CHostPath::Backup() size_t len = strlen(szPath); m_tBackup = 0; - if (len > 1) { // ルートディレクトリの場合は何もしない + if (len > 1) { // Don't do anything if it is the root directory len--; ASSERT(szPath[len] == _T('/')); szPath[len] = _T('\0'); @@ -2025,40 +1729,15 @@ void CHostPath::Backup() if (stat(S2U(szPath), &sb) == 0) m_tBackup = sb.st_mtime; } -#else - FILINFO fno; - - ASSERT(this); - ASSERT(m_szHost); - ASSERT(strlen(m_szHost) < FILEPATH_MAX); - - TCHAR szPath[FILEPATH_MAX]; - strcpy(szPath, m_szHost); - size_t len = strlen(szPath); - - m_tBackupD = 0; - m_tBackupT = 0; - if (len > 1) { // ルートディレクトリの場合は何もしない - len--; - ASSERT(szPath[len] == _T('/')); - szPath[len] = _T('\0'); - if (f_stat(S2U(szPath), &fno) == FR_OK) { - m_tBackupD = fno.fdate; - m_tBackupT = fno.ftime; - } - } -#endif // BAREMETAL } //--------------------------------------------------------------------------- // -/// ホスト側のタイムスタンプを復元 +/// Restore the host side time stamp // //--------------------------------------------------------------------------- void CHostPath::Restore() const { -#ifndef BAREMETAL - ASSERT(this); ASSERT(m_szHost); ASSERT(strlen(m_szHost) < FILEPATH_MAX); @@ -2077,53 +1756,25 @@ void CHostPath::Restore() const ut.modtime = m_tBackup; utime(szPath, &ut); } -#else - FILINFO fno; - - ASSERT(this); - ASSERT(m_szHost); - ASSERT(strlen(m_szHost) < FILEPATH_MAX); - - TCHAR szPath[FILEPATH_MAX]; - strcpy(szPath, m_szHost); - size_t len = strlen(szPath); - - if (m_tBackupD) { - ASSERT(len); - len--; - ASSERT(szPath[len] == _T('/')); - szPath[len] = _T('\0'); - - fno.fdate = m_tBackupD; - fno.ftime = m_tBackupT; - f_utime(szPath, &fno); - } -#endif // BAREMETAL } //--------------------------------------------------------------------------- // -/// 更新 +/// Update // //--------------------------------------------------------------------------- void CHostPath::Release() { - ASSERT(this); m_bRefresh = TRUE; } //=========================================================================== // -// ディレクトリエントリ管理 +// Manage directory entries // //=========================================================================== -//--------------------------------------------------------------------------- -// -/// デフォルトコンストラクタ -// -//--------------------------------------------------------------------------- CHostEntry::CHostEntry() { for (size_t n = 0; n < DriveMax; n++) { @@ -2133,17 +1784,12 @@ CHostEntry::CHostEntry() m_nTimeout = 0; } -//--------------------------------------------------------------------------- -// -/// デストラクタ final -// -//--------------------------------------------------------------------------- CHostEntry::~CHostEntry() { Clean(); #ifdef _DEBUG - // オブジェクト確認 + // Confirm object for (size_t n = 0; n < DriveMax; n++) { ASSERT(m_pDrv[n] == NULL); } @@ -2152,15 +1798,14 @@ CHostEntry::~CHostEntry() //--------------------------------------------------------------------------- // -/// 初期化 (ドライバ組込み時) +/// Initialize (when the driver is installed) // //--------------------------------------------------------------------------- void CHostEntry::Init() { - ASSERT(this); #ifdef _DEBUG - // オブジェクト確認 + // Confirm object for (size_t n = 0; n < DriveMax; n++) { ASSERT(m_pDrv[n] == NULL); } @@ -2169,14 +1814,13 @@ void CHostEntry::Init() //--------------------------------------------------------------------------- // -/// 解放 (起動・リセット時) +/// Release (at startup and reset) // //--------------------------------------------------------------------------- void CHostEntry::Clean() { - ASSERT(this); - // オブジェクト削除 + // Delete object for (size_t n = 0; n < DriveMax; n++) { delete m_pDrv[n]; m_pDrv[n] = NULL; @@ -2185,12 +1829,11 @@ void CHostEntry::Clean() //--------------------------------------------------------------------------- // -/// 全てのキャッシュを更新する +/// Update all cache // //--------------------------------------------------------------------------- void CHostEntry::CleanCache() { - ASSERT(this); for (size_t i = 0; i < DriveMax; i++) { if (m_pDrv[i]) @@ -2202,12 +1845,11 @@ void CHostEntry::CleanCache() //--------------------------------------------------------------------------- // -/// 指定されたユニットのキャッシュを更新する +/// Update the cache for the specified unit // //--------------------------------------------------------------------------- void CHostEntry::CleanCache(DWORD nUnit) { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2216,12 +1858,11 @@ void CHostEntry::CleanCache(DWORD nUnit) //--------------------------------------------------------------------------- // -/// 指定されたパスのキャッシュを更新する +/// Update the cache for the specified path // //--------------------------------------------------------------------------- void CHostEntry::CleanCache(DWORD nUnit, const BYTE* szHumanPath) { - ASSERT(this); ASSERT(szHumanPath); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2231,12 +1872,11 @@ void CHostEntry::CleanCache(DWORD nUnit, const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// 指定されたパス以下のキャッシュを全て更新する +/// Update all cache for the specified path and below // //--------------------------------------------------------------------------- void CHostEntry::CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) { - ASSERT(this); ASSERT(szHumanPath); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2246,12 +1886,11 @@ void CHostEntry::CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// 指定されたパスのキャッシュを削除する +/// Delete cache for the specified path // //--------------------------------------------------------------------------- void CHostEntry::DeleteCache(DWORD nUnit, const BYTE* szHumanPath) { - ASSERT(this); ASSERT(szHumanPath); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2261,12 +1900,11 @@ void CHostEntry::DeleteCache(DWORD nUnit, const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) +/// Find host side names (path name + file name (can be abbreviated) + attribute) // //--------------------------------------------------------------------------- BOOL CHostEntry::Find(DWORD nUnit, CHostFiles* pFiles) { - ASSERT(this); ASSERT(pFiles); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2278,12 +1916,11 @@ void CHostEntry::ShellNotify(DWORD, const TCHAR*) {} //--------------------------------------------------------------------------- // -/// ドライブ設定 +/// Drive settings // //--------------------------------------------------------------------------- void CHostEntry::SetDrv(DWORD nUnit, CHostDrv* pDrv) { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit] == NULL); @@ -2292,12 +1929,11 @@ void CHostEntry::SetDrv(DWORD nUnit, CHostDrv* pDrv) //--------------------------------------------------------------------------- // -/// 書き込み禁止か? +/// Is it write-protected? // //--------------------------------------------------------------------------- BOOL CHostEntry::isWriteProtect(DWORD nUnit) const { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2306,12 +1942,11 @@ BOOL CHostEntry::isWriteProtect(DWORD nUnit) const //--------------------------------------------------------------------------- // -/// アクセス可能か? +/// Is it accessible? // //--------------------------------------------------------------------------- BOOL CHostEntry::isEnable(DWORD nUnit) const { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2320,12 +1955,11 @@ BOOL CHostEntry::isEnable(DWORD nUnit) const //--------------------------------------------------------------------------- // -/// メディアチェック +/// Media check // //--------------------------------------------------------------------------- BOOL CHostEntry::isMediaOffline(DWORD nUnit) { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2334,12 +1968,11 @@ BOOL CHostEntry::isMediaOffline(DWORD nUnit) //--------------------------------------------------------------------------- // -/// メディアバイトの取得 +/// Get media byte // //--------------------------------------------------------------------------- BYTE CHostEntry::GetMediaByte(DWORD nUnit) const { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2348,12 +1981,11 @@ BYTE CHostEntry::GetMediaByte(DWORD nUnit) const //--------------------------------------------------------------------------- // -/// ドライブ状態の取得 +/// Get drive status // //--------------------------------------------------------------------------- DWORD CHostEntry::GetStatus(DWORD nUnit) const { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2362,12 +1994,11 @@ DWORD CHostEntry::GetStatus(DWORD nUnit) const //--------------------------------------------------------------------------- // -/// メディア交換チェック +/// Media change check // //--------------------------------------------------------------------------- BOOL CHostEntry::CheckMedia(DWORD nUnit) { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2376,12 +2007,11 @@ BOOL CHostEntry::CheckMedia(DWORD nUnit) //--------------------------------------------------------------------------- // -/// イジェクト +/// Eject // //--------------------------------------------------------------------------- void CHostEntry::Eject(DWORD nUnit) { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2390,12 +2020,11 @@ void CHostEntry::Eject(DWORD nUnit) //--------------------------------------------------------------------------- // -/// ボリュームラベルの取得 +/// Get volume label // //--------------------------------------------------------------------------- void CHostEntry::GetVolume(DWORD nUnit, TCHAR* szLabel) { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2404,12 +2033,11 @@ void CHostEntry::GetVolume(DWORD nUnit, TCHAR* szLabel) //--------------------------------------------------------------------------- // -/// キャッシュからボリュームラベルを取得 +/// Get volume label from cache // //--------------------------------------------------------------------------- BOOL CHostEntry::GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2418,12 +2046,11 @@ BOOL CHostEntry::GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const //--------------------------------------------------------------------------- // -/// 容量の取得 +/// Get capacity // //--------------------------------------------------------------------------- DWORD CHostEntry::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2432,12 +2059,11 @@ DWORD CHostEntry::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) //--------------------------------------------------------------------------- // -/// キャッシュからクラスタサイズを取得 +/// Get cluster size from cache // //--------------------------------------------------------------------------- BOOL CHostEntry::GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const { - ASSERT(this); ASSERT(nUnit < DriveMax); ASSERT(m_pDrv[nUnit]); @@ -2446,13 +2072,13 @@ BOOL CHostEntry::GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) //--------------------------------------------------------------------------- // -/// Human68kフルパス名から先頭の要素を分離・コピー +/// Split and copy the first element from the Human68k full path name /// -/// Human68kフルパス名の先頭の要素をパス区切り文字を除外して取得する。 -/// 書き込み先バッファは23バイト必要。 -/// Human68kパスは必ず/で開始すること。 -/// 途中/が2つ以上連続して出現した場合はエラーとする。 -/// 文字列終端が/だけの場合は空の文字列として処理し、エラーにはしない。 +/// Get the first element from the Human68k full path name and delete the path separator char. +/// 23 bytes is required in the buffer to write to. +/// A Human68k path always starts with a '/'. +/// Throw an error if 2 '/' appears in sequence. +/// If the array ends with a '/' treat it as an empty array and don't trow an error. // //--------------------------------------------------------------------------- const BYTE* CHostDrv::SeparateCopyFilename(const BYTE* szHuman, BYTE* szBuffer) // static @@ -2463,66 +2089,59 @@ const BYTE* CHostDrv::SeparateCopyFilename(const BYTE* szHuman, BYTE* szBuffer) const size_t nMax = 22; const BYTE* p = szHuman; - BYTE c = *p++; // 読み込み + BYTE c = *p++; // Read if (c != '/' && c != '\\') - return NULL; // エラー: 不正なパス名 + return NULL; // Error: Invalid path name - // ファイルいっこいれる + // Insert one file size_t i = 0; for (;;) { - c = *p; // 読み込み + c = *p; // Read if (c == '\0') - break; // 文字列終端なら終了 (終端位置を返す) + break; // Exit if at the end of an array (return the end position) if (c == '/' || c == '\\') { if (i == 0) - return NULL; // エラー: パス区切り文字が連続している - break; // パスの区切りを読んだら終了 (文字の位置を返す) + return NULL; // Error: Two separator chars appear in sequence + break; // Exit after reading the separator (return the char position) } p++; if (i >= nMax) - return NULL; // エラー: 1バイト目がバッファ終端にかかる - szBuffer[i++] = c; // 書き込み + return NULL; // Error: The first byte hits the end of the buffer + szBuffer[i++] = c; // Read - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には0x81~0x9Fと0xE0~0xEF - c = *p++; // 読み込み + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // Specifically 0x81~0x9F and 0xE0~0xEF + c = *p++; // Read if (c < 0x40) - return NULL; // エラー: 不正なSJIS2バイト目 + return NULL; // Error: Invalid Shift-JIS 2nd byte if (i >= nMax) - return NULL; // エラー: 2バイト目がバッファ終端にかかる - szBuffer[i++] = c; // 書き込み + return NULL; // Error: The second byte hits the end of the buffer + szBuffer[i++] = c; // Read } } - szBuffer[i] = '\0'; // 書き込み + szBuffer[i] = '\0'; // Read return p; } //=========================================================================== // -// ファイル検索処理 +// File search processing // //=========================================================================== -//--------------------------------------------------------------------------- -// -/// 初期化 -// -//--------------------------------------------------------------------------- void CHostFiles::Init() { - ASSERT(this); } //--------------------------------------------------------------------------- // -/// パス名・ファイル名を内部で生成 +/// Generate path and file name internally // //--------------------------------------------------------------------------- void CHostFiles::SetPath(const Human68k::namests_t* pNamests) { - ASSERT(this); ASSERT(pNamests); pNamests->GetCopyPath(m_szHumanPath); @@ -2534,12 +2153,11 @@ void CHostFiles::SetPath(const Human68k::namests_t* pNamests) //--------------------------------------------------------------------------- // -/// Human68k側でファイルを検索しホスト側の情報を生成 +/// Find file on the Human68k side and create data on the host side // //--------------------------------------------------------------------------- BOOL CHostFiles::Find(DWORD nUnit, CHostEntry* pEntry) { - ASSERT(this); ASSERT(pEntry); return pEntry->Find(nUnit, this); @@ -2547,12 +2165,11 @@ BOOL CHostFiles::Find(DWORD nUnit, CHostEntry* pEntry) //--------------------------------------------------------------------------- // -/// ファイル名検索 +/// Find file name // //--------------------------------------------------------------------------- const CHostFilename* CHostFiles::Find(CHostPath* pPath) { - ASSERT(this); ASSERT(pPath); if (m_nHumanWildcard) @@ -2563,29 +2180,27 @@ const CHostFilename* CHostFiles::Find(CHostPath* pPath) //--------------------------------------------------------------------------- // -/// Human68k側の検索結果保存 +/// Store the Human68k side search results // //--------------------------------------------------------------------------- void CHostFiles::SetEntry(const CHostFilename* pFilename) { - ASSERT(this); ASSERT(pFilename); - // Human68kディレクトリエントリ保存 + // Store Human68k directory entry memcpy(&m_dirHuman, pFilename->GetEntry(), sizeof(m_dirHuman)); - // Human68kファイル名保存 + // Stire Human68k file name strcpy((char*)m_szHumanResult, (const char*)pFilename->GetHuman()); } //--------------------------------------------------------------------------- // -/// ホスト側の名称を設定 +/// Set host side name // //--------------------------------------------------------------------------- void CHostFiles::SetResult(const TCHAR* szPath) { - ASSERT(this); ASSERT(szPath); ASSERT(strlen(szPath) < FILEPATH_MAX); @@ -2594,12 +2209,11 @@ void CHostFiles::SetResult(const TCHAR* szPath) //--------------------------------------------------------------------------- // -/// ホスト側の名称にファイル名を追加 +/// Add file name to the host side name // //--------------------------------------------------------------------------- void CHostFiles::AddResult(const TCHAR* szPath) { - ASSERT(this); ASSERT(szPath); ASSERT(strlen(m_szHostResult) + strlen(szPath) < FILEPATH_MAX); @@ -2608,32 +2222,25 @@ void CHostFiles::AddResult(const TCHAR* szPath) //--------------------------------------------------------------------------- // -/// ホスト側の名称にHuman68kの新規ファイル名を追加 +/// Add a new Human68k file name to the host side name // //--------------------------------------------------------------------------- void CHostFiles::AddFilename() { - ASSERT(this); ASSERT(strlen(m_szHostResult) + strlen((const char*)m_szHumanFilename) < FILEPATH_MAX); - /// @warning Unicode未対応。いずれUnicodeの世界に飮まれた時はここで変換を行なう → 済 strncat(m_szHostResult, (const char*)m_szHumanFilename, ARRAY_SIZE(m_szHumanFilename)); } //=========================================================================== // -// ファイル検索領域 マネージャ +// File search memory manager // //=========================================================================== #ifdef _DEBUG -//--------------------------------------------------------------------------- -// -/// デストラクタ final -// -//--------------------------------------------------------------------------- CHostFilesManager::~CHostFilesManager() { - // 実体が存在しないことを確認 (念のため) + // Confirm that the entity does not exist (just in case) ASSERT(m_cRing.Next() == &m_cRing); ASSERT(m_cRing.Prev() == &m_cRing); } @@ -2641,18 +2248,17 @@ CHostFilesManager::~CHostFilesManager() //--------------------------------------------------------------------------- // -/// 初期化 (ドライバ組込み時) +/// Initialization (when the driver is installed) // //--------------------------------------------------------------------------- void CHostFilesManager::Init() { - ASSERT(this); - // 実体が存在しないことを確認 (念のため) + // Confirm that the entity does not exist (just in case) ASSERT(m_cRing.Next() == &m_cRing); ASSERT(m_cRing.Prev() == &m_cRing); - // メモリ確保 + // Allocate memory for (DWORD i = 0; i < XM6_HOST_FILES_MAX; i++) { ring_t* p = new ring_t; ASSERT(p); @@ -2662,57 +2268,43 @@ void CHostFilesManager::Init() //--------------------------------------------------------------------------- // -/// 解放 (起動・リセット時) +/// Release (at startup and reset) // //--------------------------------------------------------------------------- void CHostFilesManager::Clean() { - ASSERT(this); - // メモリ解放 + // Release memory CRing* p; while ((p = m_cRing.Next()) != &m_cRing) { delete (ring_t*)p; } } -//--------------------------------------------------------------------------- -// -/// 確保 -// -//--------------------------------------------------------------------------- CHostFiles* CHostFilesManager::Alloc(DWORD nKey) { - ASSERT(this); ASSERT(nKey); - // 末尾から選択 + // Select from the end ring_t* p = (ring_t*)m_cRing.Prev(); - // リング先頭へ移動 + // Move to the start of the ring p->r.Insert(&m_cRing); - // キーを設定 p->f.SetKey(nKey); return &p->f; } -//--------------------------------------------------------------------------- -// -/// 検索 -// -//--------------------------------------------------------------------------- CHostFiles* CHostFilesManager::Search(DWORD nKey) { - ASSERT(this); - // ASSERT(nKey); // DPB破損により検索キーが0になることもある + // ASSERT(nKey); // The search key may become 0 due to DPB damage - // 該当するオブジェクトを検索 + // Find the relevant object ring_t* p = (ring_t*)m_cRing.Next(); for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { if (p->f.isSameKey(nKey)) { - // リング先頭へ移動 + // Move to the start of the ring p->r.Insert(&m_cRing); return &p->f; } @@ -2721,58 +2313,38 @@ CHostFiles* CHostFilesManager::Search(DWORD nKey) return NULL; } -//--------------------------------------------------------------------------- -// -/// 解放 -// -//--------------------------------------------------------------------------- void CHostFilesManager::Free(CHostFiles* pFiles) { - ASSERT(this); ASSERT(pFiles); - // 解放 + // Release pFiles->SetKey(0); pFiles->Init(); - // リング末尾へ移動 + // Move to the end of the ring ring_t* p = (ring_t*)((size_t)pFiles - offsetof(ring_t, f)); p->r.InsertTail(&m_cRing); } //=========================================================================== // -// FCB処理 +// FCB processing // //=========================================================================== -//--------------------------------------------------------------------------- -// -/// 初期化 -// -//--------------------------------------------------------------------------- void CHostFcb::Init() { - ASSERT(this); - m_bUpdate = FALSE; -#ifndef BAREMETAL m_pFile = NULL; -#else - memset(&m_File, 0x00, sizeof(FIL)); -#endif } //--------------------------------------------------------------------------- // -/// ファイルオープンモードを設定 +/// Set file open mode // //--------------------------------------------------------------------------- BOOL CHostFcb::SetMode(DWORD nHumanMode) { - ASSERT(this); - -#ifndef BAREMETAL switch (nHumanMode & Human68k::OP_MASK) { case Human68k::OP_READ: m_pszMode = "rb"; @@ -2786,49 +2358,22 @@ BOOL CHostFcb::SetMode(DWORD nHumanMode) default: return FALSE; } -#else - switch (nHumanMode & Human68k::OP_MASK) { - case Human68k::OP_READ: - m_Mode = FA_READ; - break; - case Human68k::OP_WRITE: - m_Mode = FA_WRITE; - break; - case Human68k::OP_FULL: - m_Mode = FA_WRITE | FA_READ; - break; - default: - return FALSE; - } -#endif // BAREMETAL m_bFlag = (nHumanMode & Human68k::OP_SPECIAL) != 0; return TRUE; } -//--------------------------------------------------------------------------- -// -/// ファイル名を設定 -// -//--------------------------------------------------------------------------- void CHostFcb::SetFilename(const TCHAR* szFilename) { - ASSERT(this); ASSERT(szFilename); ASSERT(strlen(szFilename) < FILEPATH_MAX); strcpy(m_szFilename, szFilename); } -//--------------------------------------------------------------------------- -// -/// Human68kパス名を設定 -// -//--------------------------------------------------------------------------- void CHostFcb::SetHumanPath(const BYTE* szHumanPath) { - ASSERT(this); ASSERT(szHumanPath); ASSERT(strlen((const char*)szHumanPath) < HUMAN68K_PATH_MAX); @@ -2837,142 +2382,84 @@ void CHostFcb::SetHumanPath(const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// ファイル作成 +/// Create file /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce) { -#ifndef BAREMETAL - ASSERT(this); ASSERT((nHumanAttribute & (Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == 0); ASSERT(strlen(m_szFilename) > 0); ASSERT(m_pFile == NULL); - // 重複チェック + // Duplication check if (bForce == FALSE) { struct stat sb; if (stat(S2U(m_szFilename), &sb) == 0) return FALSE; } - // ファイル作成 - m_pFile = fopen(S2U(m_szFilename), "w+b"); /// @warning 理想動作は属性ごと上書き + // Create file + m_pFile = fopen(S2U(m_szFilename), "w+b"); /// @warning The ideal operation is to overwrite each attribute return m_pFile != NULL; -#else - FRESULT fr; - - ASSERT(this); - ASSERT((nHumanAttribute & (Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == 0); - ASSERT(strlen(m_szFilename) > 0); - - // 重複チェック - if (bForce == FALSE) { - if (f_stat(S2U(m_szFilename), NULL) == FR_OK) - return FALSE; - } - - // RPIのベアメタルではRTCが無いのでHuman側の時刻を反映させる - DWORD nHumanTime = ((DWORD)pFcb->date) << 16 | ((DWORD)pFcb->time); - set_fattime(nHumanTime); - - // ファイル作成 - fr = f_open(&m_File, S2U(m_szFilename), FA_CREATE_ALWAYS | FA_WRITE | FA_READ); - /// @warning 理想動作は属性ごと上書き - - return fr == FR_OK; -#endif // BAREMETAL } //--------------------------------------------------------------------------- // -/// ファイルオープン +/// File open /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::Open() { -#ifndef BAREMETAL struct stat st; - ASSERT(this); ASSERT(strlen(m_szFilename) > 0); - // ディレクトリなら失敗 + // Fail if directory if (stat(S2U(m_szFilename), &st) == 0) { if ((st.st_mode & S_IFMT) == S_IFDIR) { return FALSE || m_bFlag; } } - // ファイルオープン + // File open if (m_pFile == NULL) m_pFile = fopen(S2U(m_szFilename), m_pszMode); return m_pFile != NULL || m_bFlag; -#else - FRESULT fr; - FILINFO fno; - - ASSERT(this); - ASSERT(strlen(m_szFilename) > 0); - - // ディレクトリなら失敗 - if (f_stat(S2U(m_szFilename), &fno) == FR_OK) { - if (fno.fattrib & AM_DIR) { - return FALSE || m_bFlag; - } - } - - // ファイルオープン - fr = FR_DISK_ERR; - if (m_File.obj.fs == NULL) - fr = f_open(&m_File, S2U(m_szFilename), m_Mode); - - return fr == FR_OK || m_bFlag; -#endif // BAREMETAL } //--------------------------------------------------------------------------- // -/// ファイルシーク +/// File seek /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::Rewind(DWORD nOffset) { - ASSERT(this); -#ifndef BAREMETAL ASSERT(m_pFile); if (fseek(m_pFile, nOffset, SEEK_SET)) return FALSE; return ftell(m_pFile) != -1L; -#else - if (f_lseek(&m_File, nOffset)) - return FALSE; - - return f_tell(&m_File) != (DWORD)-1L; -#endif // BAREMETAL } //--------------------------------------------------------------------------- // -/// ファイル読み込み +/// Read file /// -/// 0バイト読み込みでも正常動作とする。 -/// エラーの時は-1を返す。 +/// Handle a 0 byte read as normal operation too. +/// Return -1 if error is thrown. // //--------------------------------------------------------------------------- DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize) { -#ifndef BAREMETAL - ASSERT(this); ASSERT(pBuffer); ASSERT(m_pFile); @@ -2981,33 +2468,18 @@ DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize) nResult = (size_t)-1; return (DWORD)nResult; -#else - FRESULT fr; - UINT nResult; - - ASSERT(this); - ASSERT(pBuffer); - - fr = f_read(&m_File, pBuffer, nSize, &nResult); - if (fr != FR_OK) - nResult = (UINT)-1; - - return (DWORD)nResult; -#endif // BAREMETAL } //--------------------------------------------------------------------------- // -/// ファイル書き込み +/// Write file /// -/// 0バイト書き込みでも正常動作とする。 -/// エラーの時は-1を返す。 +/// Handle a 0 byte read as normal operation too. +/// Return -1 if error is thrown. // //--------------------------------------------------------------------------- DWORD CHostFcb::Write(const BYTE* pBuffer, DWORD nSize) { -#ifndef BAREMETAL - ASSERT(this); ASSERT(pBuffer); ASSERT(m_pFile); @@ -3016,51 +2488,31 @@ DWORD CHostFcb::Write(const BYTE* pBuffer, DWORD nSize) nResult = (size_t)-1; return (DWORD)nResult; -#else - FRESULT fr; - UINT nResult; - - ASSERT(this); - ASSERT(pBuffer); - - fr = f_write(&m_File, pBuffer, nSize, &nResult); - if (fr != FR_OK) - nResult = (UINT)-1; - - return (DWORD)nResult; -#endif // BAREMETAL } //--------------------------------------------------------------------------- // -/// ファイル切り詰め +/// Truncate file /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::Truncate() { - ASSERT(this); -#ifndef BAREMETAL ASSERT(m_pFile); return ftruncate(fileno(m_pFile), ftell(m_pFile)) == 0; -#else - return f_truncate(&m_File) == FR_OK; -#endif // BAREMETAL } //--------------------------------------------------------------------------- // -/// ファイルシーク +/// File seek /// -/// エラーの時は-1を返す。 +/// Return -1 if error is thrown. // //--------------------------------------------------------------------------- DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek) { -#ifndef BAREMETAL - ASSERT(this); ASSERT(nHumanSeek == Human68k::SK_BEGIN || nHumanSeek == Human68k::SK_CURRENT || nHumanSeek == Human68k::SK_END); ASSERT(m_pFile); @@ -3082,43 +2534,17 @@ DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek) return (DWORD)-1; return (DWORD)ftell(m_pFile); -#else - FRESULT fr; - - ASSERT(this); - ASSERT(nHumanSeek == Human68k::SK_BEGIN || - nHumanSeek == Human68k::SK_CURRENT || nHumanSeek == Human68k::SK_END); - - switch (nHumanSeek) { - case Human68k::SK_BEGIN: - fr = f_lseek(&m_File, nOffset); - break; - case Human68k::SK_CURRENT: - fr = f_lseek(&m_File, f_tell(&m_File) + nOffset); - break; - // case SK_END: - default: - fr = f_lseek(&m_File, f_size(&m_File) + nOffset); - break; - } - if (fr != FR_OK) - return (DWORD)-1; - - return (DWORD)f_tell(&m_File); -#endif // BAREMETAL } //--------------------------------------------------------------------------- // -/// ファイル時刻設定 +/// Set file time stamp /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::TimeStamp(DWORD nHumanTime) { -#ifndef BAREMETAL - ASSERT(this); ASSERT(m_pFile || m_bFlag); struct tm t = { 0 }; @@ -3135,69 +2561,44 @@ BOOL CHostFcb::TimeStamp(DWORD nHumanTime) ut.actime = ti; ut.modtime = ti; - // クローズ時に更新時刻が上書きされるのを防止するため - // タイムスタンプの更新前にフラッシュして同期させる + // This is for preventing the last updated time stamp to be overwritten upon closing. + // Flush and synchronize before updating the time stamp. fflush(m_pFile); return utime(S2U(m_szFilename), &ut) == 0 || m_bFlag; -#else - FILINFO fno; - - ASSERT(this); - ASSERT(m_bFlag); - - // クローズ時に更新時刻が上書きされるのを防止するため - // タイムスタンプの更新前にフラッシュして同期させる - f_sync(&m_File); - - fno.fdate = (WORD)(nHumanTime >> 16); - fno.ftime = (WORD)nHumanTime; - return f_utime(S2U(m_szFilename), &fno) == FR_OK || m_bFlag; -#endif // BAREMETAL } //--------------------------------------------------------------------------- // -/// ファイルクローズ +/// File close /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::Close() { - ASSERT(this); - BOOL bResult = TRUE; - // ファイルクローズ - // Close→Free(内部で再度Close)という流れもあるので必ず初期化すること。 -#ifndef BAREMETAL + // File close + // Always initialize because of the Close→Free (internally one more Close) flow. if (m_pFile) { fclose(m_pFile); m_pFile = NULL; } -#else - f_close(&m_File); -#endif // BAREMETAL return bResult; } //=========================================================================== // -// FCB処理 マネージャ +// FCB processing manager // //=========================================================================== #ifdef _DEBUG -//--------------------------------------------------------------------------- -// -/// デストラクタ final -// -//--------------------------------------------------------------------------- CHostFcbManager::~CHostFcbManager() { - // 実体が存在しないことを確認 (念のため) + // Confirm that the entity does not exist (just in case) ASSERT(m_cRing.Next() == &m_cRing); ASSERT(m_cRing.Prev() == &m_cRing); } @@ -3205,18 +2606,17 @@ CHostFcbManager::~CHostFcbManager() //--------------------------------------------------------------------------- // -/// 初期化 (ドライバ組込み時) +// Initialization (when the driver is installed) // //--------------------------------------------------------------------------- void CHostFcbManager::Init() { - ASSERT(this); - // 実体が存在しないことを確認 (念のため) + // Confirm that the entity does not exist (just in case) ASSERT(m_cRing.Next() == &m_cRing); ASSERT(m_cRing.Prev() == &m_cRing); - // メモリ確保 + // Memory allocation for (DWORD i = 0; i < XM6_HOST_FCB_MAX; i++) { ring_t* p = new ring_t; ASSERT(p); @@ -3226,63 +2626,50 @@ void CHostFcbManager::Init() //--------------------------------------------------------------------------- // -/// 解放 (起動・リセット時) +// Clean (at startup/reset) // //--------------------------------------------------------------------------- void CHostFcbManager::Clean() { - ASSERT(this); - // メモリ解放 + // Fast task killer CRing* p; while ((p = m_cRing.Next()) != &m_cRing) { delete (ring_t*)p; } } -//--------------------------------------------------------------------------- -// -/// 確保 -// -//--------------------------------------------------------------------------- CHostFcb* CHostFcbManager::Alloc(DWORD nKey) { - ASSERT(this); ASSERT(nKey); - // 末尾から選択 + // Select from the end ring_t* p = (ring_t*)m_cRing.Prev(); - // 使用中ならエラー (念のため) + // Error if in use (just in case) if (p->f.isSameKey(0) == FALSE) { ASSERT(0); return NULL; } - // リング先頭へ移動 + // Move to the top of the ring p->r.Insert(&m_cRing); - // キーを設定 + // Set key p->f.SetKey(nKey); return &p->f; } -//--------------------------------------------------------------------------- -// -/// 検索 -// -//--------------------------------------------------------------------------- CHostFcb* CHostFcbManager::Search(DWORD nKey) { - ASSERT(this); ASSERT(nKey); - // 該当するオブジェクトを検索 + // Search for applicable objects ring_t* p = (ring_t*)m_cRing.Next(); while (p != (ring_t*)&m_cRing) { if (p->f.isSameKey(nKey)) { - // リング先頭へ移動 + // Move to the top of the ring p->r.Insert(&m_cRing); return &p->f; } @@ -3292,41 +2679,32 @@ CHostFcb* CHostFcbManager::Search(DWORD nKey) return NULL; } -//--------------------------------------------------------------------------- -// -/// 解放 -// -//--------------------------------------------------------------------------- void CHostFcbManager::Free(CHostFcb* pFcb) { - ASSERT(this); ASSERT(pFcb); - // 解放 + // Free pFcb->SetKey(0); pFcb->Close(); - // リング末尾へ移動 + // Move to the end of the ring ring_t* p = (ring_t*)((size_t)pFcb - offsetof(ring_t, f)); p->r.InsertTail(&m_cRing); } //=========================================================================== // -// ホスト側ファイルシステム +// Host side file system // //=========================================================================== -DWORD CFileSys::g_nOption; ///< ファイル名変換フラグ +DWORD CFileSys::g_nOption; // File name conversion flag -//--------------------------------------------------------------------------- -// -/// デフォルトコンストラクタ -// -//--------------------------------------------------------------------------- CFileSys::CFileSys() { - // コンフィグデータ初期化 + m_nHostSectorCount = 0; + + // Config data initialization m_nDrives = 0; for (size_t n = 0; n < DriveMax; n++) { @@ -3334,111 +2712,107 @@ CFileSys::CFileSys() m_szBase[n][0] = _T('\0'); } - // TwentyOneオプション監視初期化 + // Initialize TwentyOne option monitoring m_nKernel = 0; m_nKernelSearch = 0; - // 動作フラグ初期化 + // Initialize operational flags m_nOptionDefault = 0; m_nOption = 0; ASSERT(g_nOption == 0); - // 登録したドライブ数は0 + // Number of registered drives are 0 m_nUnits = 0; } //--------------------------------------------------------------------------- // -/// リセット (全クローズ) +/// Reset (close all) // //--------------------------------------------------------------------------- void CFileSys::Reset() { - ASSERT(this); - // 仮想セクタ領域初期化 + // Initialize virtual sectors m_nHostSectorCount = 0; memset(m_nHostSectorBuffer, 0, sizeof(m_nHostSectorBuffer)); - // ファイル検索領域 解放 (起動・リセット時) + // File search memory - release (on startup and reset) m_cFiles.Clean(); - // FCB操作領域 解放 (起動・リセット時) + // FCB operation memory (on startup and reset) m_cFcb.Clean(); - // ディレクトリエントリ 解放 (起動・リセット時) + // Directory entry - release (on startup and reset) m_cEntry.Clean(); - // TwentyOneオプション監視初期化 + // Initialize TwentyOne option monitoring m_nKernel = 0; m_nKernelSearch = 0; - // 動作フラグ初期化 + // Initialize operational flags SetOption(m_nOptionDefault); } //--------------------------------------------------------------------------- // -/// 初期化 (デバイス起動とロード) +/// Initialize (device startup and load) // //--------------------------------------------------------------------------- void CFileSys::Init() { - ASSERT(this); - // ファイル検索領域 初期化 (デバイス起動・ロード時) + // Initialize file search memory (device startup and load) m_cFiles.Init(); - // FCB操作領域 初期化 (デバイス起動・ロード時) + // Initialize FCB operation memory (device startup and load) m_cFcb.Init(); - // ディレクトリエントリ 初期化 (デバイス起動・ロード時) + // Initialize directory entries (device startup and load) m_cEntry.Init(); - // パス個別設定の有無を判定 + // Evaluate per-path setting validity DWORD nDrives = m_nDrives; if (nDrives == 0) { - // 個別設定を使わずにルートディレクトリを使用する + // Use root directory instead of per-path settings strcpy(m_szBase[0], _T("/")); m_nFlag[0] = 0; nDrives++; } - // ファイルシステムを登録 + // Register file system DWORD nUnit = 0; for (DWORD n = 0; n < nDrives; n++) { - // ベースパスが存在しないエントリは登録しない + // Don't register is base path do not exist if (m_szBase[n][0] == _T('\0')) continue; - // ファイルシステムを1ユニット生成 + // Create 1 unit file system CHostDrv* p = new CHostDrv; // std::nothrow if (p) { m_cEntry.SetDrv(nUnit, p); p->Init(m_szBase[n], m_nFlag[n]); - // 次のユニットへ + // To the next unit nUnit++; } } - // 登録したドライブ数を保存 + // Store the registered number of drives m_nUnits = nUnit; } //--------------------------------------------------------------------------- // -/// $40 - デバイス起動 +/// $40 - Device startup // //--------------------------------------------------------------------------- DWORD CFileSys::InitDevice(const Human68k::argument_t* pArgument) { - ASSERT(this); - // オプション初期化 InitOption(pArgument); - // ファイルシステム初期化 + // File system initialization Init(); return m_nUnits; @@ -3446,21 +2820,20 @@ DWORD CFileSys::InitDevice(const Human68k::argument_t* pArgument) //--------------------------------------------------------------------------- // -/// $41 - ディレクトリチェック +/// $41 - Directory check // //--------------------------------------------------------------------------- int CFileSys::CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) { - ASSERT(this); ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + return FS_INVALIDFUNC; // Avoid triggering a fatal error in mint when resuming with an invalid drive - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); if (f.isRootPath()) @@ -3474,30 +2847,29 @@ int CFileSys::CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) //--------------------------------------------------------------------------- // -/// $42 - ディレクトリ作成 +/// $42 - Create directory // //--------------------------------------------------------------------------- int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) { - ASSERT(this); ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetPathOnly(); @@ -3505,15 +2877,11 @@ int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) return FS_INVALIDPATH; f.AddFilename(); - // ディレクトリ作成 -#ifndef BAREMETAL + // Create directory if (mkdir(S2U(f.GetPath()), 0777)) -#else - if (f_mkdir(S2U(f.GetPath())) != FR_OK) -#endif // BAREMETAL return FS_INVALIDPATH; - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); return 0; @@ -3521,37 +2889,36 @@ int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) //--------------------------------------------------------------------------- // -/// $43 - ディレクトリ削除 +/// $43 - Delete directory // //--------------------------------------------------------------------------- int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) { - ASSERT(this); ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetAttribute(Human68k::AT_DIRECTORY); if (f.Find(nUnit, &m_cEntry) == FALSE) return FS_DIRNOTFND; - // キャッシュ削除 + // Delete cache BYTE szHuman[HUMAN68K_PATH_MAX + 24]; ASSERT(strlen((const char*)f.GetHumanPath()) + strlen((const char*)f.GetHumanFilename()) < HUMAN68K_PATH_MAX + 24); @@ -3560,15 +2927,11 @@ int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) strcat((char*)szHuman, "/"); m_cEntry.DeleteCache(nUnit, szHuman); - // ディレクトリ削除 -#ifndef BAREMETAL + // Delete directory if (rmdir(S2U(f.GetPath()))) -#else - if (f_rmdir(S2U(f.GetPath())) != FR_OK) -#endif // BAREMETAL return FS_CANTDELETE; - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); return 0; @@ -3576,30 +2939,29 @@ int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) //--------------------------------------------------------------------------- // -/// $44 - ファイル名変更 +/// $44 - Change file name // //--------------------------------------------------------------------------- int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) { - ASSERT(this); ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetAttribute(Human68k::AT_ALL); @@ -3613,24 +2975,20 @@ int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Hum return FS_INVALIDPATH; fNew.AddFilename(); - // キャッシュ更新 + // Update cache if (f.GetAttribute() & Human68k::AT_DIRECTORY) m_cEntry.CleanCacheChild(nUnit, f.GetHumanPath()); - // ファイル名変更 + // Change file name char szFrom[FILENAME_MAX]; char szTo[FILENAME_MAX]; SJIS2UTF8(f.GetPath(), szFrom, FILENAME_MAX); SJIS2UTF8(fNew.GetPath(), szTo, FILENAME_MAX); -#ifndef BAREMETAL if (rename(szFrom, szTo)) { -#else - if (f_rename(szFrom, szTo) != FR_OK) { -#endif // BAREMETAL return FS_FILENOTFND; } - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); m_cEntry.CleanCache(nUnit, fNew.GetHumanPath()); @@ -3639,44 +2997,39 @@ int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Hum //--------------------------------------------------------------------------- // -/// $45 - ファイル削除 +/// $45 - Delete file // //--------------------------------------------------------------------------- int CFileSys::Delete(DWORD nUnit, const Human68k::namests_t* pNamests) { - ASSERT(this); ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); if (f.Find(nUnit, &m_cEntry) == FALSE) return FS_FILENOTFND; - // ファイル削除 -#ifndef BAREMETAL + // Delete file if (unlink(S2U(f.GetPath()))) -#else - if (f_unlink(S2U(f.GetPath())) != FR_OK) -#endif // BAREMETAL return FS_CANTDELETE; - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); return 0; @@ -3684,48 +3037,46 @@ int CFileSys::Delete(DWORD nUnit, const Human68k::namests_t* pNamests) //--------------------------------------------------------------------------- // -/// $46 - ファイル属性取得/設定 +/// $46 - Get/set file attribute // //--------------------------------------------------------------------------- int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute) { - ASSERT(this); ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetAttribute(Human68k::AT_ALL); if (f.Find(nUnit, &m_cEntry) == FALSE) return FS_FILENOTFND; - // 属性取得なら終了 + // Exit if attribute is acquired if (nHumanAttribute == 0xFF) return f.GetAttribute(); - // 属性チェック + // Attribute check if (nHumanAttribute & Human68k::AT_VOLUME) return FS_CANTACCESS; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // 属性生成 + // Generate attribute DWORD nAttribute = (nHumanAttribute & Human68k::AT_READONLY) | (f.GetAttribute() & ~Human68k::AT_READONLY); if (f.GetAttribute() != nAttribute) { -#ifndef BAREMETAL struct stat sb; if (stat(S2U(f.GetPath()), &sb)) return FS_FILENOTFND; @@ -3735,26 +3086,15 @@ int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD else m |= 0200; // u+w - // 属性設定 + // Set attribute if (chmod(S2U(f.GetPath()), m)) return FS_FILENOTFND; -#else - if (f_stat(S2U(f.GetPath()), NULL) != FR_OK) - return FS_FILENOTFND; - BYTE m = 0; - if (nAttribute & Human68k::AT_READONLY) - m = AM_RDO; - - // 属性設定 - if (f_chmod(S2U(f.GetPath()), m, AM_RDO)) - return FS_FILENOTFND; -#endif // BAREMETAL } - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); - // 変更後の属性取得 + // Get attribute after changing if (f.Find(nUnit, &m_cEntry) == FALSE) return FS_FILENOTFND; @@ -3763,59 +3103,57 @@ int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD //--------------------------------------------------------------------------- // -/// $47 - ファイル検索 +/// $47 - File search // //--------------------------------------------------------------------------- int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles) { - ASSERT(this); ASSERT(pNamests); ASSERT(nKey); ASSERT(pFiles); - // 既に同じキーを持つ領域があれば解放しておく + // Release if memory with the same key already exists CHostFiles* pHostFiles = m_cFiles.Search(nKey); if (pHostFiles != NULL) { m_cFiles.Free(pHostFiles); } - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // ボリュームラベルの取得 + // Get volume label /** @note - 直前のメディア交換チェックで正しくエラーを返しているにもかかわら - ず、ボリュームラベルの取得を実行する行儀の悪いアプリでホスト側の - リムーバブルメディア(CD-ROMドライブ等)が白帯を出すのを防ぐため、 - ボリュームラベルの取得はメディアチェックをせずに行なう仕様とした。 + This skips the media check when getting the volume label to avoid triggering a fatal error + with host side removable media (CD-ROM, etc.) Some poorly coded applications will attempt to + get the volume label even though a proper error was thrown doing a media change check just before. */ if ((pFiles->fatr & (Human68k::AT_ARCHIVE | Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == Human68k::AT_VOLUME) { - // パスチェック + // Path check CHostFiles f; f.SetPath(pNamests); if (f.isRootPath() == FALSE) return FS_FILENOTFND; - // バッファを確保せず、いきなり結果を返す + // Immediately return the results without allocating buffer if (FilesVolume(nUnit, pFiles) == FALSE) return FS_FILENOTFND; return 0; } - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // バッファ確保 + // Allocate buffer pHostFiles = m_cFiles.Alloc(nKey); if (pHostFiles == NULL) return FS_OUTOFMEM; - // ディレクトリチェック + // Directory check pHostFiles->SetPath(pNamests); if (pHostFiles->isRootPath() == FALSE) { pHostFiles->SetPathOnly(); @@ -3825,30 +3163,30 @@ int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests } } - // ワイルドカード使用可能に設定 + // Enable wildcards pHostFiles->SetPathWildcard(); pHostFiles->SetAttribute(pFiles->fatr); - // ファイル検索 + // Find file if (pHostFiles->Find(nUnit, &m_cEntry) == FALSE) { m_cFiles.Free(pHostFiles); return FS_FILENOTFND; } - // 検索結果を格納 + // Store search results pFiles->attr = (BYTE)pHostFiles->GetAttribute(); pFiles->date = pHostFiles->GetDate(); pFiles->time = pHostFiles->GetTime(); pFiles->size = pHostFiles->GetSize(); strcpy((char*)pFiles->full, (const char*)pHostFiles->GetHumanResult()); - // 擬似ディレクトリエントリを指定 + // Specify pseudo-directory entry pFiles->sector = nKey; pFiles->offset = 0; - // ファイル名にワイルドカードがなければ、この時点でバッファを解放可能 + // When the file name does not include wildcards, the buffer may be released if (pNamests->wildcard == 0) { - // しかし、仮想セクタのエミュレーションで使う可能性があるため、すぐには解放しない + // However, there is a chance the virtual selector may be used for emulation, so don't release immediately // m_cFiles.Free(pHostFiles); } @@ -3857,31 +3195,30 @@ int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests //--------------------------------------------------------------------------- // -/// $48 - ファイル次検索 +/// $48 - Search next file // //--------------------------------------------------------------------------- int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles) { - ASSERT(this); ASSERT(nKey); ASSERT(pFiles); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // バッファ検索 + // Find buffer CHostFiles* pHostFiles = m_cFiles.Search(nKey); if (pHostFiles == NULL) return FS_INVALIDPTR; - // ファイル検索 + // Find file if (pHostFiles->Find(nUnit, &m_cEntry) == FALSE) { m_cFiles.Free(pHostFiles); return FS_FILENOTFND; @@ -3890,7 +3227,7 @@ int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles) ASSERT(pFiles->sector == nKey); ASSERT(pFiles->offset == 0); - // 検索結果を格納 + // Store search results pFiles->attr = (BYTE)pHostFiles->GetAttribute(); pFiles->date = pHostFiles->GetDate(); pFiles->time = pHostFiles->GetTime(); @@ -3902,36 +3239,35 @@ int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles) //--------------------------------------------------------------------------- // -/// $49 - ファイル新規作成 +/// $49 - Create new file // //--------------------------------------------------------------------------- int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce) { - ASSERT(this); ASSERT(pNamests); ASSERT(nKey); ASSERT(pFcb); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // 既に同じキーを持つ領域があればエラーとする + // Release if memory with the same key already exists if (m_cFcb.Search(nKey) != NULL) return FS_INVALIDPTR; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetPathOnly(); @@ -3939,31 +3275,31 @@ int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamest return FS_INVALIDPATH; f.AddFilename(); - // 属性チェック + // Attribute check if (nHumanAttribute & (Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) return FS_CANTACCESS; - // パス名保存 + // Store path name CHostFcb* pHostFcb = m_cFcb.Alloc(nKey); if (pHostFcb == NULL) return FS_OUTOFMEM; pHostFcb->SetFilename(f.GetPath()); pHostFcb->SetHumanPath(f.GetHumanPath()); - // オープンモード設定 + // Set open mode pFcb->mode = (WORD)((pFcb->mode & ~Human68k::OP_MASK) | Human68k::OP_FULL); if (pHostFcb->SetMode(pFcb->mode) == FALSE) { m_cFcb.Free(pHostFcb); return FS_ILLEGALMOD; } - // ファイル作成 + // Create file if (pHostFcb->Create(pFcb, nHumanAttribute, bForce) == FALSE) { m_cFcb.Free(pHostFcb); return FS_FILEEXIST; } - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); return 0; @@ -3971,27 +3307,26 @@ int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamest //--------------------------------------------------------------------------- // -/// $4A - ファイルオープン +/// $4A - File open // //--------------------------------------------------------------------------- int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb) { - ASSERT(this); ASSERT(pNamests); ASSERT(nKey); ASSERT(pFcb); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check switch (pFcb->mode & Human68k::OP_MASK) { case Human68k::OP_WRITE: case Human68k::OP_FULL: @@ -3999,38 +3334,38 @@ int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, return FS_FATAL_WRITEPROTECT; } - // 既に同じキーを持つ領域があればエラーとする + // Release if memory with the same key already exists if (m_cFcb.Search(nKey) != NULL) return FS_INVALIDPRM; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetAttribute(Human68k::AT_ALL); if (f.Find(nUnit, &m_cEntry) == FALSE) return FS_FILENOTFND; - // タイムスタンプ + // Time stamp pFcb->date = f.GetDate(); pFcb->time = f.GetTime(); - // ファイルサイズ + // File size pFcb->size = f.GetSize(); - // パス名保存 + // Store path name CHostFcb* pHostFcb = m_cFcb.Alloc(nKey); if (pHostFcb == NULL) return FS_OUTOFMEM; pHostFcb->SetFilename(f.GetPath()); pHostFcb->SetHumanPath(f.GetHumanPath()); - // オープンモード設定 + // Set open mode if (pHostFcb->SetMode(pFcb->mode) == FALSE) { m_cFcb.Free(pHostFcb); return FS_ILLEGALMOD; } - // ファイルオープン + // File open if (pHostFcb->Open() == FALSE) { m_cFcb.Free(pHostFcb); return FS_INVALIDPATH; @@ -4041,138 +3376,135 @@ int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, //--------------------------------------------------------------------------- // -/// $4B - ファイルクローズ +/// $4B - File close // //--------------------------------------------------------------------------- int CFileSys::Close(DWORD nUnit, DWORD nKey, Human68k::fcb_t* /* pFcb */) { - ASSERT(this); ASSERT(nKey); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 既に同じキーを持つ領域がなければエラーとする + // Throw error if memory with the same key does not exist CHostFcb* pHostFcb = m_cFcb.Search(nKey); if (pHostFcb == NULL) return FS_INVALIDPRM; - // ファイルクローズと領域解放 + // File close and release memory m_cFcb.Free(pHostFcb); - // キャッシュ更新 + // Update cache if (pHostFcb->isUpdate()) m_cEntry.CleanCache(nUnit, pHostFcb->GetHumanPath()); - /// @note クローズ時のFCBの状態を他のデバイスと合わせたい + /// TODO: Match the FCB status on close with other devices return 0; } //--------------------------------------------------------------------------- // -/// $4C - ファイル読み込み +/// $4C - Read file /// -/// 0バイト読み込みでも正常終了する。 +/// Clean exit when 0 bytes are read. // //--------------------------------------------------------------------------- int CFileSys::Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, DWORD nSize) { - ASSERT(this); ASSERT(nKey); ASSERT(pFcb); - // ASSERT(pBuffer); // 必要時のみ判定 - ASSERT(nSize <= 0xFFFFFF); // クリップ済 + // ASSERT(pBuffer); // Evaluate only when needed + ASSERT(nSize <= 0xFFFFFF); // Clipped - // 既に同じキーを持つ領域がなければエラーとする + // Throw error if memory with the same key does not exist CHostFcb* pHostFcb = m_cFcb.Search(nKey); if (pHostFcb == NULL) return FS_NOTOPENED; - // バッファ存在確認 + // Confirm the existence of the buffer if (pBuffer == NULL) { m_cFcb.Free(pHostFcb); return FS_INVALIDFUNC; } - // 読み込み + // Read DWORD nResult; nResult = pHostFcb->Read(pBuffer, nSize); if (nResult == (DWORD)-1) { m_cFcb.Free(pHostFcb); - return FS_INVALIDFUNC; /// @note これに加えてエラーコード10(読み込みエラー)を返すべき + return FS_INVALIDFUNC; // TODO: Should return error code 10 (read error) as well here } ASSERT(nResult <= nSize); - // ファイルポインタ更新 - pFcb->fileptr += nResult; /// @note オーバーフロー確認は必要じゃろうか? + // Update file pointer + pFcb->fileptr += nResult; /// TODO: Maybe an overflow check is needed here? return nResult; } //--------------------------------------------------------------------------- // -/// $4D - ファイル書き込み +/// $4D - Write file /// -/// 0バイト書き込みの場合はファイルを切り詰める。 +/// Truncate file if 0 bytes are written. // //--------------------------------------------------------------------------- int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWORD nSize) { - ASSERT(this); ASSERT(nKey); ASSERT(pFcb); - // ASSERT(pBuffer); // 必要時のみ判定 - ASSERT(nSize <= 0xFFFFFF); // クリップ済 + // ASSERT(pBuffer); // Evaluate only when needed + ASSERT(nSize <= 0xFFFFFF); // Clipped - // 既に同じキーを持つ領域がなければエラーとする + // Throw error if memory with the same key does not exist CHostFcb* pHostFcb = m_cFcb.Search(nKey); if (pHostFcb == NULL) return FS_NOTOPENED; DWORD nResult; if (nSize == 0) { - // 切り詰め + // Truncate if (pHostFcb->Truncate() == FALSE) { m_cFcb.Free(pHostFcb); return FS_CANTSEEK; } - // ファイルサイズ更新 + // Update file size pFcb->size = pFcb->fileptr; nResult = 0; } else { - // バッファ存在確認 + // Confirm the existence of the buffer if (pBuffer == NULL) { m_cFcb.Free(pHostFcb); return FS_INVALIDFUNC; } - // 書き込み + // Write nResult = pHostFcb->Write(pBuffer, nSize); if (nResult == (DWORD)-1) { m_cFcb.Free(pHostFcb); - return FS_CANTWRITE; /// @note これに加えてエラーコード11(書き込みエラー)を返すべき + return FS_CANTWRITE; /// TODO: Should return error code 11 (write error) as well here } ASSERT(nResult <= nSize); - // ファイルポインタ更新 - pFcb->fileptr += nResult; /// @note オーバーフロー確認は必要じゃろうか? + // Update file pointer + pFcb->fileptr += nResult; /// TODO: Do we need an overflow check here? - // ファイルサイズ更新 + // Update file size if (pFcb->size < pFcb->fileptr) pFcb->size = pFcb->fileptr; } - // フラグ更新 + // Update flag pHostFcb->SetUpdate(); return nResult; @@ -4180,33 +3512,32 @@ int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWOR //--------------------------------------------------------------------------- // -/// $4E - ファイルシーク +/// $4E - File seek // //--------------------------------------------------------------------------- int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset) { - ASSERT(this); ASSERT(pFcb); - // 既に同じキーを持つ領域がなければエラーとする + // Throw error if memory with the same key does not exist CHostFcb* pHostFcb = m_cFcb.Search(nKey); if (pHostFcb == NULL) return FS_NOTOPENED; - // パラメータチェック + // Parameter check if (nSeek > Human68k::SK_END) { m_cFcb.Free(pHostFcb); return FS_INVALIDPRM; } - // ファイルシーク + // File seek DWORD nResult = pHostFcb->Seek(nOffset, nSeek); if (nResult == (DWORD)-1) { m_cFcb.Free(pHostFcb); return FS_CANTSEEK; } - // ファイルポインタ更新 + // Update file pointer pFcb->fileptr = nResult; return nResult; @@ -4214,42 +3545,41 @@ int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset) //--------------------------------------------------------------------------- // -/// $4F - ファイル時刻取得/設定 +/// $4F - Get/set file time stamp /// -/// 結果の上位16Bitが$FFFFだとエラー。 +/// Throw error when the top 16 bits are $FFFF. // //--------------------------------------------------------------------------- DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime) { - ASSERT(this); ASSERT(nKey); ASSERT(pFcb); - // 取得のみ + // Get only if (nHumanTime == 0) return ((DWORD)pFcb->date << 16) | pFcb->time; - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // 既に同じキーを持つ領域がなければエラーとする + // Throw error if memory with the same key does not exist CHostFcb* pHostFcb = m_cFcb.Search(nKey); if (pHostFcb == NULL) return FS_NOTOPENED; - // 時刻設定 + // Set time stamp if (pHostFcb->TimeStamp(nHumanTime) == FALSE) { m_cFcb.Free(pHostFcb); return FS_INVALIDPRM; @@ -4257,7 +3587,7 @@ DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD pFcb->date = (WORD)(nHumanTime >> 16); pFcb->time = (WORD)nHumanTime; - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, pHostFcb->GetHumanPath()); return 0; @@ -4265,61 +3595,59 @@ DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD //--------------------------------------------------------------------------- // -/// $50 - 容量取得 +/// $50 - Get capacity // //--------------------------------------------------------------------------- int CFileSys::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) { - ASSERT(this); ASSERT(pCapacity); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 容量取得 + // Get capacity return m_cEntry.GetCapacity(nUnit, pCapacity); } //--------------------------------------------------------------------------- // -/// $51 - ドライブ状態検査/制御 +/// $51 - Inspect/control drive status // //--------------------------------------------------------------------------- int CFileSys::CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) { - ASSERT(this); ASSERT(pCtrlDrive); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + return FS_INVALIDFUNC; // Avoid triggering a fatal error in mint when resuming with an invalid drive switch (pCtrlDrive->status) { - case 0: // 状態検査 - case 9: // 状態検査2 + case 0: // Inspect status + case 9: // Inspect status 2 pCtrlDrive->status = (BYTE)m_cEntry.GetStatus(nUnit); return pCtrlDrive->status; - case 1: // イジェクト - case 2: // イジェクト禁止1 (未実装) - case 3: // イジェクト許可1 (未実装) - case 4: // メディア未挿入時にLED点滅 (未実装) - case 5: // メディア未挿入時にLED消灯 (未実装) - case 6: // イジェクト禁止2 (未実装) - case 7: // イジェクト許可2 (未実装) + case 1: // Eject + case 2: // Eject forbidden 1 (not implemented) + case 3: // Eject allowed 1 (not implemented) + case 4: // Flash LED when media is not inserted (not implemented) + case 5: // Turn off LED when media is not inserted (not implemented) + case 6: // Eject forbidden 2 (not implemented) + case 7: // Eject allowed 2 (not implemented) return 0; - case 8: // イジェクト検査 + case 8: // Eject inspection return 1; } @@ -4328,18 +3656,17 @@ int CFileSys::CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) //--------------------------------------------------------------------------- // -/// $52 - DPB取得 +/// $52 - Get DPB /// -/// レジューム後にDPBが取得できないとHuman68k内部でドライブが消滅するため、 -/// 範囲外のユニットでもとにかく正常系として処理する。 +/// If DPB cannot be acquired after resuming, the drive will be torn down internally in Human68k. +/// Therefore, treat even a unit out of bounds as normal operation. // //--------------------------------------------------------------------------- int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) { - ASSERT(this); ASSERT(pDpb); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; @@ -4348,27 +3675,27 @@ int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) if (nUnit < m_nUnits) { media = m_cEntry.GetMediaByte(nUnit); - // セクタ情報獲得 + // Acquire sector data if (m_cEntry.GetCapacityCache(nUnit, &cap) == FALSE) { - // 手動イジェクトだとメディアチェックをすり抜けるためここで捕捉 + // Carry out an extra media check here because it may be skipped when doing a manual eject if (m_cEntry.isEnable(nUnit) == FALSE) goto none; - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) goto none; - // ドライブ状態取得 + // Get drive status m_cEntry.GetCapacity(nUnit, &cap); } } else { none: - cap.clusters = 4; // まっったく問題ないッスよ? + cap.clusters = 4; // This is totally fine, right? cap.sectors = 64; cap.bytes = 512; } - // シフト数計算 + // Calculate number of shifts DWORD nSize = 1; DWORD nShift = 0; for (;;) { @@ -4378,103 +3705,102 @@ int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) nShift++; } - // セクタ番号計算 + // Sector number calculation // - // 以下の順に並べる。 - // クラスタ0: 未使用 - // クラスタ1: FAT - // クラスタ2: ルートディレクトリ - // クラスタ3: データ領域(擬似セクタ) + // In the following order: + // Cluster 0: Unused + // Cluster 1: FAT + // Cluster 2: Root directory + // Cluster 3: Data memory (pseudo-sector) DWORD nFat = cap.sectors; DWORD nRoot = cap.sectors * 2; DWORD nData = cap.sectors * 3; - // DPB設定 - pDpb->sector_size = (WORD)cap.bytes; // 1セクタ当りのバイト数 + // Set DPB + pDpb->sector_size = (WORD)cap.bytes; // Bytes per sector pDpb->cluster_size = - (BYTE)(cap.sectors - 1); // 1クラスタ当りのセクタ数 - 1 - pDpb->shift = (BYTE)nShift; // クラスタ→セクタのシフト数 - pDpb->fat_sector = (WORD)nFat; // FAT の先頭セクタ番号 - pDpb->fat_max = 1; // FAT 領域の個数 - pDpb->fat_size = (BYTE)cap.sectors; // FAT の占めるセクタ数(複写分を除く) + (BYTE)(cap.sectors - 1); // Sectors per cluster - 1 + pDpb->shift = (BYTE)nShift; // Number of cluster → sector shifts + pDpb->fat_sector = (WORD)nFat; // First FAT sector number + pDpb->fat_max = 1; // Number of FAT memory spaces + pDpb->fat_size = (BYTE)cap.sectors; // Number of sectors controlled by FAT (excluding copies) pDpb->file_max = - (WORD)(cap.sectors * cap.bytes / 0x20); // ルートディレクトリに入るファイルの個数 - pDpb->data_sector = (WORD)nData; // データ領域の先頭セクタ番号 - pDpb->cluster_max = (WORD)cap.clusters; // 総クラスタ数 + 1 - pDpb->root_sector = (WORD)nRoot; // ルートディレクトリの先頭セクタ番号 - pDpb->media = media; // メディアバイト + (WORD)(cap.sectors * cap.bytes / 0x20); // Number of files in the root directory + pDpb->data_sector = (WORD)nData; // First sector number of data memory + pDpb->cluster_max = (WORD)cap.clusters; // Total number of clusters + 1 + pDpb->root_sector = (WORD)nRoot; // First sector number of the root directory + pDpb->media = media; // Media byte return 0; } //--------------------------------------------------------------------------- // -/// $53 - セクタ読み込み +/// $53 - Read sector /// -/// セクタは疑似的に構築したものを使用する。 -/// バッファサイズは$200バイト固定。 +/// We use pseudo-sectors. +/// Buffer size is hard coded to $200 byte. // //--------------------------------------------------------------------------- int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize) { - ASSERT(this); ASSERT(pBuffer); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // セクタ数1以外の場合はエラー + // Throw error if number of sectors exceed 1 if (nSize != 1) return FS_INVALIDPRM; - // セクタ情報獲得 + // Acquire sector data Human68k::capacity_t cap; if (m_cEntry.GetCapacityCache(nUnit, &cap) == FALSE) { - // ドライブ状態取得 + // Get drive status m_cEntry.GetCapacity(nUnit, &cap); } - // 擬似ディレクトリエントリへのアクセス + // Access pseudo-directory entry CHostFiles* pHostFiles = m_cFiles.Search(nSector); if (pHostFiles) { - // 擬似ディレクトリエントリを生成 + // Generate pseudo-directory entry Human68k::dirent_t* dir = (Human68k::dirent_t*)pBuffer; memcpy(pBuffer, pHostFiles->GetEntry(), sizeof(*dir)); memset(pBuffer + sizeof(*dir), 0xE5, 0x200 - sizeof(*dir)); - // 擬似ディレクトリエントリ内にファイル実体を指す擬似セクタ番号を記録 - // なお、lzdsysでは以下の式で読み込みセクタ番号を算出している。 + // Register the pseudo-sector number that points to the file entity inside the pseudo-directory. + // Note that in lzdsys the sector number to read is calculated by the following formula: // (dirent.cluster - 2) * (dpb.cluster_size + 1) + dpb.data_sector - /// @warning リトルエンディアン専用 - dir->cluster = (WORD)(m_nHostSectorCount + 2); // 擬似セクタ番号 - m_nHostSectorBuffer[m_nHostSectorCount] = nSector; // 擬似セクタの指す実体 + /// @warning little endian only + dir->cluster = (WORD)(m_nHostSectorCount + 2); // Pseudo-sector number + m_nHostSectorBuffer[m_nHostSectorCount] = nSector; // Entity that points to the pseudo-sector m_nHostSectorCount++; m_nHostSectorCount %= XM6_HOST_PSEUDO_CLUSTER_MAX; return 0; } - // クラスタ番号からセクタ番号を算出 + // Calculate the sector number from the cluster number DWORD n = nSector - (3 * cap.sectors); DWORD nMod = 1; if (cap.sectors) { - // メディアが存在しない場合はcap.sectorsが0になるので注意 + // Beware that cap.sectors becomes 0 when media does not exist nMod = n % cap.sectors; n /= cap.sectors; } - // ファイル実体へのアクセス + // Access the file entity if (nMod == 0 && n < XM6_HOST_PSEUDO_CLUSTER_MAX) { - pHostFiles = m_cFiles.Search(m_nHostSectorBuffer[n]); // 実体を検索 + pHostFiles = m_cFiles.Search(m_nHostSectorBuffer[n]); // Find entity if (pHostFiles) { - // 擬似セクタを生成 + // Generate pseudo-sector CHostFcb f; f.SetFilename(pHostFiles->GetPath()); f.SetMode(Human68k::OP_READ); @@ -4495,29 +3821,28 @@ int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize) //--------------------------------------------------------------------------- // -/// $54 - セクタ書き込み +/// $54 - Write sector // //--------------------------------------------------------------------------- int CFileSys::DiskWrite(DWORD nUnit) { - ASSERT(this); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // 現実を突きつける + // Thrust at reality return FS_INVALIDPRM; } @@ -4528,52 +3853,51 @@ int CFileSys::DiskWrite(DWORD nUnit) //--------------------------------------------------------------------------- int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl) { - ASSERT(this); ASSERT(pIoctrl); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + return FS_INVALIDFUNC; // Avoid triggering a fatal error in mint when resuming with an invalid drive switch (nFunction) { case 0: - // メディアバイトの獲得 + // Acquire media byte pIoctrl->media = m_cEntry.GetMediaByte(nUnit); return 0; case 1: - // Human68k互換のためのダミー - pIoctrl->param = (unsigned long)-1; + // Dummy for Human68k compatibility + pIoctrl->param = -1; return 0; case 2: switch (pIoctrl->param) { case (DWORD)-1: - // メディア再認識 + // Re-identify media m_cEntry.isMediaOffline(nUnit); return 0; case 0: case 1: - // Human68k互換のためのダミー + // Dummy for Human68k compatibility return 0; } break; case (DWORD)-1: - // 常駐判定 + // Resident evaluation memcpy(pIoctrl->buffer, "WindrvXM", 8); return 0; case (DWORD)-2: - // オプション設定 + // Set options SetOption(pIoctrl->param); return 0; case (DWORD)-3: - // オプション獲得 + // Get options pIoctrl->param = GetOption(); return 0; } @@ -4583,42 +3907,40 @@ int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl) //--------------------------------------------------------------------------- // -/// $56 - フラッシュ +/// $56 - Flush // //--------------------------------------------------------------------------- int CFileSys::Flush(DWORD nUnit) { - ASSERT(this); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_INVALIDFUNC; // レジューム後に無効なドライブでmintからコマンドを実行し戻る時に白帯を出さないよう改良 + return FS_INVALIDFUNC; // Avoid triggering a fatal error returning from a mint command when resuming with an invalid drive - // 常に成功 + // Always succeed return 0; } //--------------------------------------------------------------------------- // -/// $57 - メディア交換チェック +/// $57 - Media change check // //--------------------------------------------------------------------------- int CFileSys::CheckMedia(DWORD nUnit) { - ASSERT(this); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + return FS_INVALIDFUNC; // Avoid triggering a fatal error in mint when resuming with an invalid drive - // メディア交換チェック + // Media change check BOOL bResult = m_cEntry.CheckMedia(nUnit); - // メディア未挿入ならエラーとする + // Throw error when media is not inserted if (bResult == FALSE) { return FS_INVALIDFUNC; } @@ -4628,38 +3950,36 @@ int CFileSys::CheckMedia(DWORD nUnit) //--------------------------------------------------------------------------- // -/// $58 - 排他制御 +/// $58 - Lock // //--------------------------------------------------------------------------- int CFileSys::Lock(DWORD nUnit) { - ASSERT(this); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 常に成功 + // Always succeed return 0; } //--------------------------------------------------------------------------- // -/// オプション設定 +/// Set options // //--------------------------------------------------------------------------- void CFileSys::SetOption(DWORD nOption) { - ASSERT(this); - // オプション設定変更でキャッシュクリア + // Clear cache when option settings change if (m_nOption ^ nOption) m_cEntry.CleanCache(); @@ -4669,15 +3989,14 @@ void CFileSys::SetOption(DWORD nOption) //--------------------------------------------------------------------------- // -/// オプション初期化 +/// Initialize options // //--------------------------------------------------------------------------- void CFileSys::InitOption(const Human68k::argument_t* pArgument) { - ASSERT(this); ASSERT(pArgument); - // ドライブ数を初期化 + // Initialize number of drives m_nDrives = 0; const BYTE* pp = pArgument->buf; @@ -4697,7 +4016,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument) } else if (c == '-') { nMode = 0; } else if (c == '/') { - // デフォルトベースパスの指定 + // Specify default base path if (m_nDrives < DriveMax) { p--; strcpy(m_szBase[m_nDrives], (const char *)p); @@ -4706,7 +4025,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument) pp += strlen((const char*)pp) + 1; continue; } else { - // オプション指定ではないので次へ + // Continue since no option is specified pp += strlen((const char*)pp) + 1; continue; } @@ -4746,7 +4065,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument) pp = p; } - // オプション設定 + // Set options if (nOption != m_nOption) { SetOption(nOption); } @@ -4754,27 +4073,26 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument) //--------------------------------------------------------------------------- // -/// ボリュームラベル取得 +/// Get volume label // //--------------------------------------------------------------------------- BOOL CFileSys::FilesVolume(DWORD nUnit, Human68k::files_t* pFiles) { - ASSERT(this); ASSERT(pFiles); - // ボリュームラベル取得 + // Get volume label TCHAR szVolume[32]; BOOL bResult = m_cEntry.GetVolumeCache(nUnit, szVolume); if (bResult == FALSE) { - // 手動イジェクトだとメディアチェックをすり抜けるためここで捕捉 + // Carry out an extra media check here because it may be skipped when doing a manual eject if (m_cEntry.isEnable(nUnit) == FALSE) return FALSE; - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FALSE; - // ボリュームラベル取得 + // Get volume label m_cEntry.GetVolume(nUnit, szVolume); } if (szVolume[0] == _T('\0')) diff --git a/src/raspberrypi/devices/cfilesystem.h b/src/raspberrypi/devices/cfilesystem.h index bf9665d2..21f9593a 100644 --- a/src/raspberrypi/devices/cfilesystem.h +++ b/src/raspberrypi/devices/cfilesystem.h @@ -11,382 +11,365 @@ // It is highly unlikely that this will work for other platforms. //--------------------------------------------------------------------------- -#ifndef cfilesystem_h -#define cfilesystem_h - -#ifdef BAREMETAL -#include "ffconf.h" -#include "ff.h" -#endif // BAREMETAL +#pragma once //--------------------------------------------------------------------------- // -// ステータスコード定義 +// Status code definitions // //--------------------------------------------------------------------------- -#define FS_INVALIDFUNC 0xFFFFFFFF ///< 無効なファンクションコードを実行した -#define FS_FILENOTFND 0xFFFFFFFE ///< 指定したファイルが見つからない -#define FS_DIRNOTFND 0xFFFFFFFD ///< 指定したディレクトリが見つからない -#define FS_OVEROPENED 0xFFFFFFFC ///< オープンしているファイルが多すぎる -#define FS_CANTACCESS 0xFFFFFFFB ///< ディレクトリやボリュームラベルはアクセス不可 -#define FS_NOTOPENED 0xFFFFFFFA ///< 指定したハンドルはオープンされていない -#define FS_INVALIDMEM 0xFFFFFFF9 ///< メモリ管理領域が破壊された -#define FS_OUTOFMEM 0xFFFFFFF8 ///< 実行に必要なメモリがない -#define FS_INVALIDPTR 0xFFFFFFF7 ///< 無効なメモリ管理ポインタを指定した -#define FS_INVALIDENV 0xFFFFFFF6 ///< 不正な環境を指定した -#define FS_ILLEGALFMT 0xFFFFFFF5 ///< 実行ファイルのフォーマットが異常 -#define FS_ILLEGALMOD 0xFFFFFFF4 ///< オープンのアクセスモードが異常 -#define FS_INVALIDPATH 0xFFFFFFF3 ///< ファイル名の指定に誤りがある -#define FS_INVALIDPRM 0xFFFFFFF2 ///< 無効なパラメータでコールした -#define FS_INVALIDDRV 0xFFFFFFF1 ///< ドライブ指定に誤りがある -#define FS_DELCURDIR 0xFFFFFFF0 ///< カレントディレクトリは削除できない -#define FS_NOTIOCTRL 0xFFFFFFEF ///< IOCTRLできないデバイス -#define FS_LASTFILE 0xFFFFFFEE ///< これ以上ファイルが見つからない -#define FS_CANTWRITE 0xFFFFFFED ///< 指定のファイルは書き込みできない -#define FS_DIRALREADY 0xFFFFFFEC ///< 指定のディレクトリは既に登録されている -#define FS_CANTDELETE 0xFFFFFFEB ///< ファイルがあるので削除できない -#define FS_CANTRENAME 0xFFFFFFEA ///< ファイルがあるのでリネームできない -#define FS_DISKFULL 0xFFFFFFE9 ///< ディスクが一杯でファイルが作れない -#define FS_DIRFULL 0xFFFFFFE8 ///< ディレクトリが一杯でファイルが作れない -#define FS_CANTSEEK 0xFFFFFFE7 ///< 指定の位置にはシークできない -#define FS_SUPERVISOR 0xFFFFFFE6 ///< スーパーバイザ状態でスーパバイザ指定した -#define FS_THREADNAME 0xFFFFFFE5 ///< 同じスレッド名が存在する -#define FS_BUFWRITE 0xFFFFFFE4 ///< プロセス間通信のバッファが書込み禁止 -#define FS_BACKGROUND 0xFFFFFFE3 ///< バックグラウンドプロセスを起動できない -#define FS_OUTOFLOCK 0xFFFFFFE0 ///< ロック領域が足りない -#define FS_LOCKED 0xFFFFFFDF ///< ロックされていてアクセスできない -#define FS_DRIVEOPENED 0xFFFFFFDE ///< 指定のドライブはハンドラがオープンされている -#define FS_LINKOVER 0xFFFFFFDD ///< シンボリックリンクネストが16回を超えた -#define FS_FILEEXIST 0xFFFFFFB0 ///< ファイルが存在する +#define FS_INVALIDFUNC 0xFFFFFFFF ///< Executed an invalid function +#define FS_FILENOTFND 0xFFFFFFFE ///< The selected file can not be found +#define FS_DIRNOTFND 0xFFFFFFFD ///< The selected directory can not be found +#define FS_OVEROPENED 0xFFFFFFFC ///< There are too many files open +#define FS_CANTACCESS 0xFFFFFFFB ///< Can not access the direcory or volume +#define FS_NOTOPENED 0xFFFFFFFA ///< The selected handle is not opened +#define FS_INVALIDMEM 0xFFFFFFF9 ///< Memory management has been destroyed +#define FS_OUTOFMEM 0xFFFFFFF8 ///< Insufficient memory for execution +#define FS_INVALIDPTR 0xFFFFFFF7 ///< Selected an invalid memory management pointer +#define FS_INVALIDENV 0xFFFFFFF6 ///< Selected an invalid environment +#define FS_ILLEGALFMT 0xFFFFFFF5 ///< The exeucted file is in an invalid format +#define FS_ILLEGALMOD 0xFFFFFFF4 ///< Invalid open access mode +#define FS_INVALIDPATH 0xFFFFFFF3 ///< Mistake in selected file name +#define FS_INVALIDPRM 0xFFFFFFF2 ///< Called with an invalid parameter +#define FS_INVALIDDRV 0xFFFFFFF1 ///< Mistake in selected drive +#define FS_DELCURDIR 0xFFFFFFF0 ///< Unable to delete the current directory +#define FS_NOTIOCTRL 0xFFFFFFEF ///< Unable to use IOCTRL with the device +#define FS_LASTFILE 0xFFFFFFEE ///< Can not find any more files +#define FS_CANTWRITE 0xFFFFFFED ///< Selected file can not be written +#define FS_DIRALREADY 0xFFFFFFEC ///< Selected directory is already registered +#define FS_CANTDELETE 0xFFFFFFEB ///< Can not delete because of a file +#define FS_CANTRENAME 0xFFFFFFEA ///< Can not rename because of a file +#define FS_DISKFULL 0xFFFFFFE9 ///< Can not create a file because the disk is full +#define FS_DIRFULL 0xFFFFFFE8 ///< Can not create a file because the directory is full +#define FS_CANTSEEK 0xFFFFFFE7 ///< Can not seek in the selected location +#define FS_SUPERVISOR 0xFFFFFFE6 ///< Selected supervisor in supervisor mode +#define FS_THREADNAME 0xFFFFFFE5 ///< A thread with this name already exists +#define FS_BUFWRITE 0xFFFFFFE4 ///< Writing to inter-process communication buffers is disallowed +#define FS_BACKGROUND 0xFFFFFFE3 ///< Unable to start a background process +#define FS_OUTOFLOCK 0xFFFFFFE0 ///< Insufficient lock space +#define FS_LOCKED 0xFFFFFFDF ///< Can not access because it is locked +#define FS_DRIVEOPENED 0xFFFFFFDE ///< Selected drive has an open handler +#define FS_LINKOVER 0xFFFFFFDD ///< The symbolic link is nested over 16 times +#define FS_FILEEXIST 0xFFFFFFB0 ///< The file exists -#define FS_FATAL_MEDIAOFFLINE 0xFFFFFFA3 ///< メディアが入っていない -#define FS_FATAL_WRITEPROTECT 0xFFFFFFA2 ///< 書き込み禁止違反 -#define FS_FATAL_INVALIDCOMMAND 0xFFFFFFA1 ///< 不正なコマンド番号 -#define FS_FATAL_INVALIDUNIT 0xFFFFFFA0 ///< 不正なユニット番号 +#define FS_FATAL_MEDIAOFFLINE 0xFFFFFFA3 ///< No media inserted +#define FS_FATAL_WRITEPROTECT 0xFFFFFFA2 ///< Write protected +#define FS_FATAL_INVALIDCOMMAND 0xFFFFFFA1 ///< Invalid command number +#define FS_FATAL_INVALIDUNIT 0xFFFFFFA0 ///< Invalid unit number -#define HUMAN68K_PATH_MAX 96 ///< Human68kのパス最大長 +#define HUMAN68K_PATH_MAX 96 ///< Longest path allowed in Human68k //=========================================================================== // -/// Human68k 名前空間 +/// Human68k name space // //=========================================================================== namespace Human68k { - /// ファイル属性ビット + /// File attribute bit enum attribute_t { - AT_READONLY = 0x01, ///< 読み込み専用属性 - AT_HIDDEN = 0x02, ///< 隠し属性 - AT_SYSTEM = 0x04, ///< システム属性 - AT_VOLUME = 0x08, ///< ボリュームラベル属性 - AT_DIRECTORY = 0x10, ///< ディレクトリ属性 - AT_ARCHIVE = 0x20, ///< アーカイブ属性 - AT_ALL = 0xFF, ///< 全ての属性ビットが1 + AT_READONLY = 0x01, ///< Read only attribute + AT_HIDDEN = 0x02, ///< Hidden attribute + AT_SYSTEM = 0x04, ///< System attribute + AT_VOLUME = 0x08, ///< Volume label attribute + AT_DIRECTORY = 0x10, ///< Directory attribute + AT_ARCHIVE = 0x20, ///< Archive attribute + AT_ALL = 0xFF, ///< All attribute bits are 1 }; - /// ファイルオープンモード + /// File open modes enum open_t { - OP_READ = 0, ///< 読み込み - OP_WRITE = 1, ///< 書き込み - OP_FULL = 2, ///< 読み書き - OP_MASK = 0x0F, ///< 判定用マスク - OP_SHARE_NONE = 0x10, ///< 共有禁止 - OP_SHARE_READ = 0x20, ///< 読み込み共有 - OP_SHARE_WRITE = 0x30, ///< 書き込み共有 - OP_SHARE_FULL = 0x40, ///< 読み書き共有 - OP_SHARE_MASK = 0x70, ///< 共有判定用マスク - OP_SPECIAL = 0x100, ///< 辞書アクセス + OP_READ = 0, ///< Read + OP_WRITE = 1, ///< Write + OP_FULL = 2, ///< Read/Write + OP_MASK = 0x0F, ///< Decision mask + OP_SHARE_NONE = 0x10, ///< Sharing forbidden + OP_SHARE_READ = 0x20, ///< Read sharing + OP_SHARE_WRITE = 0x30, ///< Write sharing + OP_SHARE_FULL = 0x40, ///< Read/Write sharing + OP_SHARE_MASK = 0x70, ///< Sharing decision mask + OP_SPECIAL = 0x100, ///< Dictionary access }; - /// シーク種類 + /// Seek types enum seek_t { - SK_BEGIN = 0, ///< ファイル先頭から - SK_CURRENT = 1, ///< 現在位置から - SK_END = 2, ///< ファイル末尾から + SK_BEGIN = 0, ///< From the beginning of a file + SK_CURRENT = 1, ///< From the current location + SK_END = 2, ///< From the end of the file }; - /// メディアバイト + /// Media byte enum media_t { - MEDIA_2DD_10 = 0xE0, ///< 2DD/10セクタ - MEDIA_1D_9 = 0xE5, ///< 1D/9セクタ - MEDIA_2D_9 = 0xE6, ///< 2D/9セクタ - MEDIA_1D_8 = 0xE7, ///< 1D/8セクタ - MEDIA_2D_8 = 0xE8, ///< 2D/8セクタ + MEDIA_2DD_10 = 0xE0, ///< 2DD/10 sector + MEDIA_1D_9 = 0xE5, ///< 1D/9 sector + MEDIA_2D_9 = 0xE6, ///< 2D/9 sector + MEDIA_1D_8 = 0xE7, ///< 1D/8 sector + MEDIA_2D_8 = 0xE8, ///< 2D/8 sector MEDIA_2HT = 0xEA, ///< 2HT MEDIA_2HS = 0xEB, ///< 2HS MEDIA_2HDE = 0xEC, ///< 2DDE - MEDIA_1DD_9 = 0xEE, ///< 1DD/9セクタ - MEDIA_1DD_8 = 0xEF, ///< 1DD/8セクタ - MEDIA_MANUAL = 0xF1, ///< リモートドライブ (手動イジェクト) - MEDIA_REMOVABLE = 0xF2, ///< リモートドライブ (リムーバブル) - MEDIA_REMOTE = 0xF3, ///< リモートドライブ + MEDIA_1DD_9 = 0xEE, ///< 1DD/9 sector + MEDIA_1DD_8 = 0xEF, ///< 1DD/8 sector + MEDIA_MANUAL = 0xF1, ///< Remote drive (manual eject) + MEDIA_REMOVABLE = 0xF2, ///< Remote drive (removable) + MEDIA_REMOTE = 0xF3, ///< Remote drive MEDIA_DAT = 0xF4, ///< SCSI-DAT MEDIA_CDROM = 0xF5, ///< SCSI-CDROM MEDIA_MO = 0xF6, ///< SCSI-MO MEDIA_SCSI_HD = 0xF7, ///< SCSI-HD MEDIA_SASI_HD = 0xF8, ///< SASI-HD - MEDIA_RAMDISK = 0xF9, ///< RAMディスク + MEDIA_RAMDISK = 0xF9, ///< RAM disk MEDIA_2HQ = 0xFA, ///< 2HQ - MEDIA_2DD_8 = 0xFB, ///< 2DD/8セクタ - MEDIA_2DD_9 = 0xFC, ///< 2DD/9セクタ + MEDIA_2DD_8 = 0xFB, ///< 2DD/8 sector + MEDIA_2DD_9 = 0xFC, ///< 2DD/9 sector MEDIA_2HC = 0xFD, ///< 2HC MEDIA_2HD = 0xFE, ///< 2HD }; - /// namests構造体 struct namests_t { - BYTE wildcard; ///< ワイルドカード文字数 - BYTE drive; ///< ドライブ番号 - BYTE path[65]; ///< パス(サブディレクトリ+/) - BYTE name[8]; ///< ファイル名 (PADDING 0x20) - BYTE ext[3]; ///< 拡張子 (PADDING 0x20) - BYTE add[10]; ///< ファイル名追加 (PADDING 0x00) + BYTE wildcard; ///< Wildcard character length + BYTE drive; ///< Drive number + BYTE path[65]; ///< Path (subdirectory +/) + BYTE name[8]; ///< File name (PADDING 0x20) + BYTE ext[3]; ///< Extension (PADDING 0x20) + BYTE add[10]; ///< File name addition (PADDING 0x00) - // 文字列取得 - void FASTCALL GetCopyPath(BYTE* szPath) const; - ///< パス名取得 - void FASTCALL GetCopyFilename(BYTE* szFilename) const; - ///< ファイル名取得 + void GetCopyPath(BYTE* szPath) const; + void GetCopyFilename(BYTE* szFilename) const; }; - /// files構造体 struct files_t { - BYTE fatr; ///< + 0 検索する属性 読込専用 - // BYTE drive; ///< + 1 ドライブ番号 読込専用 - DWORD sector; ///< + 2 ディレクトリのセクタ DOS _FILES先頭アドレスで代用 - // WORD cluster; ///< + 6 ディレクトリのクラスタ 詳細不明 (未使用) - WORD offset; ///< + 8 ディレクトリエントリ 書込専用 - // BYTE name[8]; ///< +10 作業用ファイル名 読込専用 (未使用) - // BYTE ext[3]; ///< +18 作業用拡張子 読込専用 (未使用) - BYTE attr; ///< +21 ファイル属性 書込専用 - WORD time; ///< +22 最終変更時刻 書込専用 - WORD date; ///< +24 最終変更月日 書込専用 - DWORD size; ///< +26 ファイルサイズ 書込専用 - BYTE full[23]; ///< +30 フルファイル名 書込専用 + BYTE fatr; ///< + 0 search attribute; read-only + // BYTE drive; ///< + 1 drive number; read-only + DWORD sector; ///< + 2 directory sector; DOS _FILES first address substitute + // WORD cluster; ///< + 6 directory cluster; details unknown (unused) + WORD offset; ///< + 8 directory entry; write-only + // BYTE name[8]; ///< +10 working file name; write-only (unused) + // BYTE ext[3]; ///< +18 working extension; write-only (unused) + BYTE attr; ///< +21 file attribute; write-only + WORD time; ///< +22 last change time of day; write-only + WORD date; ///< +24 last change date; write-only + DWORD size; ///< +26 file size; write-only + BYTE full[23]; ///< +30 full name; write-only }; - /// FCB構造体 struct fcb_t { - // BYTE pad00[6]; ///< + 0~+ 5 (未使用) - DWORD fileptr; ///< + 6~+ 9 ファイルポインタ - // BYTE pad01[4]; ///< +10~+13 (未使用) - WORD mode; ///< +14~+15 オープンモード - // BYTE pad02[16]; ///< +16~+31 (未使用) - // DWORD zero; ///< +32~+35 オープンのとき0が書き込まれている (未使用) - // BYTE name[8]; ///< +36~+43 ファイル名 (PADDING 0x20) (未使用) - // BYTE ext[3]; ///< +44~+46 拡張子 (PADDING 0x20) (未使用) - BYTE attr; ///< +47 ファイル属性 - // BYTE add[10]; ///< +48~+57 ファイル名追加 (PADDING 0x00) (未使用) - WORD time; ///< +58~+59 最終変更時刻 - WORD date; ///< +60~+61 最終変更月日 - // WORD cluster; ///< +62~+63 クラスタ番号 (未使用) - DWORD size; ///< +64~+67 ファイルサイズ - // BYTE pad03[28]; ///< +68~+95 FATキャッシュ (未使用) + // BYTE pad00[6]; ///< + 0~+ 5 (unused) + DWORD fileptr; ///< + 6~+ 9 file pointer + // BYTE pad01[4]; ///< +10~+13 (unused) + WORD mode; ///< +14~+15 open mode + // BYTE pad02[16]; ///< +16~+31 (unused) + // DWORD zero; ///< +32~+35 zeros are written when opened (unused) + // BYTE name[8]; ///< +36~+43 file name (PADDING 0x20) (unused) + // BYTE ext[3]; ///< +44~+46 extension (PADDING 0x20) (unused) + BYTE attr; ///< +47 file attribute + // BYTE add[10]; ///< +48~+57 file name addition (PADDING 0x00) (unused) + WORD time; ///< +58~+59 last change time of day + WORD date; ///< +60~+61 last change date + // WORD cluster; ///< +62~+63 cluster number (unused) + DWORD size; ///< +64~+67 file size + // BYTE pad03[28]; ///< +68~+95 FAT cache (unused) }; - /// capacity構造体 struct capacity_t { - WORD freearea; ///< + 0 使用可能なクラスタ数 - WORD clusters; ///< + 2 総クラスタ数 - WORD sectors; ///< + 4 クラスタあたりのセクタ数 - WORD bytes; ///< + 6 セクタ当たりのバイト数 + WORD freearea; ///< + 0 Number of available clusters + WORD clusters; ///< + 2 Total number of clusters + WORD sectors; ///< + 4 Number of sectors per cluster + WORD bytes; ///< + 6 Number of bytes per sector }; - /// ctrldrive構造体 struct ctrldrive_t { - BYTE status; ///< +13 状態 + BYTE status; ///< +13 status BYTE pad[3]; ///< Padding }; - /// DPB構造体 struct dpb_t { - WORD sector_size; ///< + 0 1セクタ当りのバイト数 - BYTE cluster_size; ///< + 2 1クラスタ当りのセクタ数-1 - BYTE shift; ///< + 3 クラスタ→セクタのシフト数 - WORD fat_sector; ///< + 4 FATの先頭セクタ番号 - BYTE fat_max; ///< + 6 FAT領域の個数 - BYTE fat_size; ///< + 7 FATの占めるセクタ数(複写分を除く) - WORD file_max; ///< + 8 ルートディレクトリに入るファイルの個数 - WORD data_sector; ///< +10 データ領域の先頭セクタ番号 - WORD cluster_max; ///< +12 総クラスタ数+1 - WORD root_sector; ///< +14 ルートディレクトリの先頭セクタ番号 - // DWORD driverentry; ///< +16 デバイスドライバへのポインタ (未使用) - BYTE media; ///< +20 メディア識別子 - // BYTE flag; ///< +21 DPB使用フラグ (未使用) + WORD sector_size; ///< + 0 Number of bytes in one sector + BYTE cluster_size; ///< + 2 Number sectors in one cluster -1 + BYTE shift; ///< + 3 Number of cluster→sector shifts + WORD fat_sector; ///< + 4 FAT first sector number + BYTE fat_max; ///< + 6 FAT storage quantity + BYTE fat_size; ///< + 7 FAT controlled sector number (excluding duplicates) + WORD file_max; ///< + 8 Number of files in the root directory + WORD data_sector; ///< +10 First sector number of data storage + WORD cluster_max; ///< +12 Total number of clusters +1 + WORD root_sector; ///< +14 First sector number of root directory + // DWORD driverentry; ///< +16 Device driver pointer (unused) + BYTE media; ///< +20 Media identifier + // BYTE flag; ///< +21 Flag used by DPB (unused) }; - /// ディレクトリエントリ構造体 + /// Directory entry struct struct dirent_t { - BYTE name[8]; ///< + 0 ファイル名 (PADDING 0x20) - BYTE ext[3]; ///< + 8 拡張子 (PADDING 0x20) - BYTE attr; ///< +11 ファイル属性 - BYTE add[10]; ///< +12 ファイル名追加 (PADDING 0x00) - WORD time; ///< +22 最終変更時刻 - WORD date; ///< +24 最終変更月日 - WORD cluster; ///< +26 クラスタ番号 - DWORD size; ///< +28 ファイルサイズ + BYTE name[8]; ///< + 0 File name (PADDING 0x20) + BYTE ext[3]; ///< + 8 Extension (PADDING 0x20) + BYTE attr; ///< +11 File attribute + BYTE add[10]; ///< +12 File name addition (PADDING 0x00) + WORD time; ///< +22 Last change time of day + WORD date; ///< +24 Last change date + WORD cluster; ///< +26 Cluster number + DWORD size; ///< +28 File size }; - /// IOCTRLパラメータ共用体 + /// IOCTRL parameter union union ioctrl_t { - BYTE buffer[8]; ///< バイト単位でのアクセス - DWORD param; ///< パラメータ(先頭4バイト) - WORD media; ///< メディアバイト(先頭2バイト) + BYTE buffer[8]; ///< Access in byte units + DWORD param; ///< Parameter (First 4 bytes) + WORD media; ///< Media byte (First 2 bytes) }; - /// コマンドライン引数構造体 + /// Command line parameter struct /** - 先頭にドライバ自身のパスが含まれるためHUMAN68K_PATH_MAX以上のサイズにする。 + The driver itself is included in the beginning of the argument, + so setting to a length longer than HUMAN68K_PATH_MAX */ struct argument_t { - BYTE buf[256]; ///< コマンドライン引数 + BYTE buf[256]; ///< Command line argument }; } -/// FILES用バッファ個数 +/// Number of FILES buffers /** -通常は数個で十分だが、Human68kの複数のプロセスがマルチタスクで同時に -深い階層に渡って作業する時などはこの値を増やす必要がある。 +Under normal circumstances it's enough with just a few buffers, +but Human68k multitasking may lead to multiple threads working +deeply in the system, which is why this value is set this high. -デフォルトは20個。 +Default is 20 buffers. */ #define XM6_HOST_FILES_MAX 20 -/// FCB用バッファ個数 +/// Number of FCB buffers /** -同時にオープンできるファイル数はこれで決まる。 +This decides how many files can be opened at the same time. -デフォルトは100ファイル。 +Default is 100 files. */ #define XM6_HOST_FCB_MAX 100 -/// 仮想セクタ/クラスタ 最大個数 +/// Max number of virtual clusters and sectors /** -ファイル実体の先頭セクタへのアクセスに対応するための仮想セクタの個数。 -lzdsysによるアクセスを行なうスレッドの数より多めに確保する。 +Number of virtual sectors used for accessing the first sector of a file entity. +Allocating a generous amount to exceed the number of threads lzdsys uses for access. -デフォルトは10セクタ。 +Default is 10 sectors. */ #define XM6_HOST_PSEUDO_CLUSTER_MAX 10 -/// ディレクトリエントリ キャッシュ個数 +/// Number of caches for directory entries /** -Human68kは、サブディレクトリ内で処理を行なう際にディレクトリエントリ -のチェックを大量に発行する。この応答を高速化するための簡易キャッシュ -の個数を指定する。キャッシュは各ドライブ毎に確保される。 -多いほど高速になるが、増やしすぎるとホストOS側に負担がかかるので注意。 +Human68k carries out a large number of checks of directory entries when doing an operation +inside a subdirectory. This specifies the number of caches used to speed up this operation. +Cache is allocated per drive. The more you add the faster it gets, but use too many +and the host OS gets under a heavy load, so be careful. -デフォルトは16個。 +Default is 16. */ #define XM6_HOST_DIRENTRY_CACHE_MAX 16 -/// 1ディレクトリに収納できるエントリの最大数 +/// Max number of entries that can be stored per directory /** -ディレクトリ内にファイルが大量に存在すると、当時のアプリケーションが -想定していない大量のデータを返してしまうことになる。アプリによっては -一部しか認識されなかったり、速度が大幅に低下したり、メモリ不足で停止 -するなどの危険性が存在する。このため上限を設定することで対処する。 -例えばとあるファイラの場合、2560ファイルが上限となっている。この数を -一つの目安とするのが良い。 +When a large number of files are stored in a directory, a larger amount of data than +contemporanous applications can handle will be returned. This may lead to errors such as +partial data being recognized, performance dropping significantly, or OOM crashes. +To guard against this, an upper limit is defined here. In the case of a particular +file manager, the upper limit is 2560 files. This is one good example to use as reference. -デフォルトは約6万エントリ。(FATのルートディレクトリでの上限値) +Default is around 60000 entries. (Upper limit of the FAT root directory) */ #define XM6_HOST_DIRENTRY_FILE_MAX 65535 -/// ファイル名の重複除外パターンの最大数 +/// Max number of patterns for file name deduplication /** -Human68k側のファイル名は、ホスト側のファイルシステムの名称をもとに自 -動生成されるが、Human68k側のファイル名よりもホスト側のファイル名の名 -称のほうが長いため、同名のファイル名が生成されてしまう可能性がある。 -その時、Human68k側からファイル名を区別できるようにするため、WindrvXM -独自の命名規則に従って別名を生成して解決している。 -理論上は約6千万(36の5乗)通りの別名を生成できる方式を取っているが、実 -際には数百パターン以上の重複判定が発生すると処理に時間がかかってしま -うため、重複の上限を設定することで速度を維持する。常識的な運用であれ -ば、代替名は数パターンもあれば十分運用できるはずであり、この値を可能 -な限り小さい値にすることでパフォーマンスの改善が期待できる。 -この個数を超えるファイル名が重複してしまった場合は、同名のエントリが -複数生成される。この場合、ファイル一覧では見えるがファイル名で指定す -ると最初のエントリのみ扱える状態となる。 +The file names on the Human68k side are automatically created based on the file system on +the host side. However, Human68k have stricter file name length restrictions than the host has. +Because of this, there is a risk that file name duplication will occur. When this happens, +WindrvXM will use a certain renaming heuristic to generate alternate file names to resolve +the duplication. Theoretically, there are over 60 million (36^5) unique file names that +can be generated by this method. However, in reality any more than a few hundred +deduplications will take excessive processing time. So here an upper limit to deduplication +is set in order to maintain system performance. If a system is operated with common sense, +you should only need a few dozen deduplication patterns, so this value can be kept low +to further improve performance. In the case deduplication is not carried out, multiple files +with the same name will be created. When trying to access such files, +only the first entry will ever be accessed. -デフォルトは36パターン。 +Default is 36 patterns. */ #define XM6_HOST_FILENAME_PATTERN_MAX 36 -/// ファイル名重複防止マーク +/// Duplicate file identification mark /** -ホスト側のファイル名とHuman68k側ファイル名の名称の区別をつけるときに -使う文字。コマンドシェル等のエスケープ文字と重ならないものを選ぶと吉。 +A symbol used to distinguish between host and Human68k files. +Do not use a command shell escape character, or similar protected symbol. -デフォルトは「@」。 +Default is '@'. */ #define XM6_HOST_FILENAME_MARK '@' -/// WINDRV動作フラグ +/// WINDRV operational flags /** -通常は0にする。ファイル削除にOSのごみ箱機能を利用する場合は1にする。 -それ以外の値は将来のための予約とする。 -内部動作フラグとメディアバイト偽装などを見越した将来の拡張用。 +Normally set to 0. When put in the OS trash can for deletion, it is set to 1. +Other values are reserved for future use. +Can be used for future extentions such as internal operational flags or mock media byte. */ enum { - WINDRV_OPT_REMOVE = 0x00000001, ///< Bit 0: ファイル削除処理 0:直接 1:ごみ箱 - WINDRV_OPT_ALPHABET = 0x00000020, ///< Bit 5: ファイル名比較 Alphabet区別 0:なし 1:あり 0:-C 1:+C - WINDRV_OPT_COMPARE_LENGTH = 0x00000040, ///< Bit 6: ファイル名比較 文字数(未実装) 0:18+3 1:8+3 0:+T 1:-T - WINDRV_OPT_CONVERT_LENGTH = 0x00000080, ///< Bit 7: ファイル名変換 文字数 0:18+3 1:8+3 0:-A 1:+A - WINDRV_OPT_CONVERT_SPACE = 0x00000100, ///< Bit 8: ファイル名変換 スペース 0:なし 1:'_' - WINDRV_OPT_CONVERT_BADCHAR = 0x00000200, ///< Bit 9: ファイル名変換 無効な文字 0:なし 1:'_' - WINDRV_OPT_CONVERT_HYPHENS = 0x00000400, ///< Bit10: ファイル名変換 中間のハイフン 0:なし 1:'_' - WINDRV_OPT_CONVERT_HYPHEN = 0x00000800, ///< Bit11: ファイル名変換 先頭のハイフン 0:なし 1:'_' - WINDRV_OPT_CONVERT_PERIODS = 0x00001000, ///< Bit12: ファイル名変換 中間のピリオド 0:なし 1:'_' - WINDRV_OPT_CONVERT_PERIOD = 0x00002000, ///< Bit13: ファイル名変換 先頭のピリオド 0:なし 1:'_' - WINDRV_OPT_REDUCED_SPACE = 0x00010000, ///< Bit16: ファイル名短縮 スペース 0:なし 1:短縮 - WINDRV_OPT_REDUCED_BADCHAR = 0x00020000, ///< Bit17: ファイル名短縮 無効な文字 0:なし 1:短縮 - WINDRV_OPT_REDUCED_HYPHENS = 0x00040000, ///< Bit18: ファイル名短縮 中間のハイフン 0:なし 1:短縮 - WINDRV_OPT_REDUCED_HYPHEN = 0x00080000, ///< Bit19: ファイル名短縮 先頭のハイフン 0:なし 1:短縮 - WINDRV_OPT_REDUCED_PERIODS = 0x00100000, ///< Bit20: ファイル名短縮 中間のピリオド 0:なし 1:短縮 - WINDRV_OPT_REDUCED_PERIOD = 0x00200000, ///< Bit21: ファイル名短縮 先頭のピリオド 0:なし 1:短縮 - // Bit24~30 ファイル重複防止マーク 0:自動 1~127:文字 + WINDRV_OPT_REMOVE = 0x00000001, ///< Bit 0: File delete process 0:Directly 1:Trash can + WINDRV_OPT_ALPHABET = 0x00000020, ///< Bit 5: File name comparison; Alphabet distinction 0:No 1:Yes 0:-C 1:+C + WINDRV_OPT_COMPARE_LENGTH = 0x00000040, ///< Bit 6: File name comparison; String length (unimplemented) 0:18+3 1:8+3 0:+T 1:-T + WINDRV_OPT_CONVERT_LENGTH = 0x00000080, ///< Bit 7: File name conversion; String length 0:18+3 1:8+3 0:-A 1:+A + WINDRV_OPT_CONVERT_SPACE = 0x00000100, ///< Bit 8: File name conversion; Space 0:No 1:'_' + WINDRV_OPT_CONVERT_BADCHAR = 0x00000200, ///< Bit 9: File name conversion; Invalid char 0:No 1:'_' + WINDRV_OPT_CONVERT_HYPHENS = 0x00000400, ///< Bit10: File name conversion; Middle hyphen 0:No 1:'_' + WINDRV_OPT_CONVERT_HYPHEN = 0x00000800, ///< Bit11: File name conversion; Initial hyphen 0:No 1:'_' + WINDRV_OPT_CONVERT_PERIODS = 0x00001000, ///< Bit12: File name conversion; Middle period 0:No 1:'_' + WINDRV_OPT_CONVERT_PERIOD = 0x00002000, ///< Bit13: File name conversion; Initial period 0:No 1:'_' + WINDRV_OPT_REDUCED_SPACE = 0x00010000, ///< Bit16: File name reduction; Space 0:No 1:Reduced + WINDRV_OPT_REDUCED_BADCHAR = 0x00020000, ///< Bit17: File name reduction; Invalid char 0:No 1:Reduced + WINDRV_OPT_REDUCED_HYPHENS = 0x00040000, ///< Bit18: File name reduction Middle hyphen 0:No 1:Reduced + WINDRV_OPT_REDUCED_HYPHEN = 0x00080000, ///< Bit19: File name reduction Initial hyphen 0:No 1:Reduced + WINDRV_OPT_REDUCED_PERIODS = 0x00100000, ///< Bit20: File name reduction Middle period 0:No 1:Reduced + WINDRV_OPT_REDUCED_PERIOD = 0x00200000, ///< Bit21: File name reduction Initial period 0:No 1:Reduced + // Bit24~30 Duplicate file identification mark 0:Automatic 1~127:Chars }; -/// ファイルシステム動作フラグ +/// File system operational flag /** -通常は0にする。リードオンリーでマウントしたいドライブの場合は1にする。 -それ以外の値は将来のための予約とする。 -判定が困難なデバイス(自作USBストレージとか)のための保険用。 +Normal is 0. Becomes 1 if attempting to mount in read-only mode. +Reserving the other values for future use. +Insurance against hard-to-detect devices such as homemade USB storage. */ enum { - FSFLAG_WRITE_PROTECT = 0x00000001, ///< Bit0: 強制書き込み禁止 - FSFLAG_REMOVABLE = 0x00000002, ///< Bit1: 強制リムーバブルメディア - FSFLAG_MANUAL = 0x00000004, ///< Bit2: 強制手動イジェクト + FSFLAG_WRITE_PROTECT = 0x00000001, ///< Bit0: Force write protect + FSFLAG_REMOVABLE = 0x00000002, ///< Bit1: Force removable media + FSFLAG_MANUAL = 0x00000004, ///< Bit2: Force manual eject }; //=========================================================================== // -/// まるっとリングリスト +/// Full ring list /// -/// 先頭(root.next)が最も新しいオブジェクト。 -/// 末尾(root.prev)が最も古い/未使用オブジェクト。 -/// コード効率追求のため、delete時は必ずポインタをアップキャストすること。 +/// First (root.next) is the most recent object. +/// Last (root.prev) is the oldest / unused object. +/// For code optimization purposes, always upcast the pointer when deleting. // //=========================================================================== class CRing { public: - // 基本ファンクション - CRing() { Init(); } ///< デフォルトコンストラクタ - ~CRing() { Remove(); } ///< デストラクタ final - void Init() { ASSERT(this); next = prev = this; } ///< 初期化 + CRing() { Init(); } + ~CRing() { Remove(); } + void Init() { next = prev = this; } - CRing* Next() const { ASSERT(this); return next; } ///< 次の要素を取得 - CRing* Prev() const { ASSERT(this); return prev; } ///< 前の要素を取得 + CRing* Next() const { return next; } ///< Get the next element + CRing* Prev() const { return prev; } ///< Get the previous element void Insert(CRing* pRoot) { - ASSERT(this); - // 該当オブジェクトを切り離し + // Separate the relevant objects ASSERT(next); ASSERT(prev); next->prev = prev; prev->next = next; - // リング先頭へ挿入 + // Insert into the beginning of the ring ASSERT(pRoot); ASSERT(pRoot->next); next = pRoot->next; @@ -394,17 +377,16 @@ public: pRoot->next->prev = this; pRoot->next = this; } - ///< オブジェクト切り離し & リング先頭へ挿入 + ///< Separate objects & insert into the beginning of the ring void InsertTail(CRing* pRoot) { - ASSERT(this); - // 該当オブジェクトを切り離し + // Separate the relevant objects ASSERT(next); ASSERT(prev); next->prev = prev; prev->next = next; - // リング末尾へ挿入 + // Insert into the end of the ring ASSERT(pRoot); ASSERT(pRoot->prev); next = pRoot; @@ -412,14 +394,13 @@ public: pRoot->prev->next = this; pRoot->prev = this; } - ///< オブジェクト切り離し & リング末尾へ挿入 + ///< Separate objects & insert into the end of the ring void InsertRing(CRing* pRoot) { - ASSERT(this); - if (next == prev) return; + if (next == prev) return; - // リング先頭へ挿入 + // Insert into the beginning of the ring ASSERT(pRoot); ASSERT(pRoot->next); pRoot->next->prev = prev; @@ -427,567 +408,537 @@ public: pRoot->next = next; next->prev = pRoot; - // 自分自身を空にする + // Empty self next = prev = this; } - ///< 自分以外のオブジェクト切り離し & リング先頭へ挿入 + ///< Separate objects except self & insert into the beginning of the ring void Remove() { - ASSERT(this); - // 該当オブジェクトを切り離し + // Separate the relevant objects ASSERT(next); ASSERT(prev); next->prev = prev; prev->next = next; - // 安全のため自分自身を指しておく (何度切り離しても問題ない) + // To be safe, assign self (nothing stops you from separating any number of times) next = prev = this; } - ///< オブジェクト切り離し + ///< Separate objects private: - CRing* next; ///< 次の要素 - CRing* prev; ///< 前の要素 + CRing* next; ///< Next element + CRing* prev; ///< Previous element }; //=========================================================================== // -/// ディレクトリエントリ ファイル名 +/// Directory Entry: File Name // //=========================================================================== class CHostFilename { public: - // 基本ファンクション - CHostFilename(); ///< デフォルトコンストラクタ - static size_t Offset() { return offsetof(CHostFilename, m_szHost); } ///< オフセット位置取得 + CHostFilename(); + static size_t Offset() { return offsetof(CHostFilename, m_szHost); } ///< Get offset location - void SetHost(const TCHAR* szHost); ///< ホスト側の名称を設定 - const TCHAR* GetHost() const { ASSERT(this); return m_szHost; } ///< ホスト側の名称を取得 - void ConvertHuman(int nCount = -1); ///< Human68k側の名称を変換 - void CopyHuman(const BYTE* szHuman); ///< Human68k側の名称を複製 - BOOL isReduce() const; ///< Human68k側の名称が加工されたか調査 - BOOL isCorrect() const { ASSERT(this); return m_bCorrect; } ///< Human68k側のファイル名規則に合致しているか調査 - const BYTE* GetHuman() const { ASSERT(this); return m_szHuman; } ///< Human68kファイル名を取得 + void SetHost(const TCHAR* szHost); ///< Set the name of the host + const TCHAR* GetHost() const { return m_szHost; } ///< Get the name of the host + void ConvertHuman(int nCount = -1); ///< Convert the Human68k name + void CopyHuman(const BYTE* szHuman); ///< Copy the Human68k name + BOOL isReduce() const; ///< Inspect if the Human68k name is generated + BOOL isCorrect() const { return m_bCorrect; } ///< Inspect if the Human68k file name adhers to naming rules + const BYTE* GetHuman() const { return m_szHuman; } ///< Get Human68k file name const BYTE* GetHumanLast() const - { ASSERT(this); return m_pszHumanLast; } ///< Human68kファイル名を取得 - const BYTE* GetHumanExt() const { ASSERT(this); return m_pszHumanExt; }///< Human68kファイル名を取得 - void SetEntryName(); ///< Human68kディレクトリエントリを設定 + { return m_pszHumanLast; } ///< Get Human68k file name + const BYTE* GetHumanExt() const { return m_pszHumanExt; }///< Get Human68k file name + void SetEntryName(); ///< Set Human68k directory entry void SetEntryAttribute(BYTE nHumanAttribute) - { ASSERT(this); m_dirHuman.attr = nHumanAttribute; } ///< Human68kディレクトリエントリを設定 + { m_dirHuman.attr = nHumanAttribute; } ///< Set Human68k directory entry void SetEntrySize(DWORD nHumanSize) - { ASSERT(this); m_dirHuman.size = nHumanSize; } ///< Human68kディレクトリエントリを設定 + { m_dirHuman.size = nHumanSize; } ///< Set Human68k directory entry void SetEntryDate(WORD nHumanDate) - { ASSERT(this); m_dirHuman.date = nHumanDate; } ///< Human68kディレクトリエントリを設定 + { m_dirHuman.date = nHumanDate; } ///< Set Human68k directory entry void SetEntryTime(WORD nHumanTime) - { ASSERT(this); m_dirHuman.time = nHumanTime; } ///< Human68kディレクトリエントリを設定 + { m_dirHuman.time = nHumanTime; } ///< Set Human68k directory entry void SetEntryCluster(WORD nHumanCluster) - { ASSERT(this); m_dirHuman.cluster = nHumanCluster; } ///< Human68kディレクトリエントリを設定 + { m_dirHuman.cluster = nHumanCluster; } ///< Set Human68k directory entry const Human68k::dirent_t* GetEntry() const - { ASSERT(this); return &m_dirHuman; } ///< Human68kディレクトリエントリを取得 - BOOL CheckAttribute(DWORD nHumanAttribute) const; ///< Human68kディレクトリエントリの属性判定 + { return &m_dirHuman; } ///< Get Human68k directory entry + BOOL CheckAttribute(DWORD nHumanAttribute) const; ///< Determine Human68k directory entry attributes BOOL isSameEntry(const Human68k::dirent_t* pdirHuman) const - { ASSERT(this); ASSERT(pdirHuman); return memcmp(&m_dirHuman, pdirHuman, sizeof(m_dirHuman)) == 0; } - ///< Human68kディレクトリエントリの一致判定 + { ASSERT(pdirHuman); return memcmp(&m_dirHuman, pdirHuman, sizeof(m_dirHuman)) == 0; } + ///< Determine Human68k directory entry match - // パス名操作 - static const BYTE* SeparateExt(const BYTE* szHuman); ///< Human68kファイル名から拡張子を分離 + // Path name operations + static const BYTE* SeparateExt(const BYTE* szHuman); ///< Extract extension from Human68k file name private: static BYTE* CopyName(BYTE* pWrite, const BYTE* pFirst, const BYTE* pLast); - ///< Human68k側のファイル名要素をコピー + ///< Copy Human68k file name elements - const BYTE* m_pszHumanLast; ///< 該当エントリのHuman68k内部名の終端位置 - const BYTE* m_pszHumanExt; ///< 該当エントリのHuman68k内部名の拡張子位置 - BOOL m_bCorrect; ///< 該当エントリのHuman68k内部名が正しければ真 - BYTE m_szHuman[24]; ///< 該当エントリのHuman68k内部名 - Human68k::dirent_t m_dirHuman; ///< 該当エントリのHuman68k全情報 - TCHAR m_szHost[FILEPATH_MAX]; ///< 該当エントリのホスト側の名称 (可変長) + const BYTE* m_pszHumanLast; ///< Last position of the Human68k internal name of the relevant entry + const BYTE* m_pszHumanExt; ///< Position of the extension of the Human68k internal name of the relevant entry + BOOL m_bCorrect; ///< TRUE if the relevant entry of the Human68k internal name is correct + BYTE m_szHuman[24]; ///< Human68k internal name of the relevant entry + Human68k::dirent_t m_dirHuman; ///< All information for the Human68k relevant entry + TCHAR m_szHost[FILEPATH_MAX]; ///< The host name of the relevant entry (variable length) }; //=========================================================================== // -/// ディレクトリエントリ パス名 +/// Directory entry: path name /// -/// Human68k側のパス名は、必ず先頭が/で始まり、末尾が/で終わる。 -/// ユニット番号は持たない。 -/// 高速化のため、ホスト側の名称にはベースパス部分も含む。 +/// A file path in Human68k always begings with / and ends with / +/// They don't hold unit numbers. +/// Include the base path part of the name on the host side for a performance boost. // //=========================================================================== /** @note -ほとんどのHuman68kのアプリは、ファイルの更新等によってディレクトリエ -ントリに変更が生じた際、親ディレクトリのタイムスタンプは変化しないも -のと想定して作られている。 -ところがホスト側のファイルシステムでは親ディレクトリのタイムスタンプ -も変化してしまうものが主流となってしまっている。 +Most Human68k applications are written in a way that expects time stamps not to +get updated for new directories created as a result of file operations, which +triggers updates to directory entires. +However, on the host file system, new directories do typically get an updated time stamp. -このため、ディレクトリのコピー等において、アプリ側は正確にタイムスタ -ンプ情報などを設定しているにもかかわらず、実行結果では時刻情報が上書 -きされてしまうという惨劇が起きてしまう。 +The unfortunate outcome is that when copying a directory for instance, the time stamp +will get overwritten even if the application did not intend for the time stamp to get updated. -そこでディレクトリキャッシュ内にFATタイムスタンプのエミュレーション -機能を実装した。ホスト側のファイルシステムの更新時にタイムスタンプ情 -報を復元することでHuman68k側の期待する結果と一致させる。 +Here follows an implementation of a directory cache FAT time stamp emulation feature. +At the time of a file system update on the host side, time stamp information will be restored +in order to achieve expected behavior on the Human68k side. */ class CHostPath: public CRing { - /// メモリ管理用 + /// For memory management struct ring_t { - CRing r; ///< 円環 - CHostFilename f; ///< 実体 + CRing r; + CHostFilename f; }; public: - /// 検索用バッファ + /// Search buffer struct find_t { - DWORD count; ///< 検索実行回数 + 1 (0のときは以下の値は無効) - DWORD id; ///< 次回検索を続行するパスのエントリ識別ID - const ring_t* pos; ///< 次回検索を続行する位置 (識別ID一致時) - Human68k::dirent_t entry; ///< 次回検索を続行するエントリ内容 + DWORD count; ///< Search execution count + 1 (When 0 the below value is invalid) + DWORD id; ///< Entry unique ID for the path of the next search + const ring_t* pos; ///< Position of the next search (When identical to unique ID) + Human68k::dirent_t entry; ///< Contents of the next seach entry - void Clear() { count = 0; } ///< 初期化 + void Clear() { count = 0; } ///< Initialize }; - // 基本ファンクション - CHostPath(); ///< デフォルトコンストラクタ - ~CHostPath(); ///< デストラクタ final - void Clean(); ///< 再利用のための初期化 + CHostPath(); + ~CHostPath(); + void Clean(); ///< Initialialize for reuse - void SetHuman(const BYTE* szHuman); ///< Human68k側の名称を直接指定する - void SetHost(const TCHAR* szHost); ///< ホスト側の名称を直接指定する - BOOL isSameHuman(const BYTE* szHuman) const; ///< Human68k側の名称を比較する - BOOL isSameChild(const BYTE* szHuman) const; ///< Human68k側の名称を比較する - const TCHAR* GetHost() const { ASSERT(this); return m_szHost; } ///< ホスト側の名称の獲得 + void SetHuman(const BYTE* szHuman); ///< Directly specify the name on the Human68k side + void SetHost(const TCHAR* szHost); ///< Directly specify the name on the host side + BOOL isSameHuman(const BYTE* szHuman) const; ///< Compare the name on the Human68k side + BOOL isSameChild(const BYTE* szHuman) const; ///< Compare the name on the Human68k side + const TCHAR* GetHost() const { return m_szHost; } ///< Obtain the name on the host side const CHostFilename* FindFilename(const BYTE* szHuman, DWORD nHumanAttribute = Human68k::AT_ALL) const; - ///< ファイル名を検索 + ///< Find file name const CHostFilename* FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const; - ///< ファイル名を検索 (ワイルドカード対応) - BOOL isRefresh(); ///< ファイル変更が行なわれたか確認 - void Refresh(); ///< ファイル再構成 - void Backup(); /// ホスト側のタイムスタンプを保存 - void Restore() const; /// ホスト側のタイムスタンプを復元 - void Release(); ///< 更新 + ///< Find file name (with support for wildcards) + BOOL isRefresh(); ///< Check that the file change has been done + void Refresh(); ///< Refresh file + void Backup(); /// Backup the time stamp on the host side + void Restore() const; /// Restore the time stamp on the host side + void Release(); ///< Update - // CHostEntryが利用する外部API - static void InitId() { g_nId = 0; } ///< 識別ID生成用カウンタ初期化 + // CHostEntry is an external API that we use + static void InitId() { g_nId = 0; } ///< Initialize the counter for the unique ID generation private: - static ring_t* Alloc(size_t nLength); ///< ファイル名領域確保 - static void Free(ring_t* pRing); ///< ファイル名領域解放 + static ring_t* Alloc(size_t nLength); ///< Allocate memory for the file name + static void Free(ring_t* pRing); ///< Release memory for the file name static int Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFirst, const BYTE* pBufLast); - ///< 文字列比較 (ワイルドカード対応) + ///< Compare string (with support for wildcards) - CRing m_cRing; ///< CHostFilename連結用 - #ifndef BAREMETAL - time_t m_tBackup; ///< 時刻復元用 - #else - WORD m_tBackupD; ///< 時刻復元用 - WORD m_tBackupT; ///< 時刻復元用 - #endif // BAREMETAL - BOOL m_bRefresh; ///< 更新フラグ - DWORD m_nId; ///< 識別ID (値が変化した場合は更新を意味する) - BYTE m_szHuman[HUMAN68K_PATH_MAX]; ///< 該当エントリのHuman68k内部名 - TCHAR m_szHost[FILEPATH_MAX]; ///< 該当エントリのホスト側の名称 + CRing m_cRing; ///< For CHostFilename linking + time_t m_tBackup; ///< For time stamp restoration + BOOL m_bRefresh; ///< Refresh flag + DWORD m_nId; ///< Unique ID (When the value has changed, it means an update has been made) + BYTE m_szHuman[HUMAN68K_PATH_MAX]; ///< The internal Human68k name for the relevant entry + TCHAR m_szHost[FILEPATH_MAX]; ///< The host side name for the relevant entry - static DWORD g_nId; ///< 識別ID生成用カウンタ + static DWORD g_nId; ///< Counter for the unique ID generation }; //=========================================================================== // -/// ファイル検索処理 +/// File search processing /// -/// Human68k側のファイル名を内部Unicodeで処理するのは正直キツい。と -/// いうわけで、全てBYTEに変換して処理する。変換処理はディレクトリエ -/// ントリキャッシュが一手に担い、WINDRV側はすべてシフトJISのみで扱 -/// えるようにする。 -/// また、Human68k側名称は、完全にベースパス指定から独立させる。 +/// It's pretty much impossible to process Human68k file names as Unicode internally. +/// So, we carry out binary conversion for processing. We leave it up to the +/// directory entry cache to handle the conversion, which allows WINDRV to read +/// everything as Shift-JIS. Additionally, it allows Human68k names to be +/// fully independent of base path assignments. /// -/// ファイルを扱う直前に、ディレクトリエントリのキャッシュを生成する。 -/// ディレクトリエントリの生成処理は高コストのため、一度生成したエントリは -/// 可能な限り維持して使い回す。 +/// We create directory entry cache just before file handling. +/// Since creating directory entires is very costly, we try to reuse created entries +/// as much as humanly possible. /// -/// ファイル検索は3方式。すべてCHostFiles::Find()で処理する。 -/// 1. パス名のみ検索 属性はディレクトリのみ _CHKDIR _CREATE -/// 2. パス名+ファイル名+属性の検索 _OPEN -/// 3. パス名+ワイルドカード+属性の検索 _FILES _NFILES -/// 検索結果は、ディレクトリエントリ情報として保持しておく。 +/// There are three kinds of file search. They are all processed in CHostFiles::Find() +/// 1. Search by path name only; the only attribute is 'directory'; _CHKDIR _CREATE +/// 2. Path + file name + attribute search; _OPEN +/// 3. Path + wildcard + attribute search; _FILES _NFILES +/// The search results are kept as directory entry data. // //=========================================================================== class CHostFiles { public: - // 基本ファンクション - CHostFiles() { SetKey(0); Init(); } ///< デフォルトコンストラクタ - void Init(); ///< 初期化 + CHostFiles() { SetKey(0); Init(); } + void Init(); - void SetKey(DWORD nKey) { ASSERT(this); m_nKey = nKey; } ///< 検索キー設定 - BOOL isSameKey(DWORD nKey) const { ASSERT(this); return m_nKey == nKey; } ///< 検索キー比較 - void SetPath(const Human68k::namests_t* pNamests); ///< パス名・ファイル名を内部で生成 - BOOL isRootPath() const { return m_szHumanPath[1] == '\0'; } ///< ルートディレクトリ判定 - void SetPathWildcard() { m_nHumanWildcard = 1; } ///< ワイルドカードによるファイル検索を有効化 - void SetPathOnly() { m_nHumanWildcard = 0xFF; } ///< パス名のみを有効化 - BOOL isPathOnly() const { return m_nHumanWildcard == 0xFF; } ///< パス名のみ設定か判定 + void SetKey(DWORD nKey) { m_nKey = nKey; } ///< Set search key + BOOL isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< Compare search key + void SetPath(const Human68k::namests_t* pNamests); ///< Create path and file name internally + BOOL isRootPath() const { return m_szHumanPath[1] == '\0'; } ///< Check if root directory + void SetPathWildcard() { m_nHumanWildcard = 1; } ///< Enable file search using wildcards + void SetPathOnly() { m_nHumanWildcard = 0xFF; } ///< Enable only path names + BOOL isPathOnly() const { return m_nHumanWildcard == 0xFF; } ///< Check if set to only path names void SetAttribute(DWORD nHumanAttribute) { m_nHumanAttribute = nHumanAttribute; } - ///< 検索属性を設定 - BOOL Find(DWORD nUnit, class CHostEntry* pEntry); ///< Human68k側でファイルを検索しホスト側の情報を生成 - const CHostFilename* Find(CHostPath* pPath); ///< ファイル名検索 - void SetEntry(const CHostFilename* pFilename); ///< Human68k側の検索結果保存 - void SetResult(const TCHAR* szPath); ///< ホスト側の名称を設定 - void AddResult(const TCHAR* szPath); ///< ホスト側の名称にファイル名を追加 - void AddFilename(); ///< ホスト側の名称にHuman68kの新規ファイル名を追加 + ///< Set search attribute + BOOL Find(DWORD nUnit, class CHostEntry* pEntry); ///< Find files on the Human68k side, generating data on the host side + const CHostFilename* Find(CHostPath* pPath); ///< Find file name + void SetEntry(const CHostFilename* pFilename); ///< Store search results on the Human68k side + void SetResult(const TCHAR* szPath); ///< Set names on the host side + void AddResult(const TCHAR* szPath); ///< Add file name to the name on the host side + void AddFilename(); ///< Add the new Human68k file name to the name on the host side - const TCHAR* GetPath() const { ASSERT(this); return m_szHostResult; } ///< ホスト側の名称を取得 + const TCHAR* GetPath() const { return m_szHostResult; } ///< Get the name on the host side - const Human68k::dirent_t* GetEntry() const { ASSERT(this); return &m_dirHuman; }///< Human68kディレクトリエントリを取得 + const Human68k::dirent_t* GetEntry() const { return &m_dirHuman; }///< Get Human68k directory entry - DWORD GetAttribute() const { ASSERT(this); return m_dirHuman.attr; } ///< Human68k属性を取得 - WORD GetDate() const { ASSERT(this); return m_dirHuman.date; } ///< Human68k日付を取得 - WORD GetTime() const { ASSERT(this); return m_dirHuman.time; } ///< Human68k時刻を取得 - DWORD GetSize() const { ASSERT(this); return m_dirHuman.size; } ///< Human68kファイルサイズを取得 - const BYTE* GetHumanFilename() const { ASSERT(this); return m_szHumanFilename; }///< Human68kファイル名を取得 - const BYTE* GetHumanResult() const { ASSERT(this); return m_szHumanResult; } ///< Human68kファイル名検索結果を取得 - const BYTE* GetHumanPath() const { ASSERT(this); return m_szHumanPath; } ///< Human68kパス名を取得 + DWORD GetAttribute() const { return m_dirHuman.attr; } ///< Get Human68k attribute + WORD GetDate() const { return m_dirHuman.date; } ///< Get Human68k date + WORD GetTime() const { return m_dirHuman.time; } ///< Get Human68k time + DWORD GetSize() const { return m_dirHuman.size; } ///< Get Human68k file size + const BYTE* GetHumanFilename() const { return m_szHumanFilename; }///< Get Human68k file name + const BYTE* GetHumanResult() const { return m_szHumanResult; } ///< Get Human68k file name search results + const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name private: - DWORD m_nKey; ///< Human68kのFILESバッファアドレス 0なら未使用 - DWORD m_nHumanWildcard; ///< Human68kのワイルドカード情報 - DWORD m_nHumanAttribute; ///< Human68kの検索属性 - CHostPath::find_t m_findNext; ///< 次回検索位置情報 - Human68k::dirent_t m_dirHuman; ///< 検索結果 Human68kファイル情報 - BYTE m_szHumanFilename[24]; ///< Human68kのファイル名 - BYTE m_szHumanResult[24]; ///< 検索結果 Human68kファイル名 + DWORD m_nKey; ///< FILES buffer address for Human68k; 0 is unused + DWORD m_nHumanWildcard; ///< Human68k wildcard data + DWORD m_nHumanAttribute; ///< Human68k search attribute + CHostPath::find_t m_findNext; ///< Next search location data + Human68k::dirent_t m_dirHuman; ///< Search results: Human68k file data + BYTE m_szHumanFilename[24]; ///< Human68k file name + BYTE m_szHumanResult[24]; ///< Search results: Human68k file name BYTE m_szHumanPath[HUMAN68K_PATH_MAX]; - ///< Human68kのパス名 - TCHAR m_szHostResult[FILEPATH_MAX]; ///< 検索結果 ホスト側のフルパス名 + ///< Human68k path name + TCHAR m_szHostResult[FILEPATH_MAX]; ///< Search results: host's full path name }; //=========================================================================== // -/// ファイル検索領域 マネージャ +/// File search memory manager // //=========================================================================== class CHostFilesManager { public: #ifdef _DEBUG - // 基本ファンクション - ~CHostFilesManager(); ///< デストラクタ final + ~CHostFilesManager(); #endif // _DEBUG - void Init(); ///< 初期化 (ドライバ組込み時) - void Clean(); ///< 解放 (起動・リセット時) + void Init(); ///< Initialization (when the driver is installed) + void Clean(); ///< Release (when starting up or resetting) - CHostFiles* Alloc(DWORD nKey); ///< 確保 - CHostFiles* Search(DWORD nKey); ///< 検索 - void Free(CHostFiles* pFiles); ///< 解放 + CHostFiles* Alloc(DWORD nKey); + CHostFiles* Search(DWORD nKey); + void Free(CHostFiles* pFiles); private: - /// メモリ管理用 + /// For memory management struct ring_t { - CRing r; ///< 円環 - CHostFiles f; ///< 実体 + CRing r; + CHostFiles f; }; - CRing m_cRing; ///< CHostFiles連結用 + CRing m_cRing; ///< For attaching to CHostFiles }; //=========================================================================== // -/// FCB処理 +/// FCB processing // //=========================================================================== class CHostFcb { public: - // 基本ファンクション - CHostFcb() { SetKey(0); Init(); } ///< デフォルトコンストラクタ - ~CHostFcb() { Close(); } ///< デストラクタ final - void Init(); ///< 初期化 + CHostFcb() { SetKey(0); Init(); } + ~CHostFcb() { Close(); } + void Init(); - void SetKey(DWORD nKey) { ASSERT(this); m_nKey = nKey; } ///< 検索キー設定 - BOOL isSameKey(DWORD nKey) const { ASSERT(this); return m_nKey == nKey; } ///< 検索キー比較 - void SetUpdate() { ASSERT(this); m_bUpdate = TRUE; } ///< 更新 - BOOL isUpdate() const { ASSERT(this); return m_bUpdate; } ///< 更新状態取得 - BOOL SetMode(DWORD nHumanMode); ///< ファイルオープンモードを設定 - void SetFilename(const TCHAR* szFilename); ///< ファイル名を設定 - void SetHumanPath(const BYTE* szHumanPath); ///< Human68kパス名を設定 - const BYTE* GetHumanPath() const { ASSERT(this); return m_szHumanPath; } ///< Human68kパス名を取得 + void SetKey(DWORD nKey) { m_nKey = nKey; } ///< Set search key + BOOL isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< Compare search key + void SetUpdate() { m_bUpdate = TRUE; } ///< Update + BOOL isUpdate() const { return m_bUpdate; } ///< Get update state + BOOL SetMode(DWORD nHumanMode); ///< Set file open mode + void SetFilename(const TCHAR* szFilename); ///< Set file name + void SetHumanPath(const BYTE* szHumanPath); ///< Set Human68k path name + const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name - BOOL Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); ///< ファイル作成 - BOOL Open(); ///< ファイルオープン - BOOL Rewind(DWORD nOffset); ///< ファイルシーク - DWORD Read(BYTE* pBuffer, DWORD nSize); ///< ファイル読み込み - DWORD Write(const BYTE* pBuffer, DWORD nSize); ///< ファイル書き込み - BOOL Truncate(); ///< ファイル切り詰め - DWORD Seek(DWORD nOffset, DWORD nHumanSeek); ///< ファイルシーク - BOOL TimeStamp(DWORD nHumanTime); ///< ファイル時刻設定 - BOOL Close(); ///< ファイルクローズ + BOOL Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); ///< Create file + BOOL Open(); ///< Open file + BOOL Rewind(DWORD nOffset); ///< Seek file + DWORD Read(BYTE* pBuffer, DWORD nSize); ///< Read file + DWORD Write(const BYTE* pBuffer, DWORD nSize); ///< Write file + BOOL Truncate(); ///< Truncate file + DWORD Seek(DWORD nOffset, DWORD nHumanSeek); ///< Seek file + BOOL TimeStamp(DWORD nHumanTime); ///< Set file time stamp + BOOL Close(); ///< Close file private: - DWORD m_nKey; ///< Human68kのFCBバッファアドレス (0なら未使用) - BOOL m_bUpdate; ///< 更新フラグ - #ifndef BAREMETAL - FILE* m_pFile; ///< ホスト側のファイルオブジェクト - const char* m_pszMode; ///< ホスト側のファイルオープンモード - #else - FIL m_File; ///< ホスト側のファイルオブジェクト - BYTE m_Mode; ///< ホスト側のファイルオープンモード - #endif // BAREMETAL - bool m_bFlag; ///< ホスト側のファイルオープンフラグ + DWORD m_nKey; ///< Human68k FCB buffer address (0 if unused) + BOOL m_bUpdate; ///< Update flag + FILE* m_pFile; ///< Host side file object + const char* m_pszMode; ///< Host side file open mode + bool m_bFlag; ///< Host side file open flag BYTE m_szHumanPath[HUMAN68K_PATH_MAX]; - ///< Human68kのパス名 - TCHAR m_szFilename[FILEPATH_MAX]; ///< ホスト側のファイル名 + ///< Human68k path name + TCHAR m_szFilename[FILEPATH_MAX]; ///< Host side file name }; //=========================================================================== // -/// FCB処理 マネージャ +/// FCB processing manager // //=========================================================================== class CHostFcbManager { public: #ifdef _DEBUG - // 基本ファンクション - ~CHostFcbManager(); ///< デストラクタ final + ~CHostFcbManager(); #endif // _DEBUG - void Init(); ///< 初期化 (ドライバ組込み時) - void Clean(); ///< 解放 (起動・リセット時) + void Init(); ///< Initialization (when the driver is installed) + void Clean(); ///< Release (when starting up or resetting) - CHostFcb* Alloc(DWORD nKey); ///< 確保 - CHostFcb* Search(DWORD nKey); ///< 検索 - void Free(CHostFcb* p); ///< 解放 + CHostFcb* Alloc(DWORD nKey); + CHostFcb* Search(DWORD nKey); + void Free(CHostFcb* p); private: - /// メモリ管理用 + /// For memory management struct ring_t { - CRing r; ///< 円環 - CHostFcb f; ///< 実体 + CRing r; + CHostFcb f; }; - CRing m_cRing; ///< CHostFcb連結用 + CRing m_cRing; ///< For attaching to CHostFcb }; //=========================================================================== // -/// ホスト側ドライブ +/// Host side drive /// -/// ドライブ毎に必要な情報の保持に専念し、管理はCHostEntryで行なう。 +/// Keeps the required data for each drive, managed in CHostEntry. // //=========================================================================== class CHostDrv { public: - // 基本ファンクション - CHostDrv(); ///< デフォルトコンストラクタ - ~CHostDrv(); ///< デストラクタ final - void Init(const TCHAR* szBase, DWORD nFlag); ///< 初期化 (デバイス起動とロード) + CHostDrv(); + ~CHostDrv(); + void Init(const TCHAR* szBase, DWORD nFlag); ///< Initialization (device startup and load) - BOOL isWriteProtect() const { ASSERT(this); return m_bWriteProtect; } ///< 書き込み禁止か? - BOOL isEnable() const { ASSERT(this); return m_bEnable; } ///< アクセス可能か? - BOOL isMediaOffline(); ///< メディアチェック - BYTE GetMediaByte() const; ///< メディアバイトの取得 - DWORD GetStatus() const; ///< ドライブ状態の取得 - void SetEnable(BOOL bEnable); ///< メディア状態設定 - BOOL CheckMedia(); ///< メディア交換チェック - void Update(); ///< メディア状態更新 - void Eject(); ///< イジェクト - void GetVolume(TCHAR* szLabel); ///< ボリュームラベルの取得 - BOOL GetVolumeCache(TCHAR* szLabel) const; ///< キャッシュからボリュームラベルを取得 - DWORD GetCapacity(Human68k::capacity_t* pCapacity); ///< 容量の取得 - BOOL GetCapacityCache(Human68k::capacity_t* pCapacity) const; ///< キャッシュから容量を取得 + BOOL isWriteProtect() const { return m_bWriteProtect; } + BOOL isEnable() const { return m_bEnable; } ///< Is it accessible? + BOOL isMediaOffline(); + BYTE GetMediaByte() const; + DWORD GetStatus() const; + void SetEnable(BOOL bEnable); ///< Set media status + BOOL CheckMedia(); ///< Check if media was changed + void Update(); ///< Update media status + void Eject(); + void GetVolume(TCHAR* szLabel); ///< Get volume label + BOOL GetVolumeCache(TCHAR* szLabel) const; ///< Get volume label from cache + DWORD GetCapacity(Human68k::capacity_t* pCapacity); + BOOL GetCapacityCache(Human68k::capacity_t* pCapacity) const; ///< Get capacity from cache - // キャッシュ操作 - void CleanCache(); ///< 全てのキャッシュを更新する - void CleanCache(const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを更新する - void CleanCacheChild(const BYTE* szHumanPath); ///< 指定されたパス以下のキャッシュを全て更新する - void DeleteCache(const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを削除する - CHostPath* FindCache(const BYTE* szHuman); ///< 指定されたパスがキャッシュされているか検索する - CHostPath* CopyCache(CHostFiles* pFiles); ///< キャッシュ情報を元に、ホスト側の名称を獲得する - CHostPath* MakeCache(CHostFiles* pFiles); ///< ホスト側の名称の構築に必要な情報をすべて取得する - BOOL Find(CHostFiles* pFiles); ///< ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) + // Cache operations + void CleanCache(); ///< Update all cache + void CleanCache(const BYTE* szHumanPath); ///< Update cache for the specified path + void CleanCacheChild(const BYTE* szHumanPath); ///< Update all cache below the specified path + void DeleteCache(const BYTE* szHumanPath); ///< Delete the cache for the specified path + CHostPath* FindCache(const BYTE* szHuman); ///< Inspect if the specified path is cached + CHostPath* CopyCache(CHostFiles* pFiles); ///< Acquire the host side name on the basis of cache information + CHostPath* MakeCache(CHostFiles* pFiles); ///< Get all required data to construct a host side name + BOOL Find(CHostFiles* pFiles); ///< Find host side name (path + file name (can be abbreviated) + attribute) private: - // パス名操作 + // Path name operations static const BYTE* SeparateCopyFilename(const BYTE* szHuman, BYTE* szBuffer); - ///< Human68kフルパス名から先頭の要素を分離・コピー + ///< Split and copy the first element of the Human68k full path name - // 排他制御 void Lock() {} void Unlock() {} - /// メモリ管理用 + /// For memory management struct ring_t { - CRing r; ///< 円環 - CHostPath f; ///< 実体 + CRing r; + CHostPath f; }; - BOOL m_bWriteProtect; ///< 書き込み禁止ならTRUE - BOOL m_bEnable; ///< メディアが利用可能ならTRUE - DWORD m_nRing; ///< パス名保持数 - CRing m_cRing; ///< CHostPath連結用 - Human68k::capacity_t m_capCache; ///< セクタ情報キャッシュ sectors == 0 なら未キャッシュ - BOOL m_bVolumeCache; ///< ボリュームラベル読み込み済みならTRUE - TCHAR m_szVolumeCache[24]; ///< ボリュームラベルキャッシュ - TCHAR m_szBase[FILEPATH_MAX]; ///< ベースパス + BOOL m_bWriteProtect; ///< TRUE if write-protected + BOOL m_bEnable; ///< TRUE if media is usable + DWORD m_nRing; ///< Number of stored path names + CRing m_cRing; ///< For attaching to CHostPath + Human68k::capacity_t m_capCache; ///< Sector data cache: if "sectors == 0" then not cached + BOOL m_bVolumeCache; ///< TRUE if the volume label has been read + TCHAR m_szVolumeCache[24]; ///< Volume label cache + TCHAR m_szBase[FILEPATH_MAX]; ///< Base path }; //=========================================================================== // -/// ディレクトリエントリ管理 +/// Directory entry management // //=========================================================================== class CHostEntry { public: - // 基本ファンクション - CHostEntry(); ///< デフォルトコンストラクタ - ~CHostEntry(); ///< デストラクタ final - void Init(); ///< 初期化 (ドライバ組込み時) - void Clean(); ///< 解放 (起動・リセット時) + CHostEntry(); + ~CHostEntry(); + void Init(); ///< Initialization (when the driver is installed) + void Clean(); ///< Release (when starting up or resetting) - // キャッシュ操作 - void CleanCache(); ///< 全てのキャッシュを更新する - void CleanCache(DWORD nUnit); ///< 指定されたユニットのキャッシュを更新する - void CleanCache(DWORD nUnit, const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを更新する - void CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath); ///< 指定されたパス以下のキャッシュを全て更新する - void DeleteCache(DWORD nUnit, const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを削除する - BOOL Find(DWORD nUnit, CHostFiles* pFiles); ///< ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) - void ShellNotify(DWORD nEvent, const TCHAR* szPath); ///< ホスト側ファイルシステム状態変化通知 + // Cache operations + void CleanCache(); ///< Update all cache + void CleanCache(DWORD nUnit); ///< Update cache for the specified unit + void CleanCache(DWORD nUnit, const BYTE* szHumanPath); ///< Update cache for the specified path + void CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath); ///< Update cache below the specified path + void DeleteCache(DWORD nUnit, const BYTE* szHumanPath); ///< Delete cache for the specified path + BOOL Find(DWORD nUnit, CHostFiles* pFiles); ///< Find host side name (path + file name (can be abbreviated) + attribute) + void ShellNotify(DWORD nEvent, const TCHAR* szPath); ///< Notify status change in the host side file system - // ドライブオブジェクト操作 - void SetDrv(DWORD nUnit, CHostDrv* pDrv); ///< ドライブ設定 - BOOL isWriteProtect(DWORD nUnit) const; ///< 書き込み禁止か? - BOOL isEnable(DWORD nUnit) const; ///< アクセス可能か? - BOOL isMediaOffline(DWORD nUnit); ///< メディアチェック - BYTE GetMediaByte(DWORD nUnit) const; ///< メディアバイトの取得 - DWORD GetStatus(DWORD nUnit) const; ///< ドライブ状態の取得 - BOOL CheckMedia(DWORD nUnit); ///< メディア交換チェック - void Eject(DWORD nUnit); ///< イジェクト - void GetVolume(DWORD nUnit, TCHAR* szLabel); ///< ボリュームラベルの取得 - BOOL GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const; ///< キャッシュからボリュームラベルを取得 - DWORD GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); ///< 容量の取得 + // Drive object operations + void SetDrv(DWORD nUnit, CHostDrv* pDrv); + BOOL isWriteProtect(DWORD nUnit) const; + BOOL isEnable(DWORD nUnit) const; ///< Is it accessible? + BOOL isMediaOffline(DWORD nUnit); + BYTE GetMediaByte(DWORD nUnit) const; + DWORD GetStatus(DWORD nUnit) const; ///< Get drive status + BOOL CheckMedia(DWORD nUnit); ///< Media change check + void Eject(DWORD nUnit); + void GetVolume(DWORD nUnit, TCHAR* szLabel); ///< Get volume label + BOOL GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const; ///< Get volume label from cache + DWORD GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); BOOL GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const; - ///< キャッシュからクラスタサイズを取得 + ///< Get cluster size from cache - /// 定数 enum { - DriveMax = 10 ///< ドライブ最大候補数 + DriveMax = 10 ///< Max number of drive candidates }; private: - CHostDrv* m_pDrv[DriveMax]; ///< ホスト側ドライブオブジェクト - DWORD m_nTimeout; ///< 最後にタイムアウトチェックを行なった時刻 + CHostDrv* m_pDrv[DriveMax]; ///< Host side drive object + DWORD m_nTimeout; ///< Last time a timeout check was carried out }; //=========================================================================== // -/// ホスト側ファイルシステム +/// Host side file system // //=========================================================================== /** @note -現在の見解。 +Current state of affairs: -XM6の設計思想とは反するが、class Windrvまたはclass CWindrvに直接 -class CFileSysへのポインタを持たせる方法を模索するべきである。 -これにより、以下のメリットが得られる。 +While it violates the design philosophy of XM6, we should find a way for +'class Windrv' and 'class CWindrv' to have a direct pointer to 'class CFileSys'. +This way, we get the following benefits. -メリットその1。 -コマンドハンドラの大量のメソッド群を一ヶ所で集中管理できる。 -コマンドハンドラはホスト側の仕様変更などの要因によって激しく変化する -可能性が高いため、メソッドの追加削除や引数の変更などのメンテナンスが -大幅に楽になる。 +Benefit no. 1 +Makes it possible to manage a large number of command handler methods in one place. +There is a high chance the command handlers will change drastically because of +host system architectural changes, so we will save a huge amount of maintenance work +in the long run. -メリットその2。 -仮想関数のテーブル生成・参照処理に関する処理コードを駆逐できる。 -XM6では複数のファイルシステムオブジェクトを同時に使うような実装は -ありえない。つまりファイルシステムオブジェクトにポリモーフィズムは -まったく必要ないどころか、ただクロックの無駄となっているだけである。 +Benefit no. 2 +We would get rid of virtual funcion code for processing table creation and lookup. +It is not feasible to implement code in XM6 for simultaneous use of file system objects. +Therefore file system object polymorphism is a waste of CPU cycles. -試しに変えてみた。実際効率上がった。 -windrv.h内のFILESYS_FAST_STRUCTUREの値を変えてコンパイラの吐くソース -を比較すれば一目瞭然。何故私がこんな長文を書こうと思ったのかを理解で -きるはず。 +I made the change as an experiment. Performance did improve. +The improvement was obvious from looking at the source the compiler spit out +after changing the FILESYS_FAST_STRUCTURE value in windrv.h. +You may understand now why I decided to rant here. -一方ロシアはclass CWindrv内にclass CFileSysを直接設置した。 -(本当はclass CHostを廃止して直接置きたい……良い方法はないものか……) +The easy solution is to put the content of 'class CFileSys' into 'class CWindrv'. +(To be honest, I really want to deprecate 'class CHost'... I wonder if there's a good way...) */ class CFileSys { public: - // 基本ファンクション - CFileSys(); ///< デフォルトコンストラクタ - virtual ~CFileSys() {}; ///< デストラクタ + CFileSys(); + virtual ~CFileSys() {}; - // 初期化・終了 - void Reset(); ///< リセット (全クローズ) - void Init(); ///< 初期化 (デバイス起動とロード) + void Reset(); ///< Reset (close all) + void Init(); ///< Initialization (device startup and load) - // コマンドハンドラ - DWORD InitDevice(const Human68k::argument_t* pArgument); ///< $40 - デバイス起動 - int CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $41 - ディレクトリチェック - int MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $42 - ディレクトリ作成 - int RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $43 - ディレクトリ削除 + // Command handlers + DWORD InitDevice(const Human68k::argument_t* pArgument); ///< $40 - Device startup + int CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $41 - Directory check + int MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $42 - Create directory + int RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $43 - Delete directory int Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew); - ///< $44 - ファイル名変更 - int Delete(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $45 - ファイル削除 + ///< $44 - Change file name + int Delete(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $45 - Delete file int Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute); - ///< $46 - ファイル属性取得/設定 + ///< $46 - Get / set file attribute int Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles); - ///< $47 - ファイル検索 - int NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles); ///< $48 - ファイル次検索 + ///< $47 - Find file + int NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles); ///< $48 - Find next file int Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); - ///< $49 - ファイル作成 + ///< $49 - Create file int Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb); - ///< $4A - ファイルオープン - int Close(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb); ///< $4B - ファイルクローズ + ///< $4A - Open file + int Close(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb); ///< $4B - Close file int Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pAddress, DWORD nSize); - ///< $4C - ファイル読み込み + ///< $4C - Read file int Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pAddress, DWORD nSize); - ///< $4D - ファイル書き込み - int Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset); ///< $4E - ファイルシーク + ///< $4D - Write file + int Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset); ///< $4E - Seek file DWORD TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime); - ///< $4F - ファイル時刻取得/設定 - int GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); ///< $50 - 容量取得 - int CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive); ///< $51 - ドライブ状態検査/制御 - int GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb); ///< $52 - DPB取得 - int DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize); ///< $53 - セクタ読み込み - int DiskWrite(DWORD nUnit); ///< $54 - セクタ書き込み + ///< $4F - Get / set file timestamp + int GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); ///< $50 - Get capacity + int CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive); ///< $51 - Inspect / control drive status + int GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb); ///< $52 - Get DPB + int DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize); ///< $53 - Read sectors + int DiskWrite(DWORD nUnit); ///< $54 - Write sectors int Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl); ///< $55 - IOCTRL - int Flush(DWORD nUnit); ///< $56 - フラッシュ - int CheckMedia(DWORD nUnit); ///< $57 - メディア交換チェック - int Lock(DWORD nUnit); ///< $58 - 排他制御 + int Flush(DWORD nUnit); ///< $56 - Flush + int CheckMedia(DWORD nUnit); ///< $57 - Media change check + int Lock(DWORD nUnit); ///< $58 - Lock - void SetOption(DWORD nOption); ///< オプション設定 - DWORD GetOption() const { ASSERT(this); return m_nOption; } ///< オプション取得 - DWORD GetDefault() const { ASSERT(this); return m_nOptionDefault; } ///< デフォルトオプション取得 - static DWORD GetFileOption() { return g_nOption; } ///< ファイル名変換オプション取得 + void SetOption(DWORD nOption); ///< Set option + DWORD GetOption() const { return m_nOption; } ///< Get option + DWORD GetDefault() const { return m_nOptionDefault; } ///< Get default options + static DWORD GetFileOption() { return g_nOption; } ///< Get file name change option void ShellNotify(DWORD nEvent, const TCHAR* szPath) - { ASSERT(this); m_cEntry.ShellNotify(nEvent, szPath); } ///< ホスト側ファイルシステム状態変化通知 + { m_cEntry.ShellNotify(nEvent, szPath); } ///< Notify host side file system status change - /// 定数 enum { - DriveMax = CHostEntry::DriveMax ///< ドライブ最大候補数 + DriveMax = CHostEntry::DriveMax ///< Max number of drive candidates }; private: - // 内部補助用 - void InitOption(const Human68k::argument_t* pArgument); ///< オプション初期化 - BOOL FilesVolume(DWORD nUnit, Human68k::files_t* pFiles); ///< ボリュームラベル取得 + void InitOption(const Human68k::argument_t* pArgument); + BOOL FilesVolume(DWORD nUnit, Human68k::files_t* pFiles); ///< Get volume label - DWORD m_nUnits; ///< 現在のドライブオブジェクト数 (レジューム毎に変化) + DWORD m_nUnits; ///< Number of current drive objects (Changes for every resume) - DWORD m_nOption; ///< 現在の動作フラグ - DWORD m_nOptionDefault; ///< リセット時の動作フラグ + DWORD m_nOption; ///< Current runtime flag + DWORD m_nOptionDefault; ///< Runtime flag at reset - DWORD m_nDrives; ///< ベースパス状態復元用の候補数 (0なら毎回スキャン) + DWORD m_nDrives; ///< Number of candidates for base path status restoration (scan every time if 0) - DWORD m_nKernel; ///< カーネルチェック用カウンタ - DWORD m_nKernelSearch; ///< NULデバイスの先頭アドレス + DWORD m_nKernel; ///< Counter for kernel check + DWORD m_nKernelSearch; ///< Initial address for NUL device - DWORD m_nHostSectorCount; ///< 擬似セクタ番号 + DWORD m_nHostSectorCount; ///< Virtual sector identifier - CHostFilesManager m_cFiles; ///< ファイル検索領域 - CHostFcbManager m_cFcb; ///< FCB操作領域 - CHostEntry m_cEntry; ///< ドライブオブジェクトとディレクトリエントリ + CHostFilesManager m_cFiles; ///< File search memory + CHostFcbManager m_cFcb; ///< FCB operation memory + CHostEntry m_cEntry; ///< Drive object and directory entry DWORD m_nHostSectorBuffer[XM6_HOST_PSEUDO_CLUSTER_MAX]; - ///< 擬似セクタの指すファイル実体 + ///< Entity that the virtual sector points to - DWORD m_nFlag[DriveMax]; ///< ベースパス状態復元用の動作フラグ候補 - TCHAR m_szBase[DriveMax][FILEPATH_MAX]; ///< ベースパス状態復元用の候補 - static DWORD g_nOption; ///< ファイル名変換フラグ + DWORD m_nFlag[DriveMax]; ///< Candidate runtime flag for base path restoration + TCHAR m_szBase[DriveMax][FILEPATH_MAX]; ///< Candidate for base path restoration + static DWORD g_nOption; ///< File name change flag }; - -#endif // cfilesystem_h diff --git a/src/raspberrypi/devices/ctapdriver.cpp b/src/raspberrypi/devices/ctapdriver.cpp index e06e3eb0..f3942105 100644 --- a/src/raspberrypi/devices/ctapdriver.cpp +++ b/src/raspberrypi/devices/ctapdriver.cpp @@ -5,27 +5,45 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS +// Copyright (C) akuker // -// Imported NetBSD support and some optimisation patch by Rin Okuyama. +// Imported NetBSD support and some optimisation patches by Rin Okuyama. // // [ TAP Driver ] // //--------------------------------------------------------------------------- +#include +#include +#ifdef __linux__ +#include +#include +#include +#endif +#include // For crc32() #include "os.h" -#include "xm6.h" +#include "../rascsi.h" #include "ctapdriver.h" +#include "log.h" +#include "exceptions.h" +#include -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -CTapDriver::CTapDriver() +using namespace std; + +CTapDriver::CTapDriver(const string& interfaces) { + stringstream s(interfaces); + string interface; + while (getline(s, interface, ',')) { + this->interfaces.push_back(interface); + } + // Initialization + m_bTxValid = FALSE; m_hTAP = -1; memset(&m_MacAddr, 0, sizeof(m_MacAddr)); + m_pcap = NULL; + m_pcap_dumper = NULL; } //--------------------------------------------------------------------------- @@ -34,79 +52,322 @@ CTapDriver::CTapDriver() // //--------------------------------------------------------------------------- #ifdef __linux__ -BOOL FASTCALL CTapDriver::Init() -{ - char dev[IFNAMSIZ] = "ras0"; + +static BOOL br_setif(int br_socket_fd, const char* bridgename, const char* ifname, BOOL add) { struct ifreq ifr; - int ret; - - ASSERT(this); - - // TAP device initilization - if ((m_hTAP = open("/dev/net/tun", O_RDWR)) < 0) { - printf("Error: can't open tun\n"); + ifr.ifr_ifindex = if_nametoindex(ifname); + if (ifr.ifr_ifindex == 0) { + LOGERROR("Error: can't if_nametoindex. Errno: %d %s", errno, strerror(errno)); return FALSE; } + strncpy(ifr.ifr_name, bridgename, IFNAMSIZ); + if (ioctl(br_socket_fd, add ? SIOCBRADDIF : SIOCBRDELIF, &ifr) < 0) { + LOGERROR("Error: can't ioctl %s. Errno: %d %s", add ? "SIOCBRADDIF" : "SIOCBRDELIF", errno, strerror(errno)); + return FALSE; + } + return TRUE; +} +static BOOL ip_link(int fd, const char* ifname, BOOL up) { + struct ifreq ifr; + strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); // Need to save room for null terminator + int err = ioctl(fd, SIOCGIFFLAGS, &ifr); + if (err) { + LOGERROR("Error: can't ioctl SIOCGIFFLAGS. Errno: %d %s", errno, strerror(errno)); + return FALSE; + } + ifr.ifr_flags &= ~IFF_UP; + if (up) { + ifr.ifr_flags |= IFF_UP; + } + err = ioctl(fd, SIOCSIFFLAGS, &ifr); + if (err) { + LOGERROR("Error: can't ioctl SIOCSIFFLAGS. Errno: %d %s", errno, strerror(errno)); + return FALSE; + } + return TRUE; +} + +static bool is_interface_up(const string& interface) { + string file = "/sys/class/net/"; + file += interface; + file += "/carrier"; + + bool status = true; + FILE *fp = fopen(file.c_str(), "r"); + if (!fp || fgetc(fp) != '1') { + status = false; + } + + if (fp) { + fclose(fp); + } + + return status; +} + +bool CTapDriver::Init() +{ + LOGTRACE("Opening Tap device"); + // TAP device initilization + if ((m_hTAP = open("/dev/net/tun", O_RDWR)) < 0) { + LOGERROR("Error: can't open tun. Errno: %d %s", errno, strerror(errno)); + return false; + } + + LOGTRACE("Opened tap device %d",m_hTAP); + // IFF_NO_PI for no extra packet information + struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + char dev[IFNAMSIZ] = "ras0"; strncpy(ifr.ifr_name, dev, IFNAMSIZ); - if ((ret = ioctl(m_hTAP, TUNSETIFF, (void *)&ifr)) < 0) { - printf("Error: can't ioctl TUNSETIFF\n"); + + LOGTRACE("Going to open %s", ifr.ifr_name); + + int ret = ioctl(m_hTAP, TUNSETIFF, (void *)&ifr); + if (ret < 0) { + LOGERROR("Error: can't ioctl TUNSETIFF. Errno: %d %s", errno, strerror(errno)); + close(m_hTAP); - return FALSE; + return false; + } + + LOGTRACE("return code from ioctl was %d", ret); + + int ip_fd = socket(PF_INET, SOCK_DGRAM, 0); + if (ip_fd < 0) { + LOGERROR("Error: can't open ip socket. Errno: %d %s", errno, strerror(errno)); + + close(m_hTAP); + return false; + } + + int br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (br_socket_fd < 0) { + LOGERROR("Error: can't open bridge socket. Errno: %d %s", errno, strerror(errno)); + + close(m_hTAP); + close(ip_fd); + return false; + } + + // Check if the bridge has already been created + if (access("/sys/class/net/rascsi_bridge", F_OK)) { + LOGINFO("rascsi_bridge is not yet available"); + + LOGTRACE("Checking which interface is available for creating the bridge"); + + string bridge_interface; + string bridge_ip; + for (const string& iface : interfaces) { + string interface; + size_t separatorPos = iface.find(':'); + if (separatorPos != string::npos) { + interface = iface.substr(0, separatorPos); + bridge_ip = iface.substr(separatorPos + 1); + } + else { + interface = iface; + bridge_ip = "10.10.20.1/24"; + } + + ostringstream msg; + if (is_interface_up(interface)) { + msg << "Interface " << interface << " is up"; + LOGTRACE("%s", msg.str().c_str()); + + bridge_interface = interface; + break; + } + else { + msg << "Interface " << interface << " is not available or is not up"; + LOGTRACE("%s", msg.str().c_str()); + } + } + + if (bridge_interface.empty()) { + LOGERROR("No interface is up, not creating bridge"); + return false; + } + + LOGINFO("Creating rascsi_bridge for interface %s", bridge_interface.c_str()); + + if (bridge_interface == "eth0") { + LOGDEBUG("brctl addbr rascsi_bridge"); + + if ((ret = ioctl(br_socket_fd, SIOCBRADDBR, "rascsi_bridge")) < 0) { + LOGERROR("Error: can't ioctl SIOCBRADDBR. Errno: %d %s", errno, strerror(errno)); + + close(m_hTAP); + close(ip_fd); + close(br_socket_fd); + return false; + } + + LOGDEBUG("brctl addif rascsi_bridge %s", bridge_interface.c_str()); + + if (!br_setif(br_socket_fd, "rascsi_bridge", bridge_interface.c_str(), TRUE)) { + close(m_hTAP); + close(ip_fd); + close(br_socket_fd); + return false; + } + } + else { + LOGDEBUG("ip address add %s dev rascsi_bridge", bridge_ip.c_str()); + + string address = bridge_ip; + string netmask = "255.255.255.0"; + size_t separatorPos = bridge_ip.find('/'); + if (separatorPos != string::npos) { + address = bridge_ip.substr(0, separatorPos); + + string mask = bridge_ip.substr(separatorPos + 1); + if (mask == "8") { + netmask = "255.0.0.0"; + } + else if (mask == "16") { + netmask = "255.255.0.0"; + } + else if (mask == "24") { + netmask = "255.255.255.0"; + } + else { + LOGERROR("Error: Invalid netmask in %s", bridge_ip.c_str()); + + close(m_hTAP); + close(ip_fd); + close(br_socket_fd); + return false; + } + } + + LOGDEBUG("brctl addbr rascsi_bridge"); + + if ((ret = ioctl(br_socket_fd, SIOCBRADDBR, "rascsi_bridge")) < 0) { + LOGERROR("Error: can't ioctl SIOCBRADDBR. Errno: %d %s", errno, strerror(errno)); + + close(m_hTAP); + close(ip_fd); + close(br_socket_fd); + return false; + } + + LOGDEBUG("ip address add %s dev rascsi_bridge", bridge_ip.c_str()); + + struct ifreq ifr_a; + ifr_a.ifr_addr.sa_family = AF_INET; + strncpy(ifr_a.ifr_name, "rascsi_bridge", IFNAMSIZ); + struct sockaddr_in* addr = (struct sockaddr_in*)&ifr_a.ifr_addr; + inet_pton(AF_INET, address.c_str(), &addr->sin_addr); + struct ifreq ifr_n; + ifr_n.ifr_addr.sa_family = AF_INET; + strncpy(ifr_n.ifr_name, "rascsi_bridge", IFNAMSIZ); + struct sockaddr_in* mask = (struct sockaddr_in*)&ifr_n.ifr_addr; + inet_pton(AF_INET, netmask.c_str(), &mask->sin_addr); + if (ioctl(ip_fd, SIOCSIFADDR, &ifr_a) < 0 || ioctl(ip_fd, SIOCSIFNETMASK, &ifr_n) < 0) { + LOGERROR("Error: can't ioctl SIOCSIFADDR or SIOCSIFNETMASK. Errno: %d %s", errno, strerror(errno)); + + close(m_hTAP); + close(ip_fd); + close(br_socket_fd); + return false; + } + } + + LOGDEBUG("ip link set dev rascsi_bridge up"); + + if (!ip_link(ip_fd, "rascsi_bridge", TRUE)) { + close(m_hTAP); + close(ip_fd); + close(br_socket_fd); + return false; + } + } + else + { + LOGINFO("rascsi_bridge is already available"); + } + + LOGDEBUG("ip link set ras0 up"); + + if (!ip_link(ip_fd, "ras0", TRUE)) { + close(m_hTAP); + close(ip_fd); + close(br_socket_fd); + return false; + } + + LOGDEBUG("brctl addif rascsi_bridge ras0"); + + if (!br_setif(br_socket_fd, "rascsi_bridge", "ras0", TRUE)) { + close(m_hTAP); + close(ip_fd); + close(br_socket_fd); + return false; } // Get MAC address + LOGTRACE("Getting the MAC address"); + ifr.ifr_addr.sa_family = AF_INET; if ((ret = ioctl(m_hTAP, SIOCGIFHWADDR, &ifr)) < 0) { - printf("Error: can't ioctl SIOCGIFHWADDR\n"); + LOGERROR("Error: can't ioctl SIOCGIFHWADDR. Errno: %d %s", errno, strerror(errno)); + close(m_hTAP); - return FALSE; + close(ip_fd); + close(br_socket_fd); + return false; } + LOGTRACE("Got the MAC"); // Save MAC address memcpy(m_MacAddr, ifr.ifr_hwaddr.sa_data, sizeof(m_MacAddr)); - return TRUE; + + close(ip_fd); + close(br_socket_fd); + + LOGINFO("Tap device %s created", ifr.ifr_name); + + return true; } #endif // __linux__ #ifdef __NetBSD__ -BOOL FASTCALL CTapDriver::Init() +BOOL CTapDriver::Init() { struct ifreq ifr; struct ifaddrs *ifa, *a; - ASSERT(this); - // TAP Device Initialization if ((m_hTAP = open("/dev/tap", O_RDWR)) < 0) { - printf("Error: can't open tap\n"); - return FALSE; + LOGERROR("Error: can't open tap. Errno: %d %s", errno, strerror(errno)); + return false; } // Get device name if (ioctl(m_hTAP, TAPGIFNAME, (void *)&ifr) < 0) { - printf("Error: can't ioctl TAPGIFNAME\n"); + LOGERROR("Error: can't ioctl TAPGIFNAME. Errno: %d %s", errno, strerror(errno)); close(m_hTAP); - return FALSE; + return false; } // Get MAC address if (getifaddrs(&ifa) == -1) { - printf("Error: can't getifaddrs\n"); + LOGERROR("Error: can't getifaddrs. Errno: %d %s", errno, strerror(errno)); close(m_hTAP); - return FALSE; + return false; } for (a = ifa; a != NULL; a = a->ifa_next) if (strcmp(ifr.ifr_name, a->ifa_name) == 0 && a->ifa_addr->sa_family == AF_LINK) break; if (a == NULL) { - printf("Error: can't get MAC address\n"); + LOGERROR("Error: can't get MAC addressErrno: %d %s", errno, strerror(errno)); close(m_hTAP); - return FALSE; + return false; } // Save MAC address @@ -114,26 +375,101 @@ BOOL FASTCALL CTapDriver::Init() sizeof(m_MacAddr)); freeifaddrs(ifa); - printf("Tap device : %s\n", ifr.ifr_name); + LOGINFO("Tap device : %s\n", ifr.ifr_name); - return TRUE; + return true; } #endif // __NetBSD__ +void CTapDriver::OpenDump(const Filepath& path) { + if (m_pcap == NULL) { + m_pcap = pcap_open_dead(DLT_EN10MB, 65535); + } + if (m_pcap_dumper != NULL) { + pcap_dump_close(m_pcap_dumper); + } + m_pcap_dumper = pcap_dump_open(m_pcap, path.GetPath()); + if (m_pcap_dumper == NULL) { + LOGERROR("Error: can't open pcap file: %s", pcap_geterr(m_pcap)); + throw io_exception("Can't open pcap file"); + } + + LOGTRACE("%s Opened %s for dumping", __PRETTY_FUNCTION__, path.GetPath()); +} + //--------------------------------------------------------------------------- // // Cleanup // //--------------------------------------------------------------------------- -void FASTCALL CTapDriver::Cleanup() +void CTapDriver::Cleanup() { - ASSERT(this); + int br_socket_fd = -1; + if ((br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) { + LOGERROR("Error: can't open bridge socket. Errno: %d %s", errno, strerror(errno)); + } else { + LOGDEBUG("brctl delif rascsi_bridge ras0"); + if (!br_setif(br_socket_fd, "rascsi_bridge", "ras0", FALSE)) { + LOGWARN("Warning: Removing ras0 from the bridge failed."); + LOGWARN("You may need to manually remove the ras0 tap device from the bridge"); + } + close(br_socket_fd); + } - // TAPデバイス解放 + // Release TAP defice if (m_hTAP != -1) { close(m_hTAP); m_hTAP = -1; } + + if (m_pcap_dumper != NULL) { + pcap_dump_close(m_pcap_dumper); + m_pcap_dumper = NULL; + } + + if (m_pcap != NULL) { + pcap_close(m_pcap); + m_pcap = NULL; + } +} + +//--------------------------------------------------------------------------- +// +// Enable +// +//--------------------------------------------------------------------------- +bool CTapDriver::Enable(){ + int fd = socket(PF_INET, SOCK_DGRAM, 0); + LOGDEBUG("%s: ip link set ras0 up", __PRETTY_FUNCTION__); + bool result = ip_link(fd, "ras0", TRUE); + close(fd); + return result; +} + +//--------------------------------------------------------------------------- +// +// Disable +// +//--------------------------------------------------------------------------- +bool CTapDriver::Disable(){ + int fd = socket(PF_INET, SOCK_DGRAM, 0); + LOGDEBUG("%s: ip link set ras0 down", __PRETTY_FUNCTION__); + bool result = ip_link(fd, "ras0", FALSE); + close(fd); + return result; +} + +//--------------------------------------------------------------------------- +// +// Flush +// +//--------------------------------------------------------------------------- +BOOL CTapDriver::Flush(){ + LOGTRACE("%s", __PRETTY_FUNCTION__); + while(PendingPackets()){ + (void)Rx(m_garbage_buffer); + } + return TRUE; } //--------------------------------------------------------------------------- @@ -141,9 +477,8 @@ void FASTCALL CTapDriver::Cleanup() // MGet MAC Address // //--------------------------------------------------------------------------- -void FASTCALL CTapDriver::GetMacAddr(BYTE *mac) +void CTapDriver::GetMacAddr(BYTE *mac) { - ASSERT(this); ASSERT(mac); memcpy(mac, m_MacAddr, sizeof(m_MacAddr)); @@ -154,12 +489,10 @@ void FASTCALL CTapDriver::GetMacAddr(BYTE *mac) // Receive // //--------------------------------------------------------------------------- -int FASTCALL CTapDriver::Rx(BYTE *buf) +BOOL CTapDriver::PendingPackets() { struct pollfd fds; - DWORD dwReceived; - ASSERT(this); ASSERT(m_hTAP != -1); // Check if there is data that can be received @@ -167,29 +500,67 @@ int FASTCALL CTapDriver::Rx(BYTE *buf) fds.events = POLLIN | POLLERR; fds.revents = 0; poll(&fds, 1, 0); + LOGTRACE("%s %u revents", __PRETTY_FUNCTION__, fds.revents); if (!(fds.revents & POLLIN)) { + return FALSE; + }else { + return TRUE; + } +} + +//--------------------------------------------------------------------------- +// +// Receive +// +//--------------------------------------------------------------------------- +int CTapDriver::Rx(BYTE *buf) +{ + ASSERT(m_hTAP != -1); + + // Check if there is data that can be received + if(!PendingPackets()){ return 0; } // Receive - dwReceived = read(m_hTAP, buf, ETH_FRAME_LEN); + DWORD dwReceived = read(m_hTAP, buf, ETH_FRAME_LEN); if (dwReceived == (DWORD)-1) { + LOGWARN("%s Error occured while receiving an packet", __PRETTY_FUNCTION__); return 0; } // If reception is enabled if (dwReceived > 0) { - // Pad to the maximum frame size (60 bytes) excluding FCS - if (dwReceived < 60) { - memset(buf + dwReceived, 0, 60 - dwReceived); - dwReceived = 60; - } + // We need to add the Frame Check Status (FCS) CRC back onto the end of the packet. + // The Linux network subsystem removes it, since most software apps shouldn't ever + // need it. - // Add a dummy FCS - memset(buf + dwReceived, 0, 4); + // Initialize the CRC + DWORD crc = crc32(0L, Z_NULL, 0); + // Calculate the CRC + crc = crc32(crc, buf, dwReceived); + + buf[dwReceived + 0] = (BYTE)((crc >> 0) & 0xFF); + buf[dwReceived + 1] = (BYTE)((crc >> 8) & 0xFF); + buf[dwReceived + 2] = (BYTE)((crc >> 16) & 0xFF); + buf[dwReceived + 3] = (BYTE)((crc >> 24) & 0xFF); + + LOGDEBUG("%s CRC is %08X - %02X %02X %02X %02X\n", __PRETTY_FUNCTION__, crc, buf[dwReceived+0], buf[dwReceived+1], buf[dwReceived+2], buf[dwReceived+3]); + + // Add FCS size to the received message size dwReceived += 4; } + if (m_pcap_dumper != NULL) { + struct pcap_pkthdr h = { + .caplen = dwReceived, + .len = dwReceived, + }; + gettimeofday(&h.ts, NULL); + pcap_dump((u_char*)m_pcap_dumper, &h, buf); + LOGTRACE("%s Dumped %d byte packet (first byte: %02x last byte: %02x)", __PRETTY_FUNCTION__, (unsigned int)dwReceived, buf[0], buf[dwReceived-1]); + } + // Return the number of bytes return dwReceived; } @@ -199,11 +570,20 @@ int FASTCALL CTapDriver::Rx(BYTE *buf) // Send // //--------------------------------------------------------------------------- -int FASTCALL CTapDriver::Tx(BYTE *buf, int len) +int CTapDriver::Tx(const BYTE *buf, int len) { - ASSERT(this); ASSERT(m_hTAP != -1); + if (m_pcap_dumper != NULL) { + struct pcap_pkthdr h = { + .caplen = (bpf_u_int32)len, + .len = (bpf_u_int32)len, + }; + gettimeofday(&h.ts, NULL); + pcap_dump((u_char*)m_pcap_dumper, &h, buf); + LOGTRACE("%s Dumped %d byte packet (first byte: %02x last byte: %02x)", __PRETTY_FUNCTION__, (unsigned int)h.len, buf[0], buf[h.len-1]); + } + // Start sending return write(m_hTAP, buf, len); } diff --git a/src/raspberrypi/devices/ctapdriver.h b/src/raspberrypi/devices/ctapdriver.h index 9122a2d6..1d4f4c6f 100644 --- a/src/raspberrypi/devices/ctapdriver.h +++ b/src/raspberrypi/devices/ctapdriver.h @@ -5,6 +5,7 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS +// Copyright (C) akuker // // Imported NetBSD support and some optimisation patch by Rin Okuyama. // @@ -12,8 +13,12 @@ // //--------------------------------------------------------------------------- -#if !defined(ctapdriver_h) -#define ctapdriver_h +#pragma once + +#include +#include "filepath.h" +#include +#include #ifndef ETH_FRAME_LEN #define ETH_FRAME_LEN 1514 @@ -27,18 +32,32 @@ class CTapDriver { public: - // Basic Functionality - CTapDriver(); // Constructor - BOOL FASTCALL Init(); // Initilization - void FASTCALL Cleanup(); // Cleanup - void FASTCALL GetMacAddr(BYTE *mac); // Get Mac Address - int FASTCALL Rx(BYTE *buf); // Receive - int FASTCALL Tx(BYTE *buf, int len); // Send + CTapDriver(const std::string&); + ~CTapDriver() {}; + + bool Init(); + void OpenDump(const Filepath& path); + // Capture packets + void Cleanup(); // Cleanup + void GetMacAddr(BYTE *mac); // Get Mac Address + int Rx(BYTE *buf); // Receive + int Tx(const BYTE *buf, int len); // Send + BOOL PendingPackets(); // Check if there are IP packets available + bool Enable(); // Enable the ras0 interface + bool Disable(); // Disable the ras0 interface + BOOL Flush(); // Purge all of the packets that are waiting to be processed private: BYTE m_MacAddr[6]; // MAC Address BOOL m_bTxValid; // Send Valid Flag int m_hTAP; // File handle + + BYTE m_garbage_buffer[ETH_FRAME_LEN]; + + pcap_t *m_pcap; + pcap_dumper_t *m_pcap_dumper; + + // Prioritized comma-separated list of interfaces to create the bridge for + std::vector interfaces; }; -#endif // ctapdriver_h diff --git a/src/raspberrypi/devices/device.cpp b/src/raspberrypi/devices/device.cpp new file mode 100644 index 00000000..2c9f3275 --- /dev/null +++ b/src/raspberrypi/devices/device.cpp @@ -0,0 +1,162 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include +#include +#include "rascsi_version.h" +#include "os.h" +#include "log.h" +#include "exceptions.h" +#include "device.h" + +Device::Device(const string& type) +{ + assert(type.length() == 4); + + this->type = type; + + vendor = DEFAULT_VENDOR; + char rev[5]; + sprintf(rev, "%02d%02d", rascsi_major_version, rascsi_minor_version); + revision = rev; + + ready = false; + reset = false; + attn = false; + supported_luns = 1; + protectable = false; + write_protected = false; + read_only = false; + stoppable = false; + stopped = false; + removable = false; + removed = false; + lockable = false; + locked = false; + block_size_configurable = false; + supports_params = false; + + id = 0; + lun = 0; + + status_code = STATUS_NOERROR; +} + +void Device::Reset() +{ + locked = false; + attn = false; + reset = false; +} + +void Device::SetProtected(bool write_protected) +{ + if (!read_only) { + this->write_protected = write_protected; + } +} + +void Device::SetVendor(const string& vendor) +{ + if (vendor.empty() || vendor.length() > 8) { + throw illegal_argument_exception("Vendor '" + vendor + "' must be between 1 and 8 characters"); + } + + this->vendor = vendor; +} + +void Device::SetProduct(const string& product, bool force) +{ + // Changing the device name is not SCSI compliant + if (!this->product.empty() && !force) { + return; + } + + if (product.empty() || product.length() > 16) { + throw illegal_argument_exception("Product '" + product + "' must be between 1 and 16 characters"); + } + + this->product = product; +} + +void Device::SetRevision(const string& revision) +{ + if (revision.empty() || revision.length() > 4) { + throw illegal_argument_exception("Revision '" + revision + "' must be between 1 and 4 characters"); + } + + this->revision = revision; +} + +const string Device::GetPaddedName() const +{ + string name = vendor; + name.append(8 - vendor.length(), ' '); + name += product; + name.append(16 - product.length(), ' '); + name += revision; + name.append(4 - revision.length(), ' '); + + assert(name.length() == 28); + + return name; +} + +const string Device::GetParam(const string& key) +{ + return params.find(key) != params.end() ? params[key] : ""; +} + +void Device::SetStatusCode(int status_code) +{ + if (status_code) { + LOGDEBUG("Error status: Sense Key $%02X, ASC $%02X, ASCQ $%02X", status_code >> 16, (status_code >> 8 &0xff), status_code & 0xff); + } + + this->status_code = status_code; +} + +bool Device::Start() +{ + if (!ready) { + return false; + } + + stopped = false; + + return true; +} + +void Device::Stop() +{ + ready = false; + attn = false; + stopped = true; +} + +bool Device::Eject(bool force) +{ + if (!ready || !removable) { + return false; + } + + // Must be unlocked if there is no force flag + if (!force && locked) { + return false; + } + + ready = false; + attn = false; + removed = true; + write_protected = false; + locked = false; + stopped = true; + + return true; +} diff --git a/src/raspberrypi/devices/device.h b/src/raspberrypi/devices/device.h new file mode 100644 index 00000000..f08372a9 --- /dev/null +++ b/src/raspberrypi/devices/device.h @@ -0,0 +1,185 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include +#include + +using namespace std; + +#define DEFAULT_VENDOR "RaSCSI" + +//--------------------------------------------------------------------------- +// +// Error definition (sense code returned by REQUEST SENSE) +// +// MSB Reserved (0x00) +// Sense Key +// Additional Sense Code (ASC) +// LSB Additional Sense Code Qualifier(ASCQ) +// +//--------------------------------------------------------------------------- +#define STATUS_NOERROR 0x00000000 // NO ADDITIONAL SENSE INFO. +#define STATUS_DEVRESET 0x00062900 // POWER ON OR RESET OCCURED +#define STATUS_NOTREADY 0x00023a00 // MEDIUM NOT PRESENT +#define STATUS_ATTENTION 0x00062800 // MEDIUM MAY HAVE CHANGED +#define STATUS_PREVENT 0x00045302 // MEDIUM REMOVAL PREVENTED +#define STATUS_READFAULT 0x00031100 // UNRECOVERED READ ERROR +#define STATUS_WRITEFAULT 0x00030300 // PERIPHERAL DEVICE WRITE FAULT +#define STATUS_WRITEPROTECT 0x00042700 // WRITE PROTECTED +#define STATUS_MISCOMPARE 0x000e1d00 // MISCOMPARE DURING VERIFY +#define STATUS_INVALIDCMD 0x00052000 // INVALID COMMAND OPERATION CODE +#define STATUS_INVALIDLBA 0x00052100 // LOGICAL BLOCK ADDR. OUT OF RANGE +#define STATUS_INVALIDCDB 0x00052400 // INVALID FIELD IN CDB +#define STATUS_INVALIDLUN 0x00052500 // LOGICAL UNIT NOT SUPPORTED +#define STATUS_INVALIDPRM 0x00052600 // INVALID FIELD IN PARAMETER LIST +#define STATUS_INVALIDMSG 0x00054900 // INVALID MESSAGE ERROR +#define STATUS_PARAMLEN 0x00051a00 // PARAMETERS LIST LENGTH ERROR +#define STATUS_PARAMNOT 0x00052601 // PARAMETERS NOT SUPPORTED +#define STATUS_PARAMVALUE 0x00052602 // PARAMETERS VALUE INVALID +#define STATUS_PARAMSAVE 0x00053900 // SAVING PARAMETERS NOT SUPPORTED +#define STATUS_NODEFECT 0x00010000 // DEFECT LIST NOT FOUND + +class SCSIDEV; + +class Device +{ +private: + + string type; + + bool ready; + bool reset; + bool attn; + + // Number of supported luns + int supported_luns; + + // Device is protectable/write-protected + bool protectable; + bool write_protected; + // Device is permanently read-only + bool read_only; + + // Device can be stopped (parked)/is stopped (parked) + bool stoppable; + bool stopped; + + // Device is removable/removed + bool removable; + bool removed; + + // Device is lockable/locked + bool lockable; + bool locked; + + // The block size is configurable + bool block_size_configurable; + + // Device can be created with parameters + bool supports_params; + + // Device ID and LUN + int32_t id; + int32_t lun; + + // Device identifier (for INQUIRY) + string vendor; + string product; + string revision; + + // The parameters the device was created with + map params; + + // The default parameters + map default_params; + + // Sense Key, ASC and ASCQ + int status_code; + +public: + + Device(const string&); + virtual ~Device() {}; + + // Override for device specific initializations, to be called after all device properties have been set + virtual bool Init(const map&) { return true; }; + + virtual bool Dispatch(SCSIDEV *) = 0; + + const string& GetType() const { return type; } + + bool IsReady() const { return ready; } + void SetReady(bool ready) { this->ready = ready; } + bool IsReset() const { return reset; } + void SetReset(bool reset) { this->reset = reset; } + void Reset(); + bool IsAttn() const { return attn; } + void SetAttn(bool attn) { this->attn = attn; } + + int GetSupportedLuns() const { return supported_luns; } + void SetSupportedLuns(int supported_luns) { this->supported_luns = supported_luns; } + + bool IsProtectable() const { return protectable; } + void SetProtectable(bool protectable) { this->protectable = protectable; } + bool IsProtected() const { return write_protected; } + void SetProtected(bool); + bool IsReadOnly() const { return read_only; } + void SetReadOnly(bool read_only) { this->read_only = read_only; } + + bool IsStoppable() const { return stoppable; } + void SetStoppable(bool stoppable) { this->stoppable = stoppable; } + bool IsStopped() const { return stopped; } + void SetStopped(bool stopped) { this->stopped = stopped; } + bool IsRemovable() const { return removable; } + void SetRemovable(bool removable) { this->removable = removable; } + bool IsRemoved() const { return removed; } + void SetRemoved(bool removed) { this->removed = removed; } + + bool IsLockable() const { return lockable; } + void SetLockable(bool lockable) { this->lockable = lockable; } + bool IsLocked() const { return locked; } + void SetLocked(bool locked) { this->locked = locked; } + + int32_t GetId() const { return id; } + void SetId(int32_t id) { this->id = id; } + int32_t GetLun() const { return lun; } + void SetLun(int32_t lun) { this->lun = lun; } + + const string GetVendor() const { return vendor; } + void SetVendor(const string&); + const string GetProduct() const { return product; } + void SetProduct(const string&, bool = true); + const string GetRevision() const { return revision; } + void SetRevision(const string&); + const string GetPaddedName() const; + + bool SupportsParams() const { return supports_params; } + void SupportsParams(bool supports_paams) { this->supports_params = supports_paams; } + const map GetParams() const { return params; } + const string GetParam(const string&); + void SetParams(const map& params) { this->params = params; } + const map GetDefaultParams() const { return default_params; } + void SetDefaultParams(const map& default_params) { this->default_params = default_params; } + + int GetStatusCode() const { return status_code; } + void SetStatusCode(int); + + bool Start(); + void Stop(); + virtual bool Eject(bool); + + bool IsSASIHD() const { return type == "SAHD"; } + bool IsSCSIHD() const { return type == "SCHD" || type == "SCRM"; } + bool IsCdRom() const { return type == "SCCD"; } + bool IsMo() const { return type == "SCMO"; } + bool IsBridge() const { return type == "SCBR"; } + bool IsDaynaPort() const { return type == "SCDP"; } +}; diff --git a/src/raspberrypi/devices/device_factory.cpp b/src/raspberrypi/devices/device_factory.cpp new file mode 100644 index 00000000..a1b420a1 --- /dev/null +++ b/src/raspberrypi/devices/device_factory.cpp @@ -0,0 +1,274 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "sasihd.h" +#include "scsihd.h" +#include "scsihd_nec.h" +#include "scsimo.h" +#include "scsicd.h" +#include "scsi_host_bridge.h" +#include "scsi_daynaport.h" +#include "exceptions.h" +#include "device_factory.h" +#include +#include +#include + +using namespace std; +using namespace rascsi_interface; + +DeviceFactory::DeviceFactory() +{ + sector_sizes[SAHD] = { 256, 1024 }; + sector_sizes[SCHD] = { 512, 1024, 2048, 4096 }; + sector_sizes[SCRM] = { 512, 1024, 2048, 4096 }; + sector_sizes[SCMO] = { 512, 1024, 2048, 4096 }; + // Some old Sun CD-ROM drives support 512 bytes per sector + sector_sizes[SCCD] = { 512, 2048}; + sector_sizes[SCBR] = {}; + sector_sizes[SCDP] = {}; + + // 128 MB, 512 bytes per sector, 248826 sectors + geometries[SCMO][0x797f400] = make_pair(512, 248826); + // 230 MB, 512 bytes per block, 446325 sectors + geometries[SCMO][0xd9eea00] = make_pair(512, 446325); + // 540 MB, 512 bytes per sector, 1041500 sectors + geometries[SCMO][0x1fc8b800] = make_pair(512, 1041500); + // 640 MB, 20248 bytes per sector, 310352 sectors + geometries[SCMO][0x25e28000] = make_pair(2048, 310352); + geometries[SAHD] = {}; + geometries[SCHD] = {}; + geometries[SCRM] = {}; + geometries[SCCD] = {}; + geometries[SCBR] = {}; + geometries[SCDP] = {}; + + string network_interfaces; + for (const auto& network_interface : GetNetworkInterfaces()) { + if (!network_interfaces.empty()) { + network_interfaces += ","; + } + network_interfaces += network_interface; + } + + default_params[SAHD] = {}; + default_params[SCHD] = {}; + default_params[SCRM] = {}; + default_params[SCMO] = {}; + default_params[SCCD] = {}; + default_params[SCBR]["interfaces"] = network_interfaces; + default_params[SCDP]["interfaces"] = network_interfaces; + + extension_mapping["hdf"] = SAHD; + extension_mapping["hds"] = SCHD; + extension_mapping["hda"] = SCHD; + extension_mapping["hdn"] = SCHD; + extension_mapping["hdi"] = SCHD; + extension_mapping["nhd"] = SCHD; + extension_mapping["hdr"] = SCRM; + extension_mapping["mos"] = SCMO; + extension_mapping["iso"] = SCCD; +} + +DeviceFactory& DeviceFactory::instance() +{ + static DeviceFactory instance; + return instance; +} + +string DeviceFactory::GetExtension(const string& filename) const +{ + string ext; + size_t separator = filename.rfind('.'); + if (separator != string::npos) { + ext = filename.substr(separator + 1); + } + std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){ return std::tolower(c); }); + + return ext; +} + +PbDeviceType DeviceFactory::GetTypeForFile(const string& filename) +{ + string ext = GetExtension(filename); + + if (extension_mapping.find(ext) != extension_mapping.end()) { + return extension_mapping[ext]; + } + else if (filename == "bridge") { + return SCBR; + } + else if (filename == "daynaport") { + return SCDP; + } + + return UNDEFINED; +} + +Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename) +{ + // If no type was specified try to derive the device type from the filename + if (type == UNDEFINED) { + type = GetTypeForFile(filename); + if (type == UNDEFINED) { + return NULL; + } + } + + Device *device = NULL; + try { + switch (type) { + case SAHD: + device = new SASIHD(); + device->SetSupportedLuns(2); + device->SetProduct("SASI HD"); + ((Disk *)device)->SetSectorSizes(sector_sizes[SAHD]); + break; + + case SCHD: { + string ext = GetExtension(filename); + if (ext == "hdn" || ext == "hdi" || ext == "nhd") { + device = new SCSIHD_NEC(); + ((Disk *)device)->SetSectorSizes({ 512 }); + } else { + device = new SCSIHD(false); + ((Disk *)device)->SetSectorSizes(sector_sizes[SCHD]); + + // Some Apple tools require a particular drive identification + if (ext == "hda") { + device->SetVendor("QUANTUM"); + device->SetProduct("FIREBALL"); + } + } + device->SetSupportedLuns(32); + device->SetProtectable(true); + device->SetStoppable(true); + break; + } + + case SCRM: + device = new SCSIHD(true); + device->SetSupportedLuns(32); + device->SetProtectable(true); + device->SetStoppable(true); + device->SetRemovable(true); + device->SetLockable(true); + device->SetProduct("SCSI HD (REM.)"); + ((Disk *)device)->SetSectorSizes(sector_sizes[SCRM]); + break; + + case SCMO: + device = new SCSIMO(); + device->SetSupportedLuns(32); + device->SetProtectable(true); + device->SetStoppable(true); + device->SetRemovable(true); + device->SetLockable(true); + device->SetProduct("SCSI MO"); + ((Disk *)device)->SetSectorSizes(sector_sizes[SCRM]); + ((Disk *)device)->SetGeometries(geometries[SCMO]); + break; + + case SCCD: + device = new SCSICD(); + device->SetSupportedLuns(32); + device->SetReadOnly(true); + device->SetStoppable(true); + device->SetRemovable(true); + device->SetLockable(true); + device->SetProduct("SCSI CD-ROM"); + ((Disk *)device)->SetSectorSizes(sector_sizes[SCCD]); + break; + + case SCBR: + device = new SCSIBR(); + device->SetSupportedLuns(1); + device->SetProduct("SCSI HOST BRIDGE"); + device->SupportsParams(true); + device->SetDefaultParams(default_params[SCBR]); + break; + + case SCDP: + device = new SCSIDaynaPort(); + device->SetSupportedLuns(1); + // Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly + device->SetVendor("Dayna"); + device->SetProduct("SCSI/Link"); + device->SetRevision("1.4a"); + device->SupportsParams(true); + device->SetDefaultParams(default_params[SCDP]); + break; + + default: + break; + } + } + catch(const illegal_argument_exception& e) { + // There was an internal problem with setting up the device data for INQUIRY + return NULL; + } + + return device; +} + +const set& DeviceFactory::GetSectorSizes(const string& type) +{ + PbDeviceType t = UNDEFINED; + PbDeviceType_Parse(type, &t); + return sector_sizes[t]; +} + +const set DeviceFactory::GetCapacities(PbDeviceType type) +{ + set keys; + + for (const auto& geometry : geometries[type]) { + keys.insert(geometry.first); + } + + return keys; +} + +const list DeviceFactory::GetNetworkInterfaces() const +{ + list network_interfaces; + + struct ifaddrs *addrs; + getifaddrs(&addrs); + struct ifaddrs *tmp = addrs; + + while (tmp) { + if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET && + strcmp(tmp->ifa_name, "lo") && strcmp(tmp->ifa_name, "rascsi_bridge")) { + int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + + strcpy(ifr.ifr_name, tmp->ifa_name); + if (!ioctl(fd, SIOCGIFFLAGS, &ifr)) { + close(fd); + + // Only list interfaces that are up + if (ifr.ifr_flags & IFF_UP) { + network_interfaces.push_back(tmp->ifa_name); + } + } + else { + close(fd); + } + } + + tmp = tmp->ifa_next; + } + + freeifaddrs(addrs); + + return network_interfaces; +} diff --git a/src/raspberrypi/devices/device_factory.h b/src/raspberrypi/devices/device_factory.h new file mode 100644 index 00000000..d6f73f63 --- /dev/null +++ b/src/raspberrypi/devices/device_factory.h @@ -0,0 +1,58 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +// The DeviceFactory singleton creates devices based on their type and the image file extension +// +//--------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include +#include "rascsi_interface.pb.h" + +using namespace std; +using namespace rascsi_interface; + +typedef pair Geometry; + +class Device; + +class DeviceFactory +{ +private: + DeviceFactory(); + ~DeviceFactory() {}; + +public: + + static DeviceFactory& instance(); + + Device *CreateDevice(PbDeviceType, const string&); + PbDeviceType GetTypeForFile(const string&); + const set& GetSectorSizes(PbDeviceType type) { return sector_sizes[type]; } + const set& GetSectorSizes(const string&); + const set GetCapacities(PbDeviceType); + const map& GetDefaultParams(PbDeviceType type) { return default_params[type]; } + const list GetNetworkInterfaces() const; + const map GetExtensionMapping() const { return extension_mapping; } + +private: + + map> sector_sizes; + + // Optional mapping of drive capacities to drive geometries + map> geometries; + + map> default_params; + + map extension_mapping; + + string GetExtension(const string&) const; +}; diff --git a/src/raspberrypi/devices/disk.cpp b/src/raspberrypi/devices/disk.cpp index f608e7e3..7a94f250 100644 --- a/src/raspberrypi/devices/disk.cpp +++ b/src/raspberrypi/devices/disk.cpp @@ -13,789 +13,64 @@ // Imported NetBSD support and some optimisation patch by Rin Okuyama. // Comments translated to english by akuker. // -// [ Disk ] -// //--------------------------------------------------------------------------- #include "os.h" -#include "xm6.h" -#include "filepath.h" -#include "fileio.h" -#ifdef RASCSI -#include "gpiobus.h" -#ifndef BAREMETAL -#include "ctapdriver.h" -#endif // BAREMETAL -#include "cfilesystem.h" +#include "controllers/sasidev_ctrl.h" +#include "device_factory.h" +#include "exceptions.h" #include "disk.h" -#else -#include "vm.h" -#include "disk.h" -#include "windrv.h" -#include "ctapdriver.h" -#include "mfc_com.h" -#include "mfc_host.h" -#endif // RASCSI +#include +#include "../rascsi.h" -//=========================================================================== -// -// Disk -// -//=========================================================================== - - -//=========================================================================== -// -// Disk Track -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -DiskTrack::DiskTrack() -{ - // Initialization of internal information - dt.track = 0; - dt.size = 0; - dt.sectors = 0; - dt.raw = FALSE; - dt.init = FALSE; - dt.changed = FALSE; - dt.length = 0; - dt.buffer = NULL; - dt.maplen = 0; - dt.changemap = NULL; - dt.imgoffset = 0; -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -DiskTrack::~DiskTrack() -{ - // Release memory, but do not save automatically - if (dt.buffer) { - free(dt.buffer); - dt.buffer = NULL; - } - if (dt.changemap) { - free(dt.changemap); - dt.changemap = NULL; - } -} - -//--------------------------------------------------------------------------- -// -// Initialization -// -//--------------------------------------------------------------------------- -void FASTCALL DiskTrack::Init( - int track, int size, int sectors, BOOL raw, off64_t imgoff) -{ - ASSERT(track >= 0); - ASSERT((size >= 8) && (size <= 11)); - ASSERT((sectors > 0) && (sectors <= 0x100)); - ASSERT(imgoff >= 0); - - // Set Parameters - dt.track = track; - dt.size = size; - dt.sectors = sectors; - dt.raw = raw; - - // Not initialized (needs to be loaded) - dt.init = FALSE; - - // Not Changed - dt.changed = FALSE; - - // Offset to actual data - dt.imgoffset = imgoff; -} - -//--------------------------------------------------------------------------- -// -// Load -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskTrack::Load(const Filepath& path) -{ - Fileio fio; - off64_t offset; - int i; - int length; - - ASSERT(this); - - // Not needed if already loaded - if (dt.init) { - ASSERT(dt.buffer); - ASSERT(dt.changemap); - return TRUE; - } - - // Calculate offset (previous tracks are considered to - // hold 256 sectors) - offset = ((off64_t)dt.track << 8); - if (dt.raw) { - ASSERT(dt.size == 11); - offset *= 0x930; - offset += 0x10; - } else { - offset <<= dt.size; - } - - // Add offset to real image - offset += dt.imgoffset; - - // Calculate length (data size of this track) - length = dt.sectors << dt.size; - - // Allocate buffer memory - ASSERT((dt.size >= 8) && (dt.size <= 11)); - ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); - - if (dt.buffer == NULL) { - #if defined(RASCSI) && !defined(BAREMETAL) - posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512); - #else - dt.buffer = (BYTE *)malloc(length * sizeof(BYTE)); - #endif // RASCSI && !BAREMETAL - dt.length = length; - } - - if (!dt.buffer) { - return FALSE; - } - - // Reallocate if the buffer length is different - if (dt.length != (DWORD)length) { - free(dt.buffer); - #if defined(RASCSI) && !defined(BAREMETAL) - posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512); - #else - dt.buffer = (BYTE *)malloc(length * sizeof(BYTE)); - #endif // RASCSI && !BAREMETAL - dt.length = length; - } - - // Reserve change map memory - if (dt.changemap == NULL) { - dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL)); - dt.maplen = dt.sectors; - } - - if (!dt.changemap) { - return FALSE; - } - - // Reallocate if the buffer length is different - if (dt.maplen != (DWORD)dt.sectors) { - free(dt.changemap); - dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL)); - dt.maplen = dt.sectors; - } - - // Clear changemap - memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL)); - - // Read from File - #if defined(RASCSI) && !defined(BAREMETAL) - if (!fio.OpenDIO(path, Fileio::ReadOnly)) { - #else - if (!fio.Open(path, Fileio::ReadOnly)) { - #endif // RASCSI && !BAREMETAL - return FALSE; - } - if (dt.raw) { - // Split Reading - for (i = 0; i < dt.sectors; i++) { - // Seek - if (!fio.Seek(offset)) { - fio.Close(); - return FALSE; - } - - // Read - if (!fio.Read(&dt.buffer[i << dt.size], 1 << dt.size)) { - fio.Close(); - return FALSE; - } - - // Next offset - offset += 0x930; - } - } else { - // Continuous reading - if (!fio.Seek(offset)) { - fio.Close(); - return FALSE; - } - if (!fio.Read(dt.buffer, length)) { - fio.Close(); - return FALSE; - } - } - fio.Close(); - - // Set a flag and end normally - dt.init = TRUE; - dt.changed = FALSE; - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Save -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskTrack::Save(const Filepath& path) -{ - off64_t offset; - int i; - int j; - Fileio fio; - int length; - int total; - - ASSERT(this); - - // Not needed if not initialized - if (!dt.init) { - return TRUE; - } - - // Not needed unless changed - if (!dt.changed) { - return TRUE; - } - - // Need to write - ASSERT(dt.buffer); - ASSERT(dt.changemap); - ASSERT((dt.size >= 8) && (dt.size <= 11)); - ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); - - // Writing in RAW mode is not allowed - ASSERT(!dt.raw); - - // Calculate offset (previous tracks are considered to hold 256 - offset = ((off64_t)dt.track << 8); - offset <<= dt.size; - - // Add offset to real image - offset += dt.imgoffset; - - // Calculate length per sector - length = 1 << dt.size; - - // Open file - if (!fio.Open(path, Fileio::ReadWrite)) { - return FALSE; - } - - // Partial write loop - for (i = 0; i < dt.sectors;) { - // If changed - if (dt.changemap[i]) { - // Initialize write size - total = 0; - - // Seek - if (!fio.Seek(offset + ((off64_t)i << dt.size))) { - fio.Close(); - return FALSE; - } - - // Consectutive sector length - for (j = i; j < dt.sectors; j++) { - // end when interrupted - if (!dt.changemap[j]) { - break; - } - - // Add one sector - total += length; - } - - // Write - if (!fio.Write(&dt.buffer[i << dt.size], total)) { - fio.Close(); - return FALSE; - } - - // To unmodified sector - i = j; - } else { - // Next Sector - i++; - } - } - - // Close - fio.Close(); - - // Drop the change flag and exit - memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL)); - dt.changed = FALSE; - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Read Sector -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskTrack::Read(BYTE *buf, int sec) const -{ - ASSERT(this); - ASSERT(buf); - ASSERT((sec >= 0) & (sec < 0x100)); - - // Error if not initialized - if (!dt.init) { - return FALSE; - } - - // // Error if the number of sectors exceeds the valid number - if (sec >= dt.sectors) { - return FALSE; - } - - // Copy - ASSERT(dt.buffer); - ASSERT((dt.size >= 8) && (dt.size <= 11)); - ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); - memcpy(buf, &dt.buffer[(off64_t)sec << dt.size], (off64_t)1 << dt.size); - - // Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Write Sector -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskTrack::Write(const BYTE *buf, int sec) -{ - int offset; - int length; - - ASSERT(this); - ASSERT(buf); - ASSERT((sec >= 0) & (sec < 0x100)); - ASSERT(!dt.raw); - - // Error if not initialized - if (!dt.init) { - return FALSE; - } - - // // Error if the number of sectors exceeds the valid number - if (sec >= dt.sectors) { - return FALSE; - } - - // Calculate offset and length - offset = sec << dt.size; - length = 1 << dt.size; - - // Compare - ASSERT(dt.buffer); - ASSERT((dt.size >= 8) && (dt.size <= 11)); - ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); - if (memcmp(buf, &dt.buffer[offset], length) == 0) { - // 同じものを書き込もうとしているので、正常終了 - return TRUE; - } - - // Copy, change - memcpy(&dt.buffer[offset], buf, length); - dt.changemap[sec] = TRUE; - dt.changed = TRUE; - - // Success - return TRUE; -} - -//=========================================================================== -// -// Disk Cache -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -DiskCache::DiskCache( - const Filepath& path, int size, int blocks, off64_t imgoff) -{ - int i; - - ASSERT((size >= 8) && (size <= 11)); - ASSERT(blocks > 0); - ASSERT(imgoff >= 0); - - // Cache work - for (i = 0; i < CacheMax; i++) { - cache[i].disktrk = NULL; - cache[i].serial = 0; - } - - // Other - serial = 0; - sec_path = path; - sec_size = size; - sec_blocks = blocks; - cd_raw = FALSE; - imgoffset = imgoff; -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -DiskCache::~DiskCache() -{ - // Clear the track - Clear(); -} - -//--------------------------------------------------------------------------- -// -// RAW Mode Setting -// -//--------------------------------------------------------------------------- -void FASTCALL DiskCache::SetRawMode(BOOL raw) -{ - ASSERT(this); - ASSERT(sec_size == 11); - - // Configuration - cd_raw = raw; -} - -//--------------------------------------------------------------------------- -// -// Save -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskCache::Save() -{ - int i; - - ASSERT(this); - - // Save track - for (i = 0; i < CacheMax; i++) { - // Is it a valid track? - if (cache[i].disktrk) { - // Save - if (!cache[i].disktrk->Save(sec_path)) { - return FALSE; - } - } - } - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Get disk cache information -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskCache::GetCache(int index, int& track, DWORD& aserial) const -{ - ASSERT(this); - ASSERT((index >= 0) && (index < CacheMax)); - - // FALSE if unused - if (!cache[index].disktrk) { - return FALSE; - } - - // Set track and serial - track = cache[index].disktrk->GetTrack(); - aserial = cache[index].serial; - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Clear -// -//--------------------------------------------------------------------------- -void FASTCALL DiskCache::Clear() -{ - int i; - - ASSERT(this); - - // Free the cache - for (i = 0; i < CacheMax; i++) { - if (cache[i].disktrk) { - delete cache[i].disktrk; - cache[i].disktrk = NULL; - } - } -} - -//--------------------------------------------------------------------------- -// -// Sector Read -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskCache::Read(BYTE *buf, int block) -{ - int track; - DiskTrack *disktrk; - - ASSERT(this); - ASSERT(sec_size != 0); - - // Update first - Update(); - - // Calculate track (fixed to 256 sectors/track) - track = block >> 8; - - // Get the track data - disktrk = Assign(track); - if (!disktrk) { - return FALSE; - } - - // Read the track data to the cache - return disktrk->Read(buf, (BYTE)block); -} - -//--------------------------------------------------------------------------- -// -// Sector write -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskCache::Write(const BYTE *buf, int block) -{ - int track; - DiskTrack *disktrk; - - ASSERT(this); - ASSERT(sec_size != 0); - - // Update first - Update(); - - // Calculate track (fixed to 256 sectors/track) - track = block >> 8; - - // Get that track data - disktrk = Assign(track); - if (!disktrk) { - return FALSE; - } - - // Write the data to the cache - return disktrk->Write(buf, (BYTE)block); -} - -//--------------------------------------------------------------------------- -// -// Track Assignment -// -//--------------------------------------------------------------------------- -DiskTrack* FASTCALL DiskCache::Assign(int track) -{ - int i; - int c; - DWORD s; - DiskTrack *disktrk; - - ASSERT(this); - ASSERT(sec_size != 0); - ASSERT(track >= 0); - - // First, check if it is already assigned - for (i = 0; i < CacheMax; i++) { - if (cache[i].disktrk) { - if (cache[i].disktrk->GetTrack() == track) { - // Track match - cache[i].serial = serial; - return cache[i].disktrk; - } - } - } - - // Next, check for empty - for (i = 0; i < CacheMax; i++) { - if (!cache[i].disktrk) { - // Try loading - if (Load(i, track)) { - // Success loading - cache[i].serial = serial; - return cache[i].disktrk; - } - - // Load failed - return NULL; - } - } - - // Finally, find the youngest serial number and delete it - - // Set index 0 as candidate c - s = cache[0].serial; - c = 0; - - // Compare candidate with serial and update to smaller one - for (i = 0; i < CacheMax; i++) { - ASSERT(cache[i].disktrk); - - // Compare and update the existing serial - if (cache[i].serial < s) { - s = cache[i].serial; - c = i; - } - } - - // Save this track - if (!cache[c].disktrk->Save(sec_path)) { - return NULL; - } - - // Delete this track - disktrk = cache[c].disktrk; - cache[c].disktrk = NULL; - - // Load - if (Load(c, track, disktrk)) { - // Successful loading - cache[c].serial = serial; - return cache[c].disktrk; - } - - // Load failed - return NULL; -} - -//--------------------------------------------------------------------------- -// -// Load cache -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskCache::Load(int index, int track, DiskTrack *disktrk) -{ - int sectors; - - ASSERT(this); - ASSERT((index >= 0) && (index < CacheMax)); - ASSERT(track >= 0); - ASSERT(!cache[index].disktrk); - - // Get the number of sectors on this track - sectors = sec_blocks - (track << 8); - ASSERT(sectors > 0); - if (sectors > 0x100) { - sectors = 0x100; - } - - // Create a disk track - if (disktrk == NULL) { - disktrk = new DiskTrack(); - } - - // Initialize disk track - disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset); - - // Try loading - if (!disktrk->Load(sec_path)) { - // 失敗 - delete disktrk; - return FALSE; - } - - // Allocation successful, work set - cache[index].disktrk = disktrk; - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Update serial number -// -//--------------------------------------------------------------------------- -void FASTCALL DiskCache::Update() -{ - int i; - - ASSERT(this); - - // Update and do nothing except 0 - serial++; - if (serial != 0) { - return; - } - - // Clear serial of all caches (loop in 32bit) - for (i = 0; i < CacheMax; i++) { - cache[i].serial = 0; - } -} - - -//=========================================================================== -// -// Disk -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -Disk::Disk() +Disk::Disk(const std::string id) : Device(id), ScsiPrimaryCommands(), ScsiBlockCommands() { // Work initialization - disk.id = MAKEID('N', 'U', 'L', 'L'); - disk.ready = FALSE; - disk.writep = FALSE; - disk.readonly = FALSE; - disk.removable = FALSE; - disk.lock = FALSE; - disk.attn = FALSE; - disk.reset = FALSE; + configured_sector_size = 0; disk.size = 0; disk.blocks = 0; - disk.lun = 0; - disk.code = 0; disk.dcache = NULL; - disk.imgoffset = 0; + disk.image_offset = 0; - // Other - cache_wb = TRUE; + AddCommand(SCSIDEV::eCmdTestUnitReady, "TestUnitReady", &Disk::TestUnitReady); + AddCommand(SCSIDEV::eCmdRezero, "Rezero", &Disk::Rezero); + AddCommand(SCSIDEV::eCmdRequestSense, "RequestSense", &Disk::RequestSense); + AddCommand(SCSIDEV::eCmdFormat, "FormatUnit", &Disk::FormatUnit); + AddCommand(SCSIDEV::eCmdReassign, "ReassignBlocks", &Disk::ReassignBlocks); + AddCommand(SCSIDEV::eCmdRead6, "Read6", &Disk::Read6); + AddCommand(SCSIDEV::eCmdWrite6, "Write6", &Disk::Write6); + AddCommand(SCSIDEV::eCmdSeek6, "Seek6", &Disk::Seek6); + AddCommand(SCSIDEV::eCmdInquiry, "Inquiry", &Disk::Inquiry); + AddCommand(SCSIDEV::eCmdModeSelect6, "ModeSelect6", &Disk::ModeSelect6); + AddCommand(SCSIDEV::eCmdReserve6, "Reserve6", &Disk::Reserve6); + AddCommand(SCSIDEV::eCmdRelease6, "Release6", &Disk::Release6); + AddCommand(SCSIDEV::eCmdModeSense6, "ModeSense6", &Disk::ModeSense6); + AddCommand(SCSIDEV::eCmdStartStop, "StartStopUnit", &Disk::StartStopUnit); + AddCommand(SCSIDEV::eCmdSendDiag, "SendDiagnostic", &Disk::SendDiagnostic); + AddCommand(SCSIDEV::eCmdRemoval, "PreventAllowMediumRemoval", &Disk::PreventAllowMediumRemoval); + AddCommand(SCSIDEV::eCmdReadCapacity10, "ReadCapacity10", &Disk::ReadCapacity10); + AddCommand(SCSIDEV::eCmdRead10, "Read10", &Disk::Read10); + AddCommand(SCSIDEV::eCmdWrite10, "Write10", &Disk::Write10); + AddCommand(SCSIDEV::eCmdSeek10, "Seek10", &Disk::Seek10); + AddCommand(SCSIDEV::eCmdVerify10, "Verify10", &Disk::Verify10); + AddCommand(SCSIDEV::eCmdSynchronizeCache10, "SynchronizeCache10", &Disk::SynchronizeCache10); + AddCommand(SCSIDEV::eCmdSynchronizeCache16, "SynchronizeCache16", &Disk::SynchronizeCache16); + AddCommand(SCSIDEV::eCmdReadDefectData10, "ReadDefectData10", &Disk::ReadDefectData10); + AddCommand(SCSIDEV::eCmdModeSelect10, "ModeSelect10", &Disk::ModeSelect10); + AddCommand(SCSIDEV::eCmdReserve10, "Reserve10", &Disk::Reserve10); + AddCommand(SCSIDEV::eCmdRelease10, "Release10", &Disk::Release10); + AddCommand(SCSIDEV::eCmdModeSense10, "ModeSense10", &Disk::ModeSense10); + AddCommand(SCSIDEV::eCmdRead16, "Read16", &Disk::Read16); + AddCommand(SCSIDEV::eCmdWrite16, "Write16", &Disk::Write16); + AddCommand(SCSIDEV::eCmdVerify16, "Verify16", &Disk::Verify16); + AddCommand(SCSIDEV::eCmdReadCapacity16, "ReadCapacity16", &Disk::ReadCapacity16); + AddCommand(SCSIDEV::eCmdReportLuns, "ReportLuns", &Disk::ReportLuns); } -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- Disk::~Disk() { // Save disk cache - if (disk.ready) { + if (IsReady()) { // Only if ready... if (disk.dcache) { disk.dcache->Save(); @@ -807,198 +82,33 @@ Disk::~Disk() delete disk.dcache; disk.dcache = NULL; } + + for (auto const& command : commands) { + delete command.second; + } } -//--------------------------------------------------------------------------- -// -// Reset -// -//--------------------------------------------------------------------------- -void FASTCALL Disk::Reset() +void Disk::AddCommand(SCSIDEV::scsi_command opcode, const char* name, void (Disk::*execute)(SASIDEV *)) { - ASSERT(this); - - // no lock, no attention, reset - disk.lock = FALSE; - disk.attn = FALSE; - disk.reset = TRUE; + commands[opcode] = new command_t(name, execute); } -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Save -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Save(Fileio *fio, int ver) +bool Disk::Dispatch(SCSIDEV *controller) { - DWORD sz; - DWORD padding; + ctrl = controller->GetCtrl(); - ASSERT(this); - ASSERT(fio); + if (commands.count(static_cast(ctrl->cmd[0]))) { + command_t *command = commands[static_cast(ctrl->cmd[0])]; - // Save size - sz = 52; - if (!fio->Write(&sz, sizeof(sz))) { - return FALSE; + LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, command->name, (unsigned int)ctrl->cmd[0]); + + (this->*command->execute)(controller); + + return true; } - // Save entity - PROP_EXPORT(fio, disk.id); - PROP_EXPORT(fio, disk.ready); - PROP_EXPORT(fio, disk.writep); - PROP_EXPORT(fio, disk.readonly); - PROP_EXPORT(fio, disk.removable); - PROP_EXPORT(fio, disk.lock); - PROP_EXPORT(fio, disk.attn); - PROP_EXPORT(fio, disk.reset); - PROP_EXPORT(fio, disk.size); - PROP_EXPORT(fio, disk.blocks); - PROP_EXPORT(fio, disk.lun); - PROP_EXPORT(fio, disk.code); - PROP_EXPORT(fio, padding); - - // Save the path - if (!diskpath.Save(fio, ver)) { - return FALSE; - } - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Load -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Load(Fileio *fio, int ver) -{ - DWORD sz; - disk_t buf; - DWORD padding; - Filepath path; - - ASSERT(this); - ASSERT(fio); - - // Prior to version 2.03, the disk was not saved - if (ver <= 0x0202) { - return TRUE; - } - - // Delete the current disk cache - if (disk.dcache) { - disk.dcache->Save(); - delete disk.dcache; - disk.dcache = NULL; - } - - // Load size - if (!fio->Read(&sz, sizeof(sz))) { - return FALSE; - } - if (sz != 52) { - return FALSE; - } - - // Load into buffer - PROP_IMPORT(fio, buf.id); - PROP_IMPORT(fio, buf.ready); - PROP_IMPORT(fio, buf.writep); - PROP_IMPORT(fio, buf.readonly); - PROP_IMPORT(fio, buf.removable); - PROP_IMPORT(fio, buf.lock); - PROP_IMPORT(fio, buf.attn); - PROP_IMPORT(fio, buf.reset); - PROP_IMPORT(fio, buf.size); - PROP_IMPORT(fio, buf.blocks); - PROP_IMPORT(fio, buf.lun); - PROP_IMPORT(fio, buf.code); - PROP_IMPORT(fio, padding); - - // Load path - if (!path.Load(fio, ver)) { - return FALSE; - } - - // Move only if IDs match - if (disk.id == buf.id) { - // Do nothing if null - if (IsNULL()) { - return TRUE; - } - - // Same type of device as when saving - disk.ready = FALSE; - if (Open(path)) { - // Disk cache is created in Open - // move only properties - if (!disk.readonly) { - disk.writep = buf.writep; - } - disk.lock = buf.lock; - disk.attn = buf.attn; - disk.reset = buf.reset; - disk.lun = buf.lun; - disk.code = buf.code; - - // Loaded successfully - return TRUE; - } - } - - // Disk cache rebuild - if (!IsReady()) { - disk.dcache = NULL; - } else { - disk.dcache = new DiskCache(diskpath, disk.size, disk.blocks); - } - - return TRUE; -} -#endif // RASCSI - -//--------------------------------------------------------------------------- -// -// NULL Check -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::IsNULL() const -{ - ASSERT(this); - - if (disk.id == MAKEID('N', 'U', 'L', 'L')) { - return TRUE; - } - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// SASI Check -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::IsSASI() const -{ - ASSERT(this); - - if (disk.id == MAKEID('S', 'A', 'H', 'D')) { - return TRUE; - } - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// SCSI Check -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::IsSCSI() const -{ - ASSERT(this); - // If this isn't SASI, then it must be SCSI. - return (this->IsSASI()) ? FALSE : TRUE; + // Unknown command + return false; } //--------------------------------------------------------------------------- @@ -1007,42 +117,404 @@ BOOL FASTCALL Disk::IsSCSI() const // * Call as a post-process after successful opening in a derived class // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Open(const Filepath& path, BOOL /*attn*/) +void Disk::Open(const Filepath& path) { - Fileio fio; - - ASSERT(this); - ASSERT((disk.size >= 8) && (disk.size <= 11)); ASSERT(disk.blocks > 0); - // Ready - disk.ready = TRUE; + SetReady(true); // Cache initialization - ASSERT(!disk.dcache); - disk.dcache = - new DiskCache(path, disk.size, disk.blocks, disk.imgoffset); + assert (!disk.dcache); + disk.dcache = new DiskCache(path, disk.size, disk.blocks, disk.image_offset); // Can read/write open + Fileio fio; if (fio.Open(path, Fileio::ReadWrite)) { - // Write permission, not read only - disk.writep = FALSE; - disk.readonly = FALSE; + // Write permission fio.Close(); } else { - // Write protected, read only - disk.writep = TRUE; - disk.readonly = TRUE; + // Permanently write-protected + SetReadOnly(true); + SetProtectable(false); + SetProtected(false); } - // Not locked - disk.lock = FALSE; + SetStopped(false); + SetRemoved(false); + SetLocked(false); +} - // Save path - diskpath = path; +void Disk::TestUnitReady(SASIDEV *controller) +{ + if (!CheckReady()) { + controller->Error(); + return; + } - // Success - return TRUE; + controller->Status(); +} + +void Disk::Rezero(SASIDEV *controller) +{ + if (!CheckReady()) { + controller->Error(); + return; + } + + controller->Status(); +} + +void Disk::RequestSense(SASIDEV *controller) +{ + int lun = controller->GetEffectiveLun(); + + // Note: According to the SCSI specs the LUN handling for REQUEST SENSE non-existing LUNs do *not* result + // in CHECK CONDITION. Only the Sense Key and ASC are set in order to signal the non-existing LUN. + if (!ctrl->unit[lun]) { + // LUN 0 can be assumed to be present (required to call RequestSense() below) + lun = 0; + + controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN); + ctrl->status = 0x00; + } + + ctrl->length = ctrl->unit[lun]->RequestSense(ctrl->cmd, ctrl->buffer); + ASSERT(ctrl->length > 0); + + LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, ctrl->status, ctrl->buffer[2], ctrl->buffer[12]); + + controller->DataIn(); +} + +void Disk::FormatUnit(SASIDEV *controller) +{ + if (!Format(ctrl->cmd)) { + controller->Error(); + return; + } + + controller->Status(); +} + +void Disk::ReassignBlocks(SASIDEV *controller) +{ + if (!CheckReady()) { + controller->Error(); + return; + } + + controller->Status(); +} + +//--------------------------------------------------------------------------- +// +// READ +// +//--------------------------------------------------------------------------- +void Disk::Read(SASIDEV *controller, uint64_t record) +{ + ctrl->length = Read(ctrl->cmd, ctrl->buffer, record); + LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, (int)ctrl->length); + + if (ctrl->length <= 0) { + controller->Error(); + return; + } + + // Set next block + ctrl->next = record + 1; + + controller->DataIn(); +} + +void Disk::Read6(SASIDEV *controller) +{ + // Get record number and block number + uint64_t record; + if (GetStartAndCount(controller, record, ctrl->blocks, RW6)) { + LOGDEBUG("%s READ(6) command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)record, ctrl->blocks); + + Read(controller, record); + } +} + +void Disk::Read10(SASIDEV *controller) +{ + // Get record number and block number + uint64_t record; + if (GetStartAndCount(controller, record, ctrl->blocks, RW10)) { + LOGDEBUG("%s READ(10) command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)record, ctrl->blocks); + + Read(controller, record); + } +} + +void Disk::Read16(SASIDEV *controller) +{ + // Get record number and block number + uint64_t record; + if (GetStartAndCount(controller, record, ctrl->blocks, RW16)) { + LOGDEBUG("%s READ(16) command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)record, ctrl->blocks); + + Read(controller, record); + } +} + +//--------------------------------------------------------------------------- +// +// WRITE +// +//--------------------------------------------------------------------------- +void Disk::Write(SASIDEV *controller, uint64_t record) +{ + ctrl->length = WriteCheck(record); + if (ctrl->length == 0) { + controller->Error(ERROR_CODES::sense_key::NOT_READY, ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION); + return; + } + else if (ctrl->length < 0) { + controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::WRITE_PROTECTED); + return; + } + + // Set next block + ctrl->next = record + 1; + + controller->DataOut(); +} + +void Disk::Write6(SASIDEV *controller) +{ + // Get record number and block number + uint64_t record; + if (GetStartAndCount(controller, record, ctrl->blocks, RW6)) { + LOGDEBUG("%s WRITE(6) command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)record, ctrl->blocks); + + Write(controller, record); + } +} + +void Disk::Write10(SASIDEV *controller) +{ + // Get record number and block number + uint64_t record; + if (GetStartAndCount(controller, record, ctrl->blocks, RW10)) { + LOGDEBUG("%s WRITE(10) command record=$%08X blocks=%d",__PRETTY_FUNCTION__, (uint32_t)record, ctrl->blocks); + + Write(controller, record); + } +} + +void Disk::Write16(SASIDEV *controller) +{ + // Get record number and block number + uint64_t record; + if (GetStartAndCount(controller, record, ctrl->blocks, RW16)) { + LOGDEBUG("%s WRITE(16) command record=$%08X blocks=%d",__PRETTY_FUNCTION__, (uint32_t)record, ctrl->blocks); + + Write(controller, record); + } +} + +//--------------------------------------------------------------------------- +// +// VERIFY +// +//--------------------------------------------------------------------------- +void Disk::Verify(SASIDEV *controller, uint64_t record) +{ + // if BytChk=0 + if ((ctrl->cmd[1] & 0x02) == 0) { + Seek(controller); + return; + } + + // Test loading + ctrl->length = Read(ctrl->cmd, ctrl->buffer, record); + if (ctrl->length <= 0) { + controller->Error(); + return; + } + + // Set next block + ctrl->next = record + 1; + + controller->DataOut(); +} + +void Disk::Verify10(SASIDEV *controller) +{ + // Get record number and block number + uint64_t record; + if (GetStartAndCount(controller, record, ctrl->blocks, RW10)) { + LOGDEBUG("%s VERIFY(10) command record=$%08X blocks=%d",__PRETTY_FUNCTION__, (uint32_t)record, ctrl->blocks); + + Verify(controller, record); + } +} + +void Disk::Verify16(SASIDEV *controller) +{ + // Get record number and block number + uint64_t record; + if (GetStartAndCount(controller, record, ctrl->blocks, RW16)) { + LOGDEBUG("%s VERIFY(16) command record=$%08X blocks=%d",__PRETTY_FUNCTION__, (uint32_t)record, ctrl->blocks); + + Verify(controller, record); + } +} + +void Disk::Inquiry(SASIDEV *controller) +{ + ScsiPrimaryCommands *device = NULL; + int lun = controller->GetEffectiveLun(); + device = ctrl->unit[lun]; + + // Find a valid unit + // TODO The code below is probably wrong. It results in the same INQUIRY data being + // used for all LUNs, even though each LUN has its individual set of INQUIRY data. + // In addition, it supports gaps in the LUN list, which is not correct. + if (!device) { + for (int valid_lun = 0; valid_lun < SASIDEV::UnitMax; valid_lun++) { + if (ctrl->unit[valid_lun]) { + device = ctrl->unit[valid_lun]; + break; + } + } + } + + if (device) { + ctrl->length = Inquiry(ctrl->cmd, ctrl->buffer); + } else { + ctrl->length = 0; + } + + if (ctrl->length <= 0) { + controller->Error(); + return; + } + + // Report if the device does not support the requested LUN + if (!ctrl->unit[lun]) { + LOGDEBUG("Reporting LUN %d for device ID %d as not supported", lun, ctrl->device->GetId()); + + ctrl->buffer[0] |= 0x7f; + } + + controller->DataIn(); +} + +void Disk::ModeSelect6(SASIDEV *controller) +{ + LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, ctrl->buffer[0]); + + ctrl->length = ModeSelectCheck6(ctrl->cmd); + if (ctrl->length <= 0) { + controller->Error(); + return; + } + + controller->DataOut(); +} + +void Disk::ModeSelect10(SASIDEV *controller) +{ + LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, ctrl->buffer[0]); + + ctrl->length = ModeSelectCheck10(ctrl->cmd); + if (ctrl->length <= 0) { + controller->Error(); + return; + } + + controller->DataOut(); +} + +void Disk::ModeSense6(SASIDEV *controller) +{ + ctrl->length = ModeSense6(ctrl->cmd, ctrl->buffer); + if (ctrl->length <= 0) { + LOGTRACE("%s Unsupported mode page $%02X",__PRETTY_FUNCTION__, (unsigned int)ctrl->cmd[2]); + + controller->Error(); + return; + } + + controller->DataIn(); +} + +void Disk::ModeSense10(SASIDEV *controller) +{ + ctrl->length = ModeSense10(ctrl->cmd, ctrl->buffer); + if (ctrl->length <= 0) { + LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->cmd[2]); + + controller->Error(); + return; + } + + controller->DataIn(); +} + +void Disk::StartStopUnit(SASIDEV *controller) +{ + if (!StartStop(ctrl->cmd)) { + controller->Error(); + return; + } + + controller->Status(); +} + +void Disk::SendDiagnostic(SASIDEV *controller) +{ + if (!SendDiag(ctrl->cmd)) { + controller->Error(); + return; + } + + controller->Status(); +} + +void Disk::PreventAllowMediumRemoval(SASIDEV *controller) +{ + if (!CheckReady()) { + controller->Error(); + return; + } + + bool lock = ctrl->cmd[4] & 0x01; + + LOGTRACE("%s", lock ? "Locking medium" : "Unlocking medium"); + + SetLocked(lock); + + controller->Status(); +} + +void Disk::SynchronizeCache10(SASIDEV *controller) +{ + // Nothing to do + + controller->Status(); +} + +void Disk::SynchronizeCache16(SASIDEV *controller) +{ + return SynchronizeCache10(controller); +} + +void Disk::ReadDefectData10(SASIDEV *controller) +{ + ctrl->length = ReadDefectData10(ctrl->cmd, ctrl->buffer); + ASSERT(ctrl->length >= 0); + + if (ctrl->length <= 4) { + controller->Error(); + return; + } + + controller->DataIn(); } //--------------------------------------------------------------------------- @@ -1050,103 +522,23 @@ BOOL FASTCALL Disk::Open(const Filepath& path, BOOL /*attn*/) // Eject // //--------------------------------------------------------------------------- -void FASTCALL Disk::Eject(BOOL force) +bool Disk::Eject(bool force) { - ASSERT(this); + bool status = Device::Eject(force); + if (status) { + // Remove disk cache + disk.dcache->Save(); + delete disk.dcache; + disk.dcache = NULL; - // Can only be ejected if it is removable - if (!disk.removable) { - return; - } - - // If you're not ready, you don't need to eject - if (!disk.ready) { - return; - } - - // Must be unlocked if there is no force flag - if (!force) { - if (disk.lock) { - return; + // The image file for this drive is not in use anymore + FileSupport *file_support = dynamic_cast(this); + if (file_support) { + file_support->UnreserveFile(); } } - // Remove disk cache - disk.dcache->Save(); - delete disk.dcache; - disk.dcache = NULL; - - // Not ready, no attention - disk.ready = FALSE; - disk.writep = FALSE; - disk.readonly = FALSE; - disk.attn = FALSE; -} - -//--------------------------------------------------------------------------- -// -// Write Protected -// -//--------------------------------------------------------------------------- -void FASTCALL Disk::WriteP(BOOL writep) -{ - ASSERT(this); - - // be ready - if (!disk.ready) { - return; - } - - // Read Only, protect only - if (disk.readonly) { - ASSERT(disk.writep); - return; - } - - // Write protect flag setting - disk.writep = writep; -} - -//--------------------------------------------------------------------------- -// -// Get Disk -// -//--------------------------------------------------------------------------- -void FASTCALL Disk::GetDisk(disk_t *buffer) const -{ - ASSERT(this); - ASSERT(buffer); - - // Assign internal buffer - *buffer = disk; -} - -//--------------------------------------------------------------------------- -// -// Get Path -// -//--------------------------------------------------------------------------- -void FASTCALL Disk::GetPath(Filepath& path) const -{ - path = diskpath; -} - -//--------------------------------------------------------------------------- -// -// Flush -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Flush() -{ - ASSERT(this); - - // Do nothing if there's nothing cached - if (!disk.dcache) { - return TRUE; - } - - // Save cache - return disk.dcache->Save(); + return status; } //--------------------------------------------------------------------------- @@ -1154,49 +546,35 @@ BOOL FASTCALL Disk::Flush() // Check Ready // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::CheckReady() +bool Disk::CheckReady() { - ASSERT(this); - // Not ready if reset - if (disk.reset) { - disk.code = DISK_DEVRESET; - disk.reset = FALSE; - return FALSE; + if (IsReset()) { + SetStatusCode(STATUS_DEVRESET); + SetReset(false); + LOGTRACE("%s Disk in reset", __PRETTY_FUNCTION__); + return false; } // Not ready if it needs attention - if (disk.attn) { - disk.code = DISK_ATTENTION; - disk.attn = FALSE; - return FALSE; + if (IsAttn()) { + SetStatusCode(STATUS_ATTENTION); + SetAttn(false); + LOGTRACE("%s Disk in needs attention", __PRETTY_FUNCTION__); + return false; } // Return status if not ready - if (!disk.ready) { - disk.code = DISK_NOTREADY; - return FALSE; + if (!IsReady()) { + SetStatusCode(STATUS_NOTREADY); + LOGTRACE("%s Disk not ready", __PRETTY_FUNCTION__); + return false; } // Initialization with no error - disk.code = DISK_NOERROR; - return TRUE; -} + LOGTRACE("%s Disk is ready", __PRETTY_FUNCTION__); -//--------------------------------------------------------------------------- -// -// INQUIRY -// *You need to be successful at all times -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::Inquiry( - const DWORD* /*cdb*/, BYTE* /*buf*/, DWORD /*major*/, DWORD /*minor*/) -{ - ASSERT(this); - - // default is INQUIRY failure - disk.code = DISK_INVALIDCMD; - return 0; + return true; } //--------------------------------------------------------------------------- @@ -1205,23 +583,20 @@ int FASTCALL Disk::Inquiry( // *SASI is a separate process // //--------------------------------------------------------------------------- -int FASTCALL Disk::RequestSense(const DWORD *cdb, BYTE *buf) +int Disk::RequestSense(const DWORD *cdb, BYTE *buf) { - int size; - - ASSERT(this); ASSERT(cdb); ASSERT(buf); // Return not ready only if there are no errors - if (disk.code == DISK_NOERROR) { - if (!disk.ready) { - disk.code = DISK_NOTREADY; + if (GetStatusCode() == STATUS_NOERROR) { + if (!IsReady()) { + SetStatusCode(STATUS_NOTREADY); } } // Size determination (according to allocation length) - size = (int)cdb[4]; + int size = (int)cdb[4]; ASSERT((size >= 0) && (size < 0x100)); // For SCSI-1, transfer 4 bytes when the size is 0 @@ -1234,167 +609,119 @@ int FASTCALL Disk::RequestSense(const DWORD *cdb, BYTE *buf) memset(buf, 0, size); // Set 18 bytes including extended sense data - buf[0] = 0x70; - buf[2] = (BYTE)(disk.code >> 16); - buf[7] = 10; - buf[12] = (BYTE)(disk.code >> 8); - buf[13] = (BYTE)disk.code; - // Clear the code - disk.code = 0x00; + // Current error + buf[0] = 0x70; + + buf[2] = (BYTE)(GetStatusCode() >> 16); + buf[7] = 10; + buf[12] = (BYTE)(GetStatusCode() >> 8); + buf[13] = (BYTE)GetStatusCode(); return size; } -//--------------------------------------------------------------------------- -// -// MODE SELECT check -// *Not affected by disk.code -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::SelectCheck(const DWORD *cdb) +int Disk::ModeSelectCheck(const DWORD *cdb, int length) { - int length; - - ASSERT(this); - ASSERT(cdb); - - // Error if save parameters are set instead of SCSIHD - if (disk.id != MAKEID('S', 'C', 'H', 'D')) { - // Error if save parameters are set - if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; - return 0; - } + // Error if save parameters are set for other types than of SCHD or SCRM + if (!IsSCSIHD() && (cdb[1] & 0x01)) { + SetStatusCode(STATUS_INVALIDCDB); + return 0; } - // Receive the data specified by the parameter length - length = (int)cdb[4]; return length; } - -//--------------------------------------------------------------------------- -// -// MODE SELECT(10) check -// * Not affected by disk.code -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::SelectCheck10(const DWORD *cdb) +int Disk::ModeSelectCheck6(const DWORD *cdb) { - DWORD length; - - ASSERT(this); - ASSERT(cdb); - - // Error if save parameters are set instead of SCSIHD - if (disk.id != MAKEID('S', 'C', 'H', 'D')) { - if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; - return 0; - } - } - // Receive the data specified by the parameter length - length = cdb[7]; + return ModeSelectCheck(cdb, cdb[4]); +} + +int Disk::ModeSelectCheck10(const DWORD *cdb) +{ + // Receive the data specified by the parameter length + int length = cdb[7]; length <<= 8; length |= cdb[8]; if (length > 0x800) { length = 0x800; } - return (int)length; + return ModeSelectCheck(cdb, length); } //--------------------------------------------------------------------------- // // MODE SELECT -// * Not affected by disk.code // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::ModeSelect( - const DWORD* /*cdb*/, const BYTE *buf, int length) +bool Disk::ModeSelect(const DWORD* /*cdb*/, const BYTE *buf, int length) { - ASSERT(this); ASSERT(buf); ASSERT(length >= 0); // cannot be set - disk.code = DISK_INVALIDPRM; + SetStatusCode(STATUS_INVALIDPRM); - return FALSE; + return false; } //--------------------------------------------------------------------------- // -// MODE SENSE -// *Not affected by disk.code +// MODE SENSE(6) // //--------------------------------------------------------------------------- -int FASTCALL Disk::ModeSense(const DWORD *cdb, BYTE *buf) +int Disk::ModeSense6(const DWORD *cdb, BYTE *buf) { - int page; - int length; - int size; - BOOL valid; - BOOL change; - int ret; - - ASSERT(this); ASSERT(cdb); ASSERT(buf); - ASSERT(cdb[0] == 0x1a); // Get length, clear buffer - length = (int)cdb[4]; + int length = (int)cdb[4]; ASSERT((length >= 0) && (length < 0x100)); memset(buf, 0, length); // Get changeable flag - if ((cdb[2] & 0xc0) == 0x40) { - change = TRUE; - } else { - change = FALSE; - } + bool change = (cdb[2] & 0xc0) == 0x40; // Get page code (0x00 is valid from the beginning) - page = cdb[2] & 0x3f; - if (page == 0x00) { - valid = TRUE; - } else { - valid = FALSE; - } + int page = cdb[2] & 0x3f; + bool valid = page == 0x00; // Basic information - size = 4; + int size = 4; // MEDIUM TYPE - if (disk.id == MAKEID('S', 'C', 'M', 'O')) { + if (IsMo()) { buf[1] = 0x03; // optical reversible or erasable } // DEVICE SPECIFIC PARAMETER - if (disk.writep) { + if (IsProtected()) { buf[2] = 0x80; } // add block descriptor if DBD is 0 if ((cdb[1] & 0x08) == 0) { - // Mode parameter header + // Mode parameter header, block descriptor length buf[3] = 0x08; // Only if ready - if (disk.ready) { - // Block descriptor (number of blocks) - buf[5] = (BYTE)(disk.blocks >> 16); - buf[6] = (BYTE)(disk.blocks >> 8); - buf[7] = (BYTE)disk.blocks; + if (IsReady()) { + // Short LBA mode parameter block descriptor (number of blocks and block length) + + uint64_t disk_blocks = GetBlockCount(); + buf[4] = disk_blocks >> 24; + buf[5] = disk_blocks >> 16; + buf[6] = disk_blocks >> 8; + buf[7] = disk_blocks; // Block descriptor (block length) - size = 1 << disk.size; - buf[9] = (BYTE)(size >> 16); - buf[10] = (BYTE)(size >> 8); - buf[11] = (BYTE)size; + uint32_t disk_size = GetSectorSizeInBytes(); + buf[9] = disk_size >> 16; + buf[10] = disk_size >> 8; + buf[11] = disk_size; } // size @@ -1403,95 +730,90 @@ int FASTCALL Disk::ModeSense(const DWORD *cdb, BYTE *buf) // Page code 1(read-write error recovery) if ((page == 0x01) || (page == 0x3f)) { - size += AddError(change, &buf[size]); - valid = TRUE; + size += AddErrorPage(change, &buf[size]); + valid = true; } // Page code 3(format device) if ((page == 0x03) || (page == 0x3f)) { - size += AddFormat(change, &buf[size]); - valid = TRUE; + size += AddFormatPage(change, &buf[size]); + valid = true; } // Page code 4(drive parameter) if ((page == 0x04) || (page == 0x3f)) { - size += AddDrive(change, &buf[size]); - valid = TRUE; + size += AddDrivePage(change, &buf[size]); + valid = true; } // Page code 6(optical) - if (disk.id == MAKEID('S', 'C', 'M', 'O')) { + if (IsMo()) { if ((page == 0x06) || (page == 0x3f)) { - size += AddOpt(change, &buf[size]); - valid = TRUE; + size += AddOptionPage(change, &buf[size]); + valid = true; } } // Page code 8(caching) if ((page == 0x08) || (page == 0x3f)) { - size += AddCache(change, &buf[size]); - valid = TRUE; + size += AddCachePage(change, &buf[size]); + valid = true; } // Page code 13(CD-ROM) - if (disk.id == MAKEID('S', 'C', 'C', 'D')) { + if (IsCdRom()) { if ((page == 0x0d) || (page == 0x3f)) { - size += AddCDROM(change, &buf[size]); - valid = TRUE; + size += AddCDROMPage(change, &buf[size]); + valid = true; } } // Page code 14(CD-DA) - if (disk.id == MAKEID('S', 'C', 'C', 'D')) { + if (IsCdRom()) { if ((page == 0x0e) || (page == 0x3f)) { - size += AddCDDA(change, &buf[size]); - valid = TRUE; + size += AddCDDAPage(change, &buf[size]); + valid = true; } } // Page (vendor special) - ret = AddVendor(page, change, &buf[size]); + int ret = AddVendorPage(page, change, &buf[size]); if (ret > 0) { size += ret; - valid = TRUE; + valid = true; } // final setting of mode data length - buf[0] = (BYTE)(size - 1); + buf[0] = size - 1; // Unsupported page if (!valid) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return 0; } + //check if size of data is more than size requested. + if (size > length) { + SetStatusCode(STATUS_INVALIDCDB); + return 0; + } + //Set length returned to actual size of data + length = size; - // MODE SENSE success - disk.code = DISK_NOERROR; return length; } //--------------------------------------------------------------------------- // // MODE SENSE(10) -// *Not affected by disk.code // //--------------------------------------------------------------------------- -int FASTCALL Disk::ModeSense10(const DWORD *cdb, BYTE *buf) +int Disk::ModeSense10(const DWORD *cdb, BYTE *buf) { - int page; - int length; - int size; - BOOL valid; - BOOL change; - int ret; - - ASSERT(this); ASSERT(cdb); ASSERT(buf); - ASSERT(cdb[0] == 0x5a); // Get length, clear buffer - length = cdb[7]; + int length = cdb[7]; length <<= 8; length |= cdb[8]; if (length > 0x800) { @@ -1501,151 +823,167 @@ int FASTCALL Disk::ModeSense10(const DWORD *cdb, BYTE *buf) memset(buf, 0, length); // Get changeable flag - if ((cdb[2] & 0xc0) == 0x40) { - change = TRUE; - } else { - change = FALSE; - } + bool change = (cdb[2] & 0xc0) == 0x40; // Get page code (0x00 is valid from the beginning) - page = cdb[2] & 0x3f; - if (page == 0x00) { - valid = TRUE; - } else { - valid = FALSE; - } + int page = cdb[2] & 0x3f; + bool valid = page == 0x00; // Basic Information - size = 4; - if (disk.writep) { - buf[2] = 0x80; + int size = 8; + + // MEDIUM TYPE + if (IsMo()) { + buf[2] = 0x03; // optical reversible or erasable + } + + // DEVICE SPECIFIC PARAMETER + if (IsProtected()) { + buf[3] = 0x80; } // add block descriptor if DBD is 0 if ((cdb[1] & 0x08) == 0) { - // Mode parameter header - buf[3] = 0x08; - // Only if ready - if (disk.ready) { - // Block descriptor (number of blocks) - buf[5] = (BYTE)(disk.blocks >> 16); - buf[6] = (BYTE)(disk.blocks >> 8); - buf[7] = (BYTE)disk.blocks; + if (IsReady()) { + uint64_t disk_blocks = GetBlockCount(); + uint32_t disk_size = GetSectorSizeInBytes(); - // Block descriptor (block length) - size = 1 << disk.size; - buf[9] = (BYTE)(size >> 16); - buf[10] = (BYTE)(size >> 8); - buf[11] = (BYTE)size; + // Check LLBAA for short or long block descriptor + if ((cdb[1] & 0x10) == 0 || disk_blocks <= 0xFFFFFFFF) { + // Mode parameter header, block descriptor length + buf[7] = 0x08; + + // Short LBA mode parameter block descriptor (number of blocks and block length) + + buf[8] = disk_blocks >> 24; + buf[9] = disk_blocks >> 16; + buf[10] = disk_blocks >> 8; + buf[11] = disk_blocks; + + buf[13] = disk_size >> 16; + buf[14] = disk_size >> 8; + buf[15] = disk_size; + + size = 16; + } + else { + // Mode parameter header, LONGLBA + buf[4] = 0x01; + + // Mode parameter header, block descriptor length + buf[7] = 0x10; + + // Long LBA mode parameter block descriptor (number of blocks and block length) + + buf[8] = disk_blocks >> 56; + buf[9] = disk_blocks >> 48; + buf[10] = disk_blocks >> 40; + buf[11] = disk_blocks >> 32; + buf[12] = disk_blocks >> 24; + buf[13] = disk_blocks >> 16; + buf[14] = disk_blocks >> 8; + buf[15] = disk_blocks; + + buf[20] = disk_size >> 24; + buf[21] = disk_size >> 16; + buf[22] = disk_size >> 8; + buf[23] = disk_size; + + size = 24; + } } - - // Size - size = 12; } // Page code 1(read-write error recovery) if ((page == 0x01) || (page == 0x3f)) { - size += AddError(change, &buf[size]); - valid = TRUE; + size += AddErrorPage(change, &buf[size]); + valid = true; } // Page code 3(format device) if ((page == 0x03) || (page == 0x3f)) { - size += AddFormat(change, &buf[size]); - valid = TRUE; + size += AddFormatPage(change, &buf[size]); + valid = true; } // Page code 4(drive parameter) if ((page == 0x04) || (page == 0x3f)) { - size += AddDrive(change, &buf[size]); - valid = TRUE; + size += AddDrivePage(change, &buf[size]); + valid = true; } // ペPage code 6(optical) - if (disk.id == MAKEID('S', 'C', 'M', 'O')) { + if (IsMo()) { if ((page == 0x06) || (page == 0x3f)) { - size += AddOpt(change, &buf[size]); - valid = TRUE; + size += AddOptionPage(change, &buf[size]); + valid = true; } } // Page code 8(caching) if ((page == 0x08) || (page == 0x3f)) { - size += AddCache(change, &buf[size]); - valid = TRUE; + size += AddCachePage(change, &buf[size]); + valid = true; } // Page code 13(CD-ROM) - if (disk.id == MAKEID('S', 'C', 'C', 'D')) { + if (IsCdRom()) { if ((page == 0x0d) || (page == 0x3f)) { - size += AddCDROM(change, &buf[size]); - valid = TRUE; + size += AddCDROMPage(change, &buf[size]); + valid = true; } } // Page code 14(CD-DA) - if (disk.id == MAKEID('S', 'C', 'C', 'D')) { + if (IsCdRom()) { if ((page == 0x0e) || (page == 0x3f)) { - size += AddCDDA(change, &buf[size]); - valid = TRUE; + size += AddCDDAPage(change, &buf[size]); + valid = true; } } // Page (vendor special) - ret = AddVendor(page, change, &buf[size]); + int ret = AddVendorPage(page, change, &buf[size]); if (ret > 0) { size += ret; - valid = TRUE; + valid = true; } // final setting of mode data length - buf[0] = (BYTE)(size - 1); + buf[0] = (size - 1) >> 8; + buf[1] = size - 1; // Unsupported page if (!valid) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return 0; } + //check if size of data is more than size requested. + if (size > length) { + SetStatusCode(STATUS_INVALIDCDB); + return 0; + } + //Set length returned to actual size of data + length = size; - // MODE SENSE success - disk.code = DISK_NOERROR; return length; } -//--------------------------------------------------------------------------- -// -// Add error page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddError(BOOL change, BYTE *buf) +int Disk::AddErrorPage(bool change, BYTE *buf) { - ASSERT(this); ASSERT(buf); // Set the message length buf[0] = 0x01; buf[1] = 0x0a; - // No changeable area - if (change) { - return 12; - } - // Retry count is 0, limit time uses internal default value return 12; } -//--------------------------------------------------------------------------- -// -// Add format page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddFormat(BOOL change, BYTE *buf) +int Disk::AddFormatPage(bool change, BYTE *buf) { - int size; - - ASSERT(this); ASSERT(buf); // Set the message length @@ -1660,7 +998,7 @@ int FASTCALL Disk::AddFormat(BOOL change, BYTE *buf) return 24; } - if (disk.ready) { + if (IsReady()) { // Set the number of tracks in one zone to 8 (TODO) buf[0x3] = 0x08; @@ -1669,29 +1007,21 @@ int FASTCALL Disk::AddFormat(BOOL change, BYTE *buf) buf[0xb] = 0x19; // Set the number of bytes in the physical sector - size = 1 << disk.size; + int size = 1 << disk.size; buf[0xc] = (BYTE)(size >> 8); buf[0xd] = (BYTE)size; } // Set removable attribute - if (disk.removable) { + if (IsRemovable()) { buf[20] = 0x20; } return 24; } -//--------------------------------------------------------------------------- -// -// Add drive page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddDrive(BOOL change, BYTE *buf) +int Disk::AddDrivePage(bool change, BYTE *buf) { - DWORD cylinder; - - ASSERT(this); ASSERT(buf); // Set the message length @@ -1703,10 +1033,10 @@ int FASTCALL Disk::AddDrive(BOOL change, BYTE *buf) return 24; } - if (disk.ready) { + if (IsReady()) { // Set the number of cylinders (total number of blocks // divided by 25 sectors/track and 8 heads) - cylinder = disk.blocks; + uint32_t cylinder = disk.blocks; cylinder >>= 3; cylinder /= 25; buf[0x2] = (BYTE)(cylinder >> 16); @@ -1720,60 +1050,32 @@ int FASTCALL Disk::AddDrive(BOOL change, BYTE *buf) return 24; } -//--------------------------------------------------------------------------- -// -// Add option -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddOpt(BOOL change, BYTE *buf) +int Disk::AddOptionPage(bool change, BYTE *buf) { - ASSERT(this); ASSERT(buf); // Set the message length buf[0] = 0x06; buf[1] = 0x02; - // No changeable area - if (change) { - return 4; - } - // Do not report update blocks return 4; } -//--------------------------------------------------------------------------- -// -// Add Cache Page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddCache(BOOL change, BYTE *buf) +int Disk::AddCachePage(bool change, BYTE *buf) { - ASSERT(this); ASSERT(buf); // Set the message length buf[0] = 0x08; buf[1] = 0x0a; - // No changeable area - if (change) { - return 12; - } - // Only read cache is valid, no prefetch return 12; } -//--------------------------------------------------------------------------- -// -// Add CDROM Page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddCDROM(BOOL change, BYTE *buf) +int Disk::AddCDROMPage(bool change, BYTE *buf) { - ASSERT(this); ASSERT(buf); // Set the message length @@ -1795,60 +1097,33 @@ int FASTCALL Disk::AddCDROM(BOOL change, BYTE *buf) return 8; } -//--------------------------------------------------------------------------- -// -// CD-DAページ追加 -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddCDDA(BOOL change, BYTE *buf) +int Disk::AddCDDAPage(bool change, BYTE *buf) { - ASSERT(this); ASSERT(buf); // Set the message length buf[0] = 0x0e; buf[1] = 0x0e; - // No changeable area - if (change) { - return 16; - } - // Audio waits for operation completion and allows // PLAY across multiple tracks return 16; } -//--------------------------------------------------------------------------- -// -// Add special vendor page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddVendor(int /*page*/, BOOL /*change*/, BYTE *buf) +int Disk::AddVendorPage(int /*page*/, bool /*change*/, BYTE *buf) { - ASSERT(this); ASSERT(buf); return 0; } -//--------------------------------------------------------------------------- -// -// READ DEFECT DATA(10) -// *Not affected by disk.code -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::ReadDefectData10(const DWORD *cdb, BYTE *buf) +int Disk::ReadDefectData10(const DWORD *cdb, BYTE *buf) { - DWORD length; - - ASSERT(this); ASSERT(cdb); ASSERT(buf); - ASSERT(cdb[0] == 0x37); // Get length, clear buffer - length = cdb[7]; + DWORD length = cdb[7]; length <<= 8; length |= cdb[8]; if (length > 0x800) { @@ -1872,93 +1147,30 @@ int FASTCALL Disk::ReadDefectData10(const DWORD *cdb, BYTE *buf) buf[11] = 0xff; // no list - disk.code = DISK_NODEFECT; + SetStatusCode(STATUS_NODEFECT); return 4; } -//--------------------------------------------------------------------------- -// -// Command -// -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// -// TEST UNIT READY -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::TestUnitReady(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // TEST UNIT READY Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// REZERO UNIT -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Rezero(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // REZERO Success - return TRUE; -} - //--------------------------------------------------------------------------- // // FORMAT UNIT // *Opcode $06 for SASI, Opcode $04 for SCSI // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Format(const DWORD *cdb) +bool Disk::Format(const DWORD *cdb) { - ASSERT(this); - - // Status check if (!CheckReady()) { - return FALSE; + return false; } // FMTDATA=1 is not supported (but OK if there is no DEFECT LIST) if ((cdb[1] & 0x10) != 0 && cdb[4] != 0) { - disk.code = DISK_INVALIDCDB; - return FALSE; + SetStatusCode(STATUS_INVALIDCDB); + return false; } // FORMAT Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// REASSIGN BLOCKS -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Reassign(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // REASSIGN BLOCKS Success - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -1966,25 +1178,27 @@ BOOL FASTCALL Disk::Reassign(const DWORD* /*cdb*/) // READ // //--------------------------------------------------------------------------- -int FASTCALL Disk::Read(BYTE *buf, DWORD block) +// TODO Read more than one block in a single call. Currently blocked by the SASI code (missing early range check) +// and the track-oriented cache. +int Disk::Read(const DWORD *cdb, BYTE *buf, uint64_t block) { - ASSERT(this); ASSERT(buf); - // Status check + LOGTRACE("%s", __PRETTY_FUNCTION__); + if (!CheckReady()) { return 0; } // Error if the total number of blocks is exceeded if (block >= disk.blocks) { - disk.code = DISK_INVALIDLBA; + SetStatusCode(STATUS_INVALIDLBA); return 0; } // leave it to the cache if (!disk.dcache->Read(buf, block)) { - disk.code = DISK_READFAULT; + SetStatusCode(STATUS_READFAULT); return 0; } @@ -1997,24 +1211,24 @@ int FASTCALL Disk::Read(BYTE *buf, DWORD block) // WRITE check // //--------------------------------------------------------------------------- -int FASTCALL Disk::WriteCheck(DWORD block) +int Disk::WriteCheck(DWORD block) { - ASSERT(this); - // Status check if (!CheckReady()) { + LOGDEBUG("WriteCheck failed (not ready)"); return 0; } // Error if the total number of blocks is exceeded if (block >= disk.blocks) { + LOGDEBUG("WriteCheck failed (capacity exceeded)"); return 0; } // Error if write protected - if (disk.writep) { - disk.code = DISK_WRITEPROTECT; - return 0; + if (IsProtected()) { + LOGDEBUG("WriteCheck failed (protected)"); + return -1; } // Success @@ -2026,93 +1240,70 @@ int FASTCALL Disk::WriteCheck(DWORD block) // WRITE // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Write(const BYTE *buf, DWORD block) +// TODO Write more than one block in a single call. Currently blocked by the SASI code (missing early range check) +// and the track-oriented cache. +bool Disk::Write(const DWORD *cdb, const BYTE *buf, DWORD block) { - ASSERT(this); ASSERT(buf); + LOGTRACE("%s", __PRETTY_FUNCTION__); + // Error if not ready - if (!disk.ready) { - disk.code = DISK_NOTREADY; - return FALSE; + if (!IsReady()) { + SetStatusCode(STATUS_NOTREADY); + return false; } // Error if the total number of blocks is exceeded if (block >= disk.blocks) { - disk.code = DISK_INVALIDLBA; - return FALSE; + SetStatusCode(STATUS_INVALIDLBA); + return false; } // Error if write protected - if (disk.writep) { - disk.code = DISK_WRITEPROTECT; - return FALSE; + if (IsProtected()) { + SetStatusCode(STATUS_WRITEPROTECT); + return false; } // Leave it to the cache if (!disk.dcache->Write(buf, block)) { - disk.code = DISK_WRITEFAULT; - return FALSE; + SetStatusCode(STATUS_WRITEFAULT); + return false; } - // Success - disk.code = DISK_NOERROR; - return TRUE; + return true; +} + +void Disk::Seek(SASIDEV *controller) +{ + if (!CheckReady()) { + controller->Error(); + return; + } + + controller->Status(); } //--------------------------------------------------------------------------- // -// SEEK -// *Does not check LBA (SASI IOCS) +// SEEK(6) +// Does not check LBA (SASI IOCS) // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Seek(const DWORD* /*cdb*/) +void Disk::Seek6(SASIDEV *controller) { - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // SEEK Success - return TRUE; + Seek(controller); } //--------------------------------------------------------------------------- // -// ASSIGN +// SEEK(10) // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Assign(const DWORD* /*cdb*/) +void Disk::Seek10(SASIDEV *controller) { - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// SPECIFY -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Specify(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // Success - return TRUE; + Seek(controller); } //--------------------------------------------------------------------------- @@ -2120,27 +1311,35 @@ BOOL FASTCALL Disk::Specify(const DWORD* /*cdb*/) // START STOP UNIT // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::StartStop(const DWORD *cdb) +bool Disk::StartStop(const DWORD *cdb) { - ASSERT(this); ASSERT(cdb); - ASSERT(cdb[0] == 0x1b); + + bool start = cdb[4] & 0x01; + bool load = cdb[4] & 0x02; + + if (load) { + LOGTRACE("%s", start ? "Loading medium" : "Ejecting medium"); + } + else { + LOGTRACE("%s", start ? "Starting unit" : "Stopping unit"); + + SetStopped(!start); + } // Look at the eject bit and eject if necessary - if (cdb[4] & 0x02) { - if (disk.lock) { + if (load && !start) { + if (IsLocked()) { // Cannot be ejected because it is locked - disk.code = DISK_PREVENT; - return FALSE; + SetStatusCode(STATUS_PREVENT); + return false; } // Eject - Eject(FALSE); + return Eject(false); } - // OK - disk.code = DISK_NOERROR; - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -2148,54 +1347,23 @@ BOOL FASTCALL Disk::StartStop(const DWORD *cdb) // SEND DIAGNOSTIC // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::SendDiag(const DWORD *cdb) +bool Disk::SendDiag(const DWORD *cdb) { - ASSERT(this); ASSERT(cdb); - ASSERT(cdb[0] == 0x1d); // Do not support PF bit if (cdb[1] & 0x10) { - disk.code = DISK_INVALIDCDB; - return FALSE; + SetStatusCode(STATUS_INVALIDCDB); + return false; } // Do not support parameter list if ((cdb[3] != 0) || (cdb[4] != 0)) { - disk.code = DISK_INVALIDCDB; - return FALSE; + SetStatusCode(STATUS_INVALIDCDB); + return false; } - // Always successful - disk.code = DISK_NOERROR; - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// PREVENT/ALLOW MEDIUM REMOVAL -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Removal(const DWORD *cdb) -{ - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x1e); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // Set Lock flag - if (cdb[4] & 0x01) { - disk.lock = TRUE; - } else { - disk.lock = FALSE; - } - - // REMOVAL Success - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -2203,144 +1371,364 @@ BOOL FASTCALL Disk::Removal(const DWORD *cdb) // READ CAPACITY // //--------------------------------------------------------------------------- -int FASTCALL Disk::ReadCapacity(const DWORD* /*cdb*/, BYTE *buf) +void Disk::ReadCapacity10(SASIDEV *controller) { - DWORD blocks; - DWORD length; + BYTE *buf = ctrl->buffer; - ASSERT(this); ASSERT(buf); - // Buffer clear memset(buf, 0, 8); - // Status check if (!CheckReady()) { - return 0; + controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::MEDIUM_NOT_PRESENT); + return; + } + + if (disk.blocks <= 0) { + controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::MEDIUM_NOT_PRESENT); + + LOGWARN("%s Capacity not available, medium may not be present", __PRETTY_FUNCTION__); + + return; } // Create end of logical block address (disk.blocks-1) - ASSERT(disk.blocks > 0); - blocks = disk.blocks - 1; + uint32_t blocks = disk.blocks - 1; buf[0] = (BYTE)(blocks >> 24); buf[1] = (BYTE)(blocks >> 16); buf[2] = (BYTE)(blocks >> 8); buf[3] = (BYTE)blocks; // Create block length (1 << disk.size) - length = 1 << disk.size; + uint32_t length = 1 << disk.size; buf[4] = (BYTE)(length >> 24); buf[5] = (BYTE)(length >> 16); buf[6] = (BYTE)(length >> 8); buf[7] = (BYTE)length; - // return the size - return 8; + // the size + ctrl->length = 8; + + controller->DataIn(); } -//--------------------------------------------------------------------------- -// -// VERIFY -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Verify(const DWORD *cdb) +void Disk::ReadCapacity16(SASIDEV *controller) { - DWORD record; - DWORD blocks; + BYTE *buf = ctrl->buffer; - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x2f); - - // Get parameters - record = cdb[2]; - record <<= 8; - record |= cdb[3]; - record <<= 8; - record |= cdb[4]; - record <<= 8; - record |= cdb[5]; - blocks = cdb[7]; - blocks <<= 8; - blocks |= cdb[8]; - - // Status check - if (!CheckReady()) { - return 0; - } - - // Parameter check - if (disk.blocks < (record + blocks)) { - disk.code = DISK_INVALIDLBA; - return FALSE; - } - - // Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// READ TOC -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::ReadToc(const DWORD *cdb, BYTE *buf) -{ - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x43); ASSERT(buf); - // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; + memset(buf, 0, 14); + + if (!CheckReady() || disk.blocks <= 0) { + controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::MEDIUM_NOT_PRESENT); + return; + } + + // Create end of logical block address (disk.blocks-1) + uint64_t blocks = disk.blocks - 1; + buf[0] = (BYTE)(blocks >> 56); + buf[1] = (BYTE)(blocks >> 48); + buf[2] = (BYTE)(blocks >> 40); + buf[3] = (BYTE)(blocks >> 32); + buf[4] = (BYTE)(blocks >> 24); + buf[5] = (BYTE)(blocks >> 16); + buf[6] = (BYTE)(blocks >> 8); + buf[7] = (BYTE)blocks; + + // Create block length (1 << disk.size) + uint32_t length = 1 << disk.size; + buf[8] = (BYTE)(length >> 24); + buf[9] = (BYTE)(length >> 16); + buf[10] = (BYTE)(length >> 8); + buf[11] = (BYTE)length; + + // Logical blocks per physical block: not reported (1 or more) + buf[13] = 0; + + // the size + ctrl->length = 14; + + controller->DataIn(); } //--------------------------------------------------------------------------- // -// PLAY AUDIO +// REPORT LUNS // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::PlayAudio(const DWORD *cdb) +void Disk::ReportLuns(SASIDEV *controller) { - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x45); + BYTE *buf = ctrl->buffer; - // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; + ASSERT(buf); + + if (!CheckReady()) { + controller->Error(); + return; + } + + int allocation_length = (ctrl->cmd[6] << 24) + (ctrl->cmd[7] << 16) + (ctrl->cmd[8] << 8) + ctrl->cmd[9]; + memset(buf, 0, allocation_length); + + // Count number of available LUNs for the current device + int luns; + for (luns = 0; luns < controller->GetCtrl()->device->GetSupportedLuns(); luns++) { + if (!controller->GetCtrl()->unit[luns]) { + break; + } + } + + // LUN list length, 8 bytes per LUN + // SCSI standard: The contents of the LUN LIST LENGTH field are not altered based on the allocation length + buf[0] = (luns * 8) >> 24; + buf[1] = (luns * 8) >> 16; + buf[2] = (luns * 8) >> 8; + buf[3] = luns * 8; + + ctrl->length = allocation_length < 8 + luns * 8 ? allocation_length : 8 + luns * 8; + + controller->DataIn(); } //--------------------------------------------------------------------------- // -// PLAY AUDIO MSF +// RESERVE(6) +// +// The reserve/release commands are only used in multi-initiator +// environments. RaSCSI doesn't support this use case. However, some old +// versions of Solaris will issue the reserve/release commands. We will +// just respond with an OK status. // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::PlayAudioMSF(const DWORD *cdb) +void Disk::Reserve6(SASIDEV *controller) { - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x47); - - // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; + controller->Status(); } //--------------------------------------------------------------------------- // -// PLAY AUDIO TRACK +// RESERVE(10) +// +// The reserve/release commands are only used in multi-initiator +// environments. RaSCSI doesn't support this use case. However, some old +// versions of Solaris will issue the reserve/release commands. We will +// just respond with an OK status. // //--------------------------------------------------------------------------- -BOOL FASTCALL Disk::PlayAudioTrack(const DWORD *cdb) +void Disk::Reserve10(SASIDEV *controller) { - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x48); - - // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; + controller->Status(); } +//--------------------------------------------------------------------------- +// +// RELEASE(6) +// +// The reserve/release commands are only used in multi-initiator +// environments. RaSCSI doesn't support this use case. However, some old +// versions of Solaris will issue the reserve/release commands. We will +// just respond with an OK status. +// +//--------------------------------------------------------------------------- +void Disk::Release6(SASIDEV *controller) +{ + controller->Status(); +} + +//--------------------------------------------------------------------------- +// +// RELEASE(10) +// +// The reserve/release commands are only used in multi-initiator +// environments. RaSCSI doesn't support this use case. However, some old +// versions of Solaris will issue the reserve/release commands. We will +// just respond with an OK status. +// +//--------------------------------------------------------------------------- +void Disk::Release10(SASIDEV *controller) +{ + controller->Status(); +} + +//--------------------------------------------------------------------------- +// +// Get start sector and sector count for a READ/WRITE operation +// +//--------------------------------------------------------------------------- +bool Disk::GetStartAndCount(SASIDEV *controller, uint64_t& start, uint32_t& count, access_mode mode) +{ + if (mode == RW6) { + start = ctrl->cmd[1] & 0x1f; + start <<= 8; + start |= ctrl->cmd[2]; + start <<= 8; + start |= ctrl->cmd[3]; + + count = ctrl->cmd[4]; + if (!count) { + count= 0x100; + } + } + else { + start = ctrl->cmd[2]; + start <<= 8; + start |= ctrl->cmd[3]; + start <<= 8; + start |= ctrl->cmd[4]; + start <<= 8; + start |= ctrl->cmd[5]; + if (mode == RW16) { + start <<= 8; + start |= ctrl->cmd[6]; + start <<= 8; + start |= ctrl->cmd[7]; + start <<= 8; + start |= ctrl->cmd[8]; + start <<= 8; + start |= ctrl->cmd[9]; + } + + if (mode == RW16) { + count = ctrl->cmd[10]; + count <<= 8; + count |= ctrl->cmd[11]; + count <<= 8; + count |= ctrl->cmd[12]; + count <<= 8; + count |= ctrl->cmd[13]; + } + else { + count = ctrl->cmd[7]; + count <<= 8; + count |= ctrl->cmd[8]; + } + } + + // Check capacity + uint64_t capacity = GetBlockCount(); + if (start > capacity || start + count > capacity) { + ostringstream s; + s << "Capacity of " << capacity << " blocks exceeded: " + << "Trying to read block " << start << ", block count " << ctrl->blocks; + LOGDEBUG("%s", s.str().c_str()); + controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE); + return false; + } + + // Do not process 0 blocks + if (!count) { + LOGTRACE("NOT processing 0 blocks"); + controller->Status(); + return false; + } + + return true; +} + +uint32_t Disk::GetSectorSizeInBytes() const +{ + return disk.size ? 1 << disk.size : 0; +} + +void Disk::SetSectorSizeInBytes(uint32_t size, bool sasi) +{ + set sector_sizes = DeviceFactory::instance().GetSectorSizes(GetType()); + if (!sector_sizes.empty() && sector_sizes.find(size) == sector_sizes.end()) { + stringstream error; + error << "Invalid block size of " << size << " bytes"; + throw io_exception(error.str()); + } + + switch (size) { + case 256: + disk.size = 8; + break; + + case 512: + disk.size = 9; + break; + + case 1024: + disk.size = 10; + break; + + case 2048: + disk.size = 11; + break; + + case 4096: + disk.size = 12; + break; + + default: + assert(false); + break; + } +} + +uint32_t Disk::GetSectorSizeShiftCount() const +{ + return disk.size; +} + +void Disk::SetSectorSizeShiftCount(uint32_t size) +{ + disk.size = size; +} + +bool Disk::IsSectorSizeConfigurable() const +{ + return !sector_sizes.empty(); +} + +void Disk::SetSectorSizes(const set& sector_sizes) +{ + this->sector_sizes = sector_sizes; +} + +uint32_t Disk::GetConfiguredSectorSize() const +{ + return configured_sector_size; +} + +bool Disk::SetConfiguredSectorSize(uint32_t configured_sector_size) +{ + DeviceFactory& device_factory = DeviceFactory::instance(); + + set sector_sizes = device_factory.GetSectorSizes(GetType()); + if (sector_sizes.find(configured_sector_size) == sector_sizes.end()) { + return false; + } + + this->configured_sector_size = configured_sector_size; + + return true; +} + +void Disk::SetGeometries(const map& geometries) +{ + this->geometries = geometries; +} + +bool Disk::SetGeometryForCapacity(uint64_t capacity) { + const auto& geometry = geometries.find(capacity); + if (geometry != geometries.end()) { + SetSectorSizeInBytes(geometry->second.first, false); + SetBlockCount(geometry->second.second); + + return true; + } + + return false; +} + +uint64_t Disk::GetBlockCount() const +{ + return disk.blocks; +} + +void Disk::SetBlockCount(uint32_t blocks) +{ + disk.blocks = blocks; +} diff --git a/src/raspberrypi/devices/disk.h b/src/raspberrypi/devices/disk.h index e2c7ef1d..47e2a0f0 100644 --- a/src/raspberrypi/devices/disk.h +++ b/src/raspberrypi/devices/disk.h @@ -17,258 +17,155 @@ #pragma once -#include "xm6.h" #include "log.h" #include "scsi.h" +#include "controllers/scsidev_ctrl.h" +#include "device.h" +#include "device_factory.h" +#include "disk_track_cache.h" +#include "file_support.h" #include "filepath.h" +#include +#include +#include -//--------------------------------------------------------------------------- -// -// Error definition (sense code returned by REQUEST SENSE) -// -// MSB Reserved (0x00) -// Sense Key -// Additional Sense Code (ASC) -// LSB Additional Sense Code Qualifier(ASCQ) -// -//--------------------------------------------------------------------------- -#define DISK_NOERROR 0x00000000 // NO ADDITIONAL SENSE INFO. -#define DISK_DEVRESET 0x00062900 // POWER ON OR RESET OCCURED -#define DISK_NOTREADY 0x00023a00 // MEDIUM NOT PRESENT -#define DISK_ATTENTION 0x00062800 // MEDIUM MAY HAVE CHANGED -#define DISK_PREVENT 0x00045302 // MEDIUM REMOVAL PREVENTED -#define DISK_READFAULT 0x00031100 // UNRECOVERED READ ERROR -#define DISK_WRITEFAULT 0x00030300 // PERIPHERAL DEVICE WRITE FAULT -#define DISK_WRITEPROTECT 0x00042700 // WRITE PROTECTED -#define DISK_MISCOMPARE 0x000e1d00 // MISCOMPARE DURING VERIFY -#define DISK_INVALIDCMD 0x00052000 // INVALID COMMAND OPERATION CODE -#define DISK_INVALIDLBA 0x00052100 // LOGICAL BLOCK ADDR. OUT OF RANGE -#define DISK_INVALIDCDB 0x00052400 // INVALID FIELD IN CDB -#define DISK_INVALIDLUN 0x00052500 // LOGICAL UNIT NOT SUPPORTED -#define DISK_INVALIDPRM 0x00052600 // INVALID FIELD IN PARAMETER LIST -#define DISK_INVALIDMSG 0x00054900 // INVALID MESSAGE ERROR -#define DISK_PARAMLEN 0x00051a00 // PARAMETERS LIST LENGTH ERROR -#define DISK_PARAMNOT 0x00052601 // PARAMETERS NOT SUPPORTED -#define DISK_PARAMVALUE 0x00052602 // PARAMETERS VALUE INVALID -#define DISK_PARAMSAVE 0x00053900 // SAVING PARAMETERS NOT SUPPORTED -#define DISK_NODEFECT 0x00010000 // DEFECT LIST NOT FOUND +#include "../rascsi.h" +#include "interfaces/scsi_block_commands.h" +#include "interfaces/scsi_primary_commands.h" -#if 0 -#define DISK_AUDIOPROGRESS 0x00??0011 // AUDIO PLAY IN PROGRESS -#define DISK_AUDIOPAUSED 0x00??0012 // AUDIO PLAY PAUSED -#define DISK_AUDIOSTOPPED 0x00??0014 // AUDIO PLAY STOPPED DUE TO ERROR -#define DISK_AUDIOCOMPLETE 0x00??0013 // AUDIO PLAY SUCCESSFULLY COMPLETED -#endif - - -#ifdef RASCSI -#define BENDER_SIGNATURE "RaSCSI" -// The following line was to mimic Apple's CDROM ID -// #define BENDER_SIGNATURE "SONY " -#else -#define BENDER_SIGNATURE "XM6" -#endif - -//=========================================================================== -// -// Disk Track -// -//=========================================================================== -class DiskTrack +class Disk : public Device, ScsiPrimaryCommands, ScsiBlockCommands { -public: - // Internal data definition - typedef struct { - int track; // Track Number - int size; // Sector Size(8 or 9) - int sectors; // Number of sectors(<=0x100) - DWORD length; // Data buffer length - BYTE *buffer; // Data buffer - BOOL init; // Is it initilized? - BOOL changed; // Changed flag - DWORD maplen; // Changed map length - BOOL *changemap; // Changed map - BOOL raw; // RAW mode flag - off64_t imgoffset; // Offset to actual data - } disktrk_t; - -public: - // Basic Functions - DiskTrack(); // Constructor - virtual ~DiskTrack(); // Destructor - void FASTCALL Init(int track, int size, int sectors, BOOL raw = FALSE, off64_t imgoff = 0);// Initialization - BOOL FASTCALL Load(const Filepath& path); // Load - BOOL FASTCALL Save(const Filepath& path); // Save - - // Read / Write - BOOL FASTCALL Read(BYTE *buf, int sec) const; // Sector Read - BOOL FASTCALL Write(const BYTE *buf, int sec); // Sector Write - - // Other - int FASTCALL GetTrack() const { return dt.track; } // Get track - BOOL FASTCALL IsChanged() const { return dt.changed; } // Changed flag check - private: - // Internal data - disktrk_t dt; // Internal data -}; + enum access_mode { RW6, RW10, RW16 }; + + // The supported configurable block sizes, empty if not configurable + set sector_sizes; + uint32_t configured_sector_size; + + // The mapping of supported capacities to block sizes and block counts, empty if there is no capacity restriction + map geometries; + + SASIDEV::ctrl_t *ctrl; -//=========================================================================== -// -// Disk Cache -// -//=========================================================================== -class DiskCache -{ -public: - // Internal data definition typedef struct { - DiskTrack *disktrk; // Disk Track - DWORD serial; // Serial - } cache_t; - - // Number of caches - enum { - CacheMax = 16 // Number of tracks to cache - }; - -public: - // Basic Functions - DiskCache(const Filepath& path, int size, int blocks,off64_t imgoff = 0);// Constructor - virtual ~DiskCache(); // Destructor - void FASTCALL SetRawMode(BOOL raw); // CD-ROM raw mode setting - - // Access - BOOL FASTCALL Save(); // Save and release all - BOOL FASTCALL Read(BYTE *buf, int block); // Sector Read - BOOL FASTCALL Write(const BYTE *buf, int block); // Sector Write - BOOL FASTCALL GetCache(int index, int& track, DWORD& serial) const; // Get cache information - -private: - // Internal Management - void FASTCALL Clear(); // Clear all tracks - DiskTrack* FASTCALL Assign(int track); // Load track - BOOL FASTCALL Load(int index, int track, DiskTrack *disktrk = NULL); // Load track - void FASTCALL Update(); // Update serial number - - // Internal data - cache_t cache[CacheMax]; // Cache management - DWORD serial; // Last serial number - Filepath sec_path; // Path - int sec_size; // Sector size (8 or 9 or 11) - int sec_blocks; // Blocks per sector - BOOL cd_raw; // CD-ROM RAW mode - off64_t imgoffset; // Offset to actual data -}; - -//=========================================================================== -// -// Disk -// -//=========================================================================== -class Disk -{ -public: - // Internal data structure - typedef struct { - DWORD id; // Media ID - BOOL ready; // Valid Disk - BOOL writep; // Write protected - BOOL readonly; // Read only - BOOL removable; // Removable - BOOL lock; // Locked - BOOL attn; // Attention - BOOL reset; // Reset - int size; // Sector Size - DWORD blocks; // Total number of sectors - DWORD lun; // LUN - DWORD code; // Status code + uint32_t size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096) + // TODO blocks should be a 64 bit value in order to support higher capacities + uint32_t blocks; // Total number of sectors DiskCache *dcache; // Disk cache - off64_t imgoffset; // Offset to actual data + off_t image_offset; // Offset to actual data } disk_t; -public: - // Basic Functions - Disk(); // Constructor - virtual ~Disk(); // Destructor - virtual void FASTCALL Reset(); // Device Reset - #ifndef RASCSI - virtual BOOL FASTCALL Save(Fileio *fio, int ver); // Save - virtual BOOL FASTCALL Load(Fileio *fio, int ver); // Load - #endif // RASCSI + typedef struct _command_t { + const char* name; + void (Disk::*execute)(SASIDEV *); - // ID - DWORD FASTCALL GetID() const { return disk.id; } // Get media ID - BOOL FASTCALL IsNULL() const; // NULL check - BOOL FASTCALL IsSASI() const; // SASI Check - BOOL FASTCALL IsSCSI() const; // SASI Check + _command_t(const char* _name, void (Disk::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { }; + } command_t; + std::map commands; + + void AddCommand(SCSIDEV::scsi_command, const char*, void (Disk::*)(SASIDEV *)); + +public: + Disk(std::string); + virtual ~Disk(); + + virtual bool Dispatch(SCSIDEV *) override; + + void ReserveFile(const string&); // Media Operations - virtual BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); // Open - void FASTCALL GetPath(Filepath& path) const; // Get the path - void FASTCALL Eject(BOOL force); // Eject - BOOL FASTCALL IsReady() const { return disk.ready; } // Ready check - void FASTCALL WriteP(BOOL flag); // Set Write Protect flag - BOOL FASTCALL IsWriteP() const { return disk.writep; } // Get write protect flag - BOOL FASTCALL IsReadOnly() const { return disk.readonly; } // Get read only flag - BOOL FASTCALL IsRemovable() const { return disk.removable; } // Get is removable flag - BOOL FASTCALL IsLocked() const { return disk.lock; } // Get locked status - BOOL FASTCALL IsAttn() const { return disk.attn; } // Get attention flag - BOOL FASTCALL Flush(); // Flush the cache - void FASTCALL GetDisk(disk_t *buffer) const; // Get the internal data struct + virtual void Open(const Filepath& path); + void GetPath(Filepath& path) const; + bool Eject(bool) override; - // Properties - void FASTCALL SetLUN(DWORD lun) { disk.lun = lun; } // LUN set - DWORD FASTCALL GetLUN() { return disk.lun; } // LUN get + // Commands covered by the SCSI specification (see https://www.t10.org/drafts.htm) + virtual void TestUnitReady(SASIDEV *) override; + void Inquiry(SASIDEV *) override; + void RequestSense(SASIDEV *) override; + void ModeSelect6(SASIDEV *); + void ModeSelect10(SASIDEV *); + void ModeSense6(SASIDEV *); + void ModeSense10(SASIDEV *); + void Rezero(SASIDEV *); + void FormatUnit(SASIDEV *) override; + void ReassignBlocks(SASIDEV *); + void StartStopUnit(SASIDEV *); + void SendDiagnostic(SASIDEV *); + void PreventAllowMediumRemoval(SASIDEV *); + void SynchronizeCache10(SASIDEV *); + void SynchronizeCache16(SASIDEV *); + void ReadDefectData10(SASIDEV *); + virtual void Read6(SASIDEV *); + void Read10(SASIDEV *) override; + void Read16(SASIDEV *) override; + virtual void Write6(SASIDEV *); + void Write10(SASIDEV *) override; + void Write16(SASIDEV *) override; + void Verify10(SASIDEV *) override; + void Verify16(SASIDEV *) override; + void Seek(SASIDEV *); + void Seek6(SASIDEV *); + void Seek10(SASIDEV *); + void ReadCapacity10(SASIDEV *) override; + void ReadCapacity16(SASIDEV *) override; + void ReportLuns(SASIDEV *) override; + void Reserve6(SASIDEV *); + void Reserve10(SASIDEV *); + void Release6(SASIDEV *); + void Release10(SASIDEV *); - // commands - virtual int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor);// INQUIRY command - virtual int FASTCALL RequestSense(const DWORD *cdb, BYTE *buf); // REQUEST SENSE command - int FASTCALL SelectCheck(const DWORD *cdb); // SELECT check - int FASTCALL SelectCheck10(const DWORD *cdb); // SELECT(10) check - virtual BOOL FASTCALL ModeSelect(const DWORD *cdb, const BYTE *buf, int length);// MODE SELECT command - virtual int FASTCALL ModeSense(const DWORD *cdb, BYTE *buf); // MODE SENSE command - virtual int FASTCALL ModeSense10(const DWORD *cdb, BYTE *buf); // MODE SENSE(10) command - int FASTCALL ReadDefectData10(const DWORD *cdb, BYTE *buf); // READ DEFECT DATA(10) command - virtual BOOL FASTCALL TestUnitReady(const DWORD *cdb); // TEST UNIT READY command - BOOL FASTCALL Rezero(const DWORD *cdb); // REZERO command - BOOL FASTCALL Format(const DWORD *cdb); // FORMAT UNIT command - BOOL FASTCALL Reassign(const DWORD *cdb); // REASSIGN UNIT command - virtual int FASTCALL Read(BYTE *buf, DWORD block); // READ command - int FASTCALL WriteCheck(DWORD block); // WRITE check - BOOL FASTCALL Write(const BYTE *buf, DWORD block); // WRITE command - BOOL FASTCALL Seek(const DWORD *cdb); // SEEK command - BOOL FASTCALL Assign(const DWORD *cdb); // ASSIGN command - BOOL FASTCALL Specify(const DWORD *cdb); // SPECIFY command - BOOL FASTCALL StartStop(const DWORD *cdb); // START STOP UNIT command - BOOL FASTCALL SendDiag(const DWORD *cdb); // SEND DIAGNOSTIC command - BOOL FASTCALL Removal(const DWORD *cdb); // PREVENT/ALLOW MEDIUM REMOVAL command - int FASTCALL ReadCapacity(const DWORD *cdb, BYTE *buf); // READ CAPACITY command - BOOL FASTCALL Verify(const DWORD *cdb); // VERIFY command - virtual int FASTCALL ReadToc(const DWORD *cdb, BYTE *buf); // READ TOC command - virtual BOOL FASTCALL PlayAudio(const DWORD *cdb); // PLAY AUDIO command - virtual BOOL FASTCALL PlayAudioMSF(const DWORD *cdb); // PLAY AUDIO MSF command - virtual BOOL FASTCALL PlayAudioTrack(const DWORD *cdb); // PLAY AUDIO TRACK command - void FASTCALL InvalidCmd() { disk.code = DISK_INVALIDCMD; } // Unsupported command + // Command helpers + virtual int Inquiry(const DWORD *cdb, BYTE *buf) = 0; // INQUIRY command + virtual int WriteCheck(DWORD block); // WRITE check + virtual bool Write(const DWORD *cdb, const BYTE *buf, DWORD block); // WRITE command + bool StartStop(const DWORD *cdb); // START STOP UNIT command + bool SendDiag(const DWORD *cdb); // SEND DIAGNOSTIC command - // Other - BOOL IsCacheWB() { return cache_wb; } // Get cache writeback mode - void SetCacheWB(BOOL enable) { cache_wb = enable; } // Set cache writeback mode + virtual int Read(const DWORD *cdb, BYTE *buf, uint64_t block); + int ReadDefectData10(const DWORD *cdb, BYTE *buf); + + uint32_t GetSectorSizeInBytes() const; + void SetSectorSizeInBytes(uint32_t, bool); + uint32_t GetSectorSizeShiftCount() const; + void SetSectorSizeShiftCount(uint32_t); + bool IsSectorSizeConfigurable() const; + set GetSectorSizes() const; + void SetSectorSizes(const set&); + uint32_t GetConfiguredSectorSize() const; + bool SetConfiguredSectorSize(uint32_t); + void SetGeometries(const map&); + bool SetGeometryForCapacity(uint64_t); + uint64_t GetBlockCount() const; + void SetBlockCount(uint32_t); + bool GetStartAndCount(SASIDEV *, uint64_t&, uint32_t&, access_mode); + bool CheckReady(); + + // TODO This method should not be called by SASIDEV + virtual bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length); protected: - // Internal processing - virtual int FASTCALL AddError(BOOL change, BYTE *buf); // Add error - virtual int FASTCALL AddFormat(BOOL change, BYTE *buf); // Add format - virtual int FASTCALL AddDrive(BOOL change, BYTE *buf); // Add drive - int FASTCALL AddOpt(BOOL change, BYTE *buf); // Add optical - int FASTCALL AddCache(BOOL change, BYTE *buf); // Add cache - int FASTCALL AddCDROM(BOOL change, BYTE *buf); // Add CD-ROM - int FASTCALL AddCDDA(BOOL change, BYTE *buf); // Add CD_DA - virtual int FASTCALL AddVendor(int page, BOOL change, BYTE *buf); // Add vendor special info - BOOL FASTCALL CheckReady(); // Check if ready + virtual int AddErrorPage(bool change, BYTE *buf); + virtual int AddFormatPage(bool change, BYTE *buf); + virtual int AddDrivePage(bool change, BYTE *buf); + virtual int AddVendorPage(int page, bool change, BYTE *buf); + int AddOptionPage(bool change, BYTE *buf); + int AddCachePage(bool change, BYTE *buf); + int AddCDROMPage(bool change, BYTE *buf); + int AddCDDAPage(bool, BYTE *buf); - // Internal data - disk_t disk; // Internal disk data - Filepath diskpath; // File path (for GetPath) - BOOL cache_wb; // Cache mode + virtual int RequestSense(const DWORD *cdb, BYTE *buf); + + // Internal disk data + disk_t disk; + +private: + void Read(SASIDEV *, uint64_t); + void Write(SASIDEV *, uint64_t); + void Verify(SASIDEV *, uint64_t); + bool Format(const DWORD *cdb); + int ModeSense6(const DWORD *cdb, BYTE *buf); + int ModeSense10(const DWORD *cdb, BYTE *buf); + int ModeSelectCheck(const DWORD *cdb, int length); + int ModeSelectCheck6(const DWORD *cdb); + int ModeSelectCheck10(const DWORD *cdb); }; diff --git a/src/raspberrypi/devices/disk_track_cache.cpp b/src/raspberrypi/devices/disk_track_cache.cpp new file mode 100644 index 00000000..9b5befd1 --- /dev/null +++ b/src/raspberrypi/devices/disk_track_cache.cpp @@ -0,0 +1,656 @@ +//--------------------------------------------------------------------------- +// +// X68000 EMULATOR "XM6" +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// +// XM6i +// Copyright (C) 2010-2015 isaki@NetBSD.org +// Copyright (C) 2010 Y.Sugahara +// +// Imported sava's Anex86/T98Next image and MO format support patch. +// Imported NetBSD support and some optimisation patch by Rin Okuyama. +// Comments translated to english by akuker. +// +// [ DiskTrack and DiskCache ] +// +//--------------------------------------------------------------------------- + +#include "os.h" +#include "log.h" +#include "fileio.h" +#include "disk_track_cache.h" + +//=========================================================================== +// +// Disk Track +// +//=========================================================================== + +DiskTrack::DiskTrack() +{ + // Initialization of internal information + dt.track = 0; + dt.size = 0; + dt.sectors = 0; + dt.raw = FALSE; + dt.init = FALSE; + dt.changed = FALSE; + dt.length = 0; + dt.buffer = NULL; + dt.maplen = 0; + dt.changemap = NULL; + dt.imgoffset = 0; +} + +DiskTrack::~DiskTrack() +{ + // Release memory, but do not save automatically + if (dt.buffer) { + free(dt.buffer); + dt.buffer = NULL; + } + if (dt.changemap) { + free(dt.changemap); + dt.changemap = NULL; + } +} + +//--------------------------------------------------------------------------- +// +// Initialization +// +//--------------------------------------------------------------------------- +void DiskTrack::Init(int track, int size, int sectors, BOOL raw, off_t imgoff) +{ + ASSERT(track >= 0); + ASSERT((sectors > 0) && (sectors <= 0x100)); + ASSERT(imgoff >= 0); + + // Set Parameters + dt.track = track; + dt.size = size; + dt.sectors = sectors; + dt.raw = raw; + + // Not initialized (needs to be loaded) + dt.init = FALSE; + + // Not Changed + dt.changed = FALSE; + + // Offset to actual data + dt.imgoffset = imgoff; +} + +//--------------------------------------------------------------------------- +// +// Load +// +//--------------------------------------------------------------------------- +bool DiskTrack::Load(const Filepath& path) +{ + Fileio fio; + + // Not needed if already loaded + if (dt.init) { + ASSERT(dt.buffer); + ASSERT(dt.changemap); + return true; + } + + // Calculate offset (previous tracks are considered to hold 256 sectors) + off_t offset = ((off_t)dt.track << 8); + if (dt.raw) { + ASSERT(dt.size == 11); + offset *= 0x930; + offset += 0x10; + } else { + offset <<= dt.size; + } + + // Add offset to real image + offset += dt.imgoffset; + + // Calculate length (data size of this track) + int length = dt.sectors << dt.size; + + // Allocate buffer memory + ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); + + if (dt.buffer == NULL) { + if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) { + LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__); + } + dt.length = length; + } + + if (!dt.buffer) { + return false; + } + + // Reallocate if the buffer length is different + if (dt.length != (DWORD)length) { + free(dt.buffer); + if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) { + LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__); + } + dt.length = length; + } + + // Reserve change map memory + if (dt.changemap == NULL) { + dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL)); + dt.maplen = dt.sectors; + } + + if (!dt.changemap) { + return false; + } + + // Reallocate if the buffer length is different + if (dt.maplen != (DWORD)dt.sectors) { + free(dt.changemap); + dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL)); + dt.maplen = dt.sectors; + } + + // Clear changemap + memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL)); + + // Read from File + if (!fio.OpenDIO(path, Fileio::ReadOnly)) { + return false; + } + if (dt.raw) { + // Split Reading + for (int i = 0; i < dt.sectors; i++) { + // Seek + if (!fio.Seek(offset)) { + fio.Close(); + return false; + } + + // Read + if (!fio.Read(&dt.buffer[i << dt.size], 1 << dt.size)) { + fio.Close(); + return false; + } + + // Next offset + offset += 0x930; + } + } else { + // Continuous reading + if (!fio.Seek(offset)) { + fio.Close(); + return false; + } + if (!fio.Read(dt.buffer, length)) { + fio.Close(); + return false; + } + } + fio.Close(); + + // Set a flag and end normally + dt.init = TRUE; + dt.changed = FALSE; + return true; +} + +//--------------------------------------------------------------------------- +// +// Save +// +//--------------------------------------------------------------------------- +bool DiskTrack::Save(const Filepath& path) +{ + // Not needed if not initialized + if (!dt.init) { + return true; + } + + // Not needed unless changed + if (!dt.changed) { + return true; + } + + // Need to write + ASSERT(dt.buffer); + ASSERT(dt.changemap); + ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); + + // Writing in RAW mode is not allowed + ASSERT(!dt.raw); + + // Calculate offset (previous tracks are considered to hold 256 sectors) + off_t offset = ((off_t)dt.track << 8); + offset <<= dt.size; + + // Add offset to real image + offset += dt.imgoffset; + + // Calculate length per sector + int length = 1 << dt.size; + + // Open file + Fileio fio; + if (!fio.Open(path, Fileio::ReadWrite)) { + return false; + } + + // Partial write loop + int total; + for (int i = 0; i < dt.sectors;) { + // If changed + if (dt.changemap[i]) { + // Initialize write size + total = 0; + + // Seek + if (!fio.Seek(offset + ((off_t)i << dt.size))) { + fio.Close(); + return false; + } + + // Consectutive sector length + int j; + for (j = i; j < dt.sectors; j++) { + // end when interrupted + if (!dt.changemap[j]) { + break; + } + + // Add one sector + total += length; + } + + // Write + if (!fio.Write(&dt.buffer[i << dt.size], total)) { + fio.Close(); + return false; + } + + // To unmodified sector + i = j; + } else { + // Next Sector + i++; + } + } + + // Close + fio.Close(); + + // Drop the change flag and exit + memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL)); + dt.changed = FALSE; + return true; +} + +//--------------------------------------------------------------------------- +// +// Read Sector +// +//--------------------------------------------------------------------------- +bool DiskTrack::Read(BYTE *buf, int sec) const +{ + ASSERT(buf); + ASSERT((sec >= 0) & (sec < 0x100)); + + LOGTRACE("%s reading sector: %d", __PRETTY_FUNCTION__,sec); + // Error if not initialized + if (!dt.init) { + return false; + } + + // // Error if the number of sectors exceeds the valid number + if (sec >= dt.sectors) { + return false; + } + + // Copy + ASSERT(dt.buffer); + ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); + memcpy(buf, &dt.buffer[(off_t)sec << dt.size], (off_t)1 << dt.size); + + // Success + return true; +} + +//--------------------------------------------------------------------------- +// +// Write Sector +// +//--------------------------------------------------------------------------- +bool DiskTrack::Write(const BYTE *buf, int sec) +{ + ASSERT(buf); + ASSERT((sec >= 0) & (sec < 0x100)); + ASSERT(!dt.raw); + + // Error if not initialized + if (!dt.init) { + return false; + } + + // // Error if the number of sectors exceeds the valid number + if (sec >= dt.sectors) { + return false; + } + + // Calculate offset and length + int offset = sec << dt.size; + int length = 1 << dt.size; + + // Compare + ASSERT(dt.buffer); + ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); + if (memcmp(buf, &dt.buffer[offset], length) == 0) { + // Exit normally since it's attempting to write the same thing + return true; + } + + // Copy, change + memcpy(&dt.buffer[offset], buf, length); + dt.changemap[sec] = TRUE; + dt.changed = TRUE; + + // Success + return true; +} + +//=========================================================================== +// +// Disk Cache +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +DiskCache::DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff) +{ + ASSERT(blocks > 0); + ASSERT(imgoff >= 0); + + // Cache work + for (int i = 0; i < CacheMax; i++) { + cache[i].disktrk = NULL; + cache[i].serial = 0; + } + + // Other + serial = 0; + sec_path = path; + sec_size = size; + sec_blocks = blocks; + cd_raw = FALSE; + imgoffset = imgoff; +} + +//--------------------------------------------------------------------------- +// +// Destructor +// +//--------------------------------------------------------------------------- +DiskCache::~DiskCache() +{ + // Clear the track + Clear(); +} + +//--------------------------------------------------------------------------- +// +// RAW Mode Setting +// +//--------------------------------------------------------------------------- +void DiskCache::SetRawMode(BOOL raw) +{ + // Configuration + cd_raw = raw; +} + +//--------------------------------------------------------------------------- +// +// Save +// +//--------------------------------------------------------------------------- +bool DiskCache::Save() +{ + // Save track + for (int i = 0; i < CacheMax; i++) { + // Is it a valid track? + if (cache[i].disktrk) { + // Save + if (!cache[i].disktrk->Save(sec_path)) { + return false; + } + } + } + + return true; +} + +//--------------------------------------------------------------------------- +// +// Get disk cache information +// +//--------------------------------------------------------------------------- +bool DiskCache::GetCache(int index, int& track, DWORD& aserial) const +{ + ASSERT((index >= 0) && (index < CacheMax)); + + // false if unused + if (!cache[index].disktrk) { + return false; + } + + // Set track and serial + track = cache[index].disktrk->GetTrack(); + aserial = cache[index].serial; + + return true; +} + +//--------------------------------------------------------------------------- +// +// Clear +// +//--------------------------------------------------------------------------- +void DiskCache::Clear() +{ + // Free the cache + for (int i = 0; i < CacheMax; i++) { + if (cache[i].disktrk) { + delete cache[i].disktrk; + cache[i].disktrk = NULL; + } + } +} + +//--------------------------------------------------------------------------- +// +// Sector Read +// +//--------------------------------------------------------------------------- +bool DiskCache::Read(BYTE *buf, int block) +{ + ASSERT(sec_size != 0); + + // Update first + Update(); + + // Calculate track (fixed to 256 sectors/track) + int track = block >> 8; + + // Get the track data + DiskTrack *disktrk = Assign(track); + if (!disktrk) { + return false; + } + + // Read the track data to the cache + return disktrk->Read(buf, (BYTE)block); +} + +//--------------------------------------------------------------------------- +// +// Sector write +// +//--------------------------------------------------------------------------- +bool DiskCache::Write(const BYTE *buf, int block) +{ + ASSERT(sec_size != 0); + + // Update first + Update(); + + // Calculate track (fixed to 256 sectors/track) + int track = block >> 8; + + // Get that track data + DiskTrack *disktrk = Assign(track); + if (!disktrk) { + return false; + } + + // Write the data to the cache + return disktrk->Write(buf, (BYTE)block); +} + +//--------------------------------------------------------------------------- +// +// Track Assignment +// +//--------------------------------------------------------------------------- +DiskTrack* DiskCache::Assign(int track) +{ + ASSERT(sec_size != 0); + ASSERT(track >= 0); + + // First, check if it is already assigned + for (int i = 0; i < CacheMax; i++) { + if (cache[i].disktrk) { + if (cache[i].disktrk->GetTrack() == track) { + // Track match + cache[i].serial = serial; + return cache[i].disktrk; + } + } + } + + // Next, check for empty + for (int i = 0; i < CacheMax; i++) { + if (!cache[i].disktrk) { + // Try loading + if (Load(i, track)) { + // Success loading + cache[i].serial = serial; + return cache[i].disktrk; + } + + // Load failed + return NULL; + } + } + + // Finally, find the youngest serial number and delete it + + // Set index 0 as candidate c + DWORD s = cache[0].serial; + int c = 0; + + // Compare candidate with serial and update to smaller one + for (int i = 0; i < CacheMax; i++) { + ASSERT(cache[i].disktrk); + + // Compare and update the existing serial + if (cache[i].serial < s) { + s = cache[i].serial; + c = i; + } + } + + // Save this track + if (!cache[c].disktrk->Save(sec_path)) { + return NULL; + } + + // Delete this track + DiskTrack *disktrk = cache[c].disktrk; + cache[c].disktrk = NULL; + + // Load + if (Load(c, track, disktrk)) { + // Successful loading + cache[c].serial = serial; + return cache[c].disktrk; + } + + // Load failed + return NULL; +} + +//--------------------------------------------------------------------------- +// +// Load cache +// +//--------------------------------------------------------------------------- +bool DiskCache::Load(int index, int track, DiskTrack *disktrk) +{ + ASSERT((index >= 0) && (index < CacheMax)); + ASSERT(track >= 0); + ASSERT(!cache[index].disktrk); + + // Get the number of sectors on this track + int sectors = sec_blocks - (track << 8); + ASSERT(sectors > 0); + if (sectors > 0x100) { + sectors = 0x100; + } + + // Create a disk track + if (disktrk == NULL) { + disktrk = new DiskTrack(); + } + + // Initialize disk track + disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset); + + // Try loading + if (!disktrk->Load(sec_path)) { + // Failure + delete disktrk; + return false; + } + + // Allocation successful, work set + cache[index].disktrk = disktrk; + + return true; +} + +//--------------------------------------------------------------------------- +// +// Update serial number +// +//--------------------------------------------------------------------------- +void DiskCache::Update() +{ + // Update and do nothing except 0 + serial++; + if (serial != 0) { + return; + } + + // Clear serial of all caches (loop in 32bit) + for (int i = 0; i < CacheMax; i++) { + cache[i].serial = 0; + } +} + diff --git a/src/raspberrypi/devices/disk_track_cache.h b/src/raspberrypi/devices/disk_track_cache.h new file mode 100644 index 00000000..e79aaae5 --- /dev/null +++ b/src/raspberrypi/devices/disk_track_cache.h @@ -0,0 +1,95 @@ +//--------------------------------------------------------------------------- +// +// X68000 EMULATOR "XM6" +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// +// XM6i +// Copyright (C) 2010-2015 isaki@NetBSD.org +// +// Imported sava's Anex86/T98Next image and MO format support patch. +// Comments translated to english by akuker. +// +// [ DiskTrack and DiskCache ] +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "../rascsi.h" +#include "filepath.h" + +// Number of tracks to cache +#define CacheMax 16 + +class DiskTrack +{ +private: + struct { + int track; // Track Number + int size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096) + int sectors; // Number of sectors(<0x100) + DWORD length; // Data buffer length + BYTE *buffer; // Data buffer + BOOL init; // Is it initilized? + BOOL changed; // Changed flag + DWORD maplen; // Changed map length + BOOL *changemap; // Changed map + BOOL raw; // RAW mode flag + off_t imgoffset; // Offset to actual data + } dt; + +public: + DiskTrack(); + ~DiskTrack(); + + void Init(int track, int size, int sectors, BOOL raw = FALSE, off_t imgoff = 0); + bool Load(const Filepath& path); + bool Save(const Filepath& path); + + // Read / Write + bool Read(BYTE *buf, int sec) const; // Sector Read + bool Write(const BYTE *buf, int sec); // Sector Write + + int GetTrack() const { return dt.track; } // Get track +}; + +class DiskCache +{ +public: + // Internal data definition + typedef struct { + DiskTrack *disktrk; // Disk Track + DWORD serial; // Serial + } cache_t; + +public: + DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff = 0); + ~DiskCache(); + + void SetRawMode(BOOL raw); // CD-ROM raw mode setting + + // Access + bool Save(); // Save and release all + bool Read(BYTE *buf, int block); // Sector Read + bool Write(const BYTE *buf, int block); // Sector Write + bool GetCache(int index, int& track, DWORD& serial) const; // Get cache information + +private: + // Internal Management + void Clear(); // Clear all tracks + DiskTrack* Assign(int track); // Load track + bool Load(int index, int track, DiskTrack *disktrk = NULL); // Load track + void Update(); // Update serial number + + // Internal data + cache_t cache[CacheMax]; // Cache management + DWORD serial; // Last serial number + Filepath sec_path; // Path + int sec_size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096) + int sec_blocks; // Blocks per sector + BOOL cd_raw; // CD-ROM RAW mode + off_t imgoffset; // Offset to actual data +}; + diff --git a/src/raspberrypi/devices/file_support.cpp b/src/raspberrypi/devices/file_support.cpp new file mode 100644 index 00000000..09b9061a --- /dev/null +++ b/src/raspberrypi/devices/file_support.cpp @@ -0,0 +1,43 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include +#include +#include "file_support.h" + +using namespace std; + +map FileSupport::reserved_files; + +void FileSupport::ReserveFile(const Filepath& path, int id, int lun) +{ + reserved_files[path.GetPath()] = make_pair(id, lun); +} + +void FileSupport::UnreserveFile() +{ + reserved_files.erase(diskpath.GetPath()); +} + +bool FileSupport::GetIdsForReservedFile(const Filepath& path, int& id, int& unit) +{ + if (reserved_files.find(path.GetPath()) != reserved_files.end()) { + const id_set ids = reserved_files[path.GetPath()]; + id = ids.first; + unit = ids.second; + return true; + } + + return false; +} + +void FileSupport::UnreserveAll() +{ + reserved_files.clear(); +} diff --git a/src/raspberrypi/devices/file_support.h b/src/raspberrypi/devices/file_support.h new file mode 100644 index 00000000..0d7df448 --- /dev/null +++ b/src/raspberrypi/devices/file_support.h @@ -0,0 +1,46 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +// Devices inheriting from FileSupport support image files +// +//--------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include "filepath.h" + +using namespace std; + +typedef pair id_set; + +class FileSupport +{ +private: + Filepath diskpath; + + // The list of image files in use and the IDs and LUNs using these files + static map reserved_files; + +public: + + FileSupport() {}; + virtual ~FileSupport() {}; + + void GetPath(Filepath& path) const { path = diskpath; } + void SetPath(const Filepath& path) { diskpath = path; } + static const map GetReservedFiles(){ return reserved_files; } + static void SetReservedFiles(const map& files_in_use) { FileSupport::reserved_files = files_in_use; } + void ReserveFile(const Filepath&, int, int); + void UnreserveFile(); + + static bool GetIdsForReservedFile(const Filepath&, int&, int&); + static void UnreserveAll(); + + virtual void Open(const Filepath&) = 0; +}; diff --git a/src/raspberrypi/devices/interfaces/scsi_block_commands.h b/src/raspberrypi/devices/interfaces/scsi_block_commands.h new file mode 100644 index 00000000..d1dfc60a --- /dev/null +++ b/src/raspberrypi/devices/interfaces/scsi_block_commands.h @@ -0,0 +1,48 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +// Interface for SCSI block commands (see https://www.t10.org/drafts.htm, SBC-5) +// +//--------------------------------------------------------------------------- + +#pragma once + +class SASIDEV; + +class ScsiBlockCommands +{ +public: + + ScsiBlockCommands() {}; + virtual ~ScsiBlockCommands() {}; + + // Mandatory commands + virtual void TestUnitReady(SASIDEV *) = 0; + virtual void Inquiry(SASIDEV *) = 0; + virtual void ReportLuns(SASIDEV *) = 0; + virtual void FormatUnit(SASIDEV *) = 0; + virtual void ReadCapacity10(SASIDEV *) = 0; + virtual void ReadCapacity16(SASIDEV *) = 0; + virtual void Read10(SASIDEV *) = 0; + virtual void Read16(SASIDEV *) = 0; + virtual void Write10(SASIDEV *) = 0; + virtual void Write16(SASIDEV *) = 0; + virtual void RequestSense(SASIDEV *) = 0; + + // Implemented optional commands + virtual void Verify10(SASIDEV *) = 0; + virtual void Verify16(SASIDEV *) = 0; + virtual void ModeSense6(SASIDEV *) = 0; + virtual void ModeSense10(SASIDEV *) = 0; + virtual void ModeSelect6(SASIDEV *) = 0; + virtual void ModeSelect10(SASIDEV *) = 0; + virtual void ReassignBlocks(SASIDEV *) = 0; + virtual void SendDiagnostic(SASIDEV *) = 0; + virtual void StartStopUnit(SASIDEV *) = 0; + virtual void SynchronizeCache10(SASIDEV *) = 0; + virtual void SynchronizeCache16(SASIDEV *) = 0; +}; diff --git a/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h b/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h new file mode 100644 index 00000000..c42a76ef --- /dev/null +++ b/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h @@ -0,0 +1,25 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +// Interface for SCSI Multi-Media commands (see https://www.t10.org/drafts.htm, MMC-6) +// +//--------------------------------------------------------------------------- + +#pragma once + +class SASIDEV; + +class ScsiMmcCommands +{ +public: + + ScsiMmcCommands() {}; + virtual ~ScsiMmcCommands() {}; + + virtual void ReadToc(SASIDEV *) = 0; + virtual void GetEventStatusNotification(SASIDEV *) = 0; +}; diff --git a/src/raspberrypi/devices/interfaces/scsi_primary_commands.h b/src/raspberrypi/devices/interfaces/scsi_primary_commands.h new file mode 100644 index 00000000..c7e0d344 --- /dev/null +++ b/src/raspberrypi/devices/interfaces/scsi_primary_commands.h @@ -0,0 +1,34 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +// Interface for SCSI primary commands (see https://www.t10.org/drafts.htm, SPC-6) +// +//--------------------------------------------------------------------------- + +#pragma once + +class SASIDEV; + +class ScsiPrimaryCommands +{ +public: + + ScsiPrimaryCommands() {}; + virtual ~ScsiPrimaryCommands() {}; + + // Mandatory commands + virtual void TestUnitReady(SASIDEV *) = 0; + virtual void Inquiry(SASIDEV *) = 0; + virtual void ReportLuns(SASIDEV *) = 0; + + // Implemented optional commands + virtual void RequestSense(SASIDEV *) = 0; + virtual void ModeSense6(SASIDEV *) = 0; + virtual void ModeSense10(SASIDEV *) = 0; + virtual void ModeSelect6(SASIDEV *) = 0; + virtual void ModeSelect10(SASIDEV *) = 0; +}; diff --git a/src/raspberrypi/devices/sasihd.cpp b/src/raspberrypi/devices/sasihd.cpp index e571747c..ec901478 100644 --- a/src/raspberrypi/devices/sasihd.cpp +++ b/src/raspberrypi/devices/sasihd.cpp @@ -14,9 +14,10 @@ // //--------------------------------------------------------------------------- #include "sasihd.h" -#include "xm6.h" #include "fileio.h" - +#include "exceptions.h" +#include +#include "../rascsi.h" //=========================================================================== // @@ -29,105 +30,86 @@ // Constructor // //--------------------------------------------------------------------------- -SASIHD::SASIHD() : Disk() +SASIHD::SASIHD() : Disk("SAHD") { - // SASI ハードディスク - disk.id = MAKEID('S', 'A', 'H', 'D'); } //--------------------------------------------------------------------------- // -// リセット +// Reset // //--------------------------------------------------------------------------- -void FASTCALL SASIHD::Reset() +void SASIHD::Reset() { - // ロック状態解除、アテンション解除 - disk.lock = FALSE; - disk.attn = FALSE; + // Unlock, clear attention + SetLocked(false); + SetAttn(false); - // Resetなし、コードをクリア - disk.reset = FALSE; - disk.code = 0x00; + // Reset, clear the code + SetReset(false); + SetStatusCode(STATUS_NOERROR); } //--------------------------------------------------------------------------- // -// オープン +// Open // //--------------------------------------------------------------------------- -BOOL FASTCALL SASIHD::Open(const Filepath& path, BOOL /*attn*/) +void SASIHD::Open(const Filepath& path) { - Fileio fio; - off64_t size; - - ASSERT(this); - ASSERT(!disk.ready); + ASSERT(!IsReady()); // Open as read-only + Fileio fio; if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; + throw file_not_found_exception("Can't open SASI hard disk file"); } // Get file size - size = fio.GetFileSize(); + off_t size = fio.GetFileSize(); fio.Close(); - #if defined(USE_MZ1F23_1024_SUPPORT) - // MZ-2500/MZ-2800用 MZ-1F23(SASI 20M/セクタサイズ1024)専用 - // 20M(22437888 BS=1024 C=21912) - if (size == 0x1566000) { - // セクタサイズとブロック数 - disk.size = 10; - disk.blocks = (DWORD)(size >> 10); - - // Call the base class - return Disk::Open(path); - } - #endif // USE_MZ1F23_1024_SUPPORT + // Sector size (default 256 bytes) and number of blocks + SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 256, true); + SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount())); #if defined(REMOVE_FIXED_SASIHD_SIZE) - // 256バイト単位であること - if (size & 0xff) { - return FALSE; - } - - // 10MB以上 - if (size < 0x9f5400) { - return FALSE; - } - - // 512MB程度に制限しておく - if (size > 512 * 1024 * 1024) { - return FALSE; - } + // Effective size must be a multiple of the sector size + size = (size / GetSectorSizeInBytes()) * GetSectorSizeInBytes(); #else // 10MB, 20MB, 40MBのみ switch (size) { - // 10MB(10441728 BS=256 C=40788) + // 10MB (10441728 BS=256 C=40788) case 0x9f5400: break; - // 20MB(20748288 BS=256 C=81048) + // 20MB (20748288 BS=256 C=81048) case 0x13c9800: break; - // 40MB(41496576 BS=256 C=162096) + // 40MB (41496576 BS=256 C=162096) case 0x2793000: break; - // Other(サポートしない) + // Other (Not supported ) default: - return FALSE; + throw io_exception("Unsupported file size"); } #endif // REMOVE_FIXED_SASIHD_SIZE - // セクタサイズとブロック数 - disk.size = 8; - disk.blocks = (DWORD)(size >> 8); + Disk::Open(path); + FileSupport::SetPath(path); +} - // Call the base class - return Disk::Open(path); +//--------------------------------------------------------------------------- +// +// INQUIRY +// +//--------------------------------------------------------------------------- +int SASIHD::Inquiry(const DWORD* /*cdb*/, BYTE* /*buf*/) +{ + SetStatusCode(STATUS_INVALIDCMD); + return 0; } //--------------------------------------------------------------------------- @@ -135,30 +117,24 @@ BOOL FASTCALL SASIHD::Open(const Filepath& path, BOOL /*attn*/) // REQUEST SENSE // //--------------------------------------------------------------------------- -int FASTCALL SASIHD::RequestSense(const DWORD *cdb, BYTE *buf) +int SASIHD::RequestSense(const DWORD *cdb, BYTE *buf) { - int size; - - ASSERT(this); ASSERT(cdb); ASSERT(buf); - // サイズ決定 - size = (int)cdb[4]; - ASSERT((size >= 0) && (size < 0x100)); + // Size decision + int size = (int)cdb[4]; + ASSERT(size >= 0 && size < 0x100); - // サイズ0のときに4バイト転送する(Shugart Associates System Interface仕様) + // Transfer 4 bytes when size 0 (Shugart Associates System Interface specification) if (size == 0) { size = 4; } - // SASIは非拡張フォーマットに固定 + // SASI fixed to non-extended format memset(buf, 0, size); - buf[0] = (BYTE)(disk.code >> 16); - buf[1] = (BYTE)(disk.lun << 5); - - // コードをクリア - disk.code = 0x00; + buf[0] = (BYTE)(GetStatusCode() >> 16); + buf[1] = (BYTE)(GetLun() << 5); return size; } diff --git a/src/raspberrypi/devices/sasihd.h b/src/raspberrypi/devices/sasihd.h index a031c7cc..08f163fb 100644 --- a/src/raspberrypi/devices/sasihd.h +++ b/src/raspberrypi/devices/sasihd.h @@ -24,14 +24,16 @@ // SASI Hard Disk // //=========================================================================== -class SASIHD : public Disk +class SASIHD : public Disk, public FileSupport { public: - // Basic Functions - SASIHD(); // Constructor - void FASTCALL Reset(); // Reset - BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); // Open + SASIHD(); + ~SASIHD() {}; - // commands - int FASTCALL RequestSense(const DWORD *cdb, BYTE *buf); // REQUEST SENSE command -}; \ No newline at end of file + void Reset(); + void Open(const Filepath& path); + + // Commands + int RequestSense(const DWORD *cdb, BYTE *buf) override; + int Inquiry(const DWORD *cdb, BYTE *buf) override; +}; diff --git a/src/raspberrypi/devices/scsi_daynaport.cpp b/src/raspberrypi/devices/scsi_daynaport.cpp new file mode 100644 index 00000000..04aca7f1 --- /dev/null +++ b/src/raspberrypi/devices/scsi_daynaport.cpp @@ -0,0 +1,660 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2020 akuker +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ Emulation of the DaynaPort SCSI Link Ethernet interface ] +// +// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's +// Tiny SCSI Emulator +// - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT +// - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator +// +// Additional documentation and clarification is available at the +// following link: +// - https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link +// +// This does NOT include the file system functionality that is present +// in the Sharp X68000 host bridge. +// +// Note: This requires a DaynaPort SCSI Link driver. +//--------------------------------------------------------------------------- + +#include "scsi_daynaport.h" +#include + +const BYTE SCSIDaynaPort::m_bcast_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +const BYTE SCSIDaynaPort::m_apple_talk_addr[6] = { 0x09, 0x00, 0x07, 0xff, 0xff, 0xff }; + +SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP") +{ + m_tap = NULL; + m_bTapEnable = false; + + AddCommand(SCSIDEV::eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady); + AddCommand(SCSIDEV::eCmdRead6, "Read6", &SCSIDaynaPort::Read6); + AddCommand(SCSIDEV::eCmdWrite6, "Write6", &SCSIDaynaPort::Write6); + AddCommand(SCSIDEV::eCmdRetrieveStats, "RetrieveStats", &SCSIDaynaPort::RetrieveStatistics); + AddCommand(SCSIDEV::eCmdSetIfaceMode, "SetIfaceMode", &SCSIDaynaPort::SetInterfaceMode); + AddCommand(SCSIDEV::eCmdSetMcastAddr, "SetMcastAddr", &SCSIDaynaPort::SetMcastAddr); + AddCommand(SCSIDEV::eCmdEnableInterface, "EnableInterface", &SCSIDaynaPort::EnableInterface); +} + +SCSIDaynaPort::~SCSIDaynaPort() +{ + // TAP driver release + if (m_tap) { + m_tap->Cleanup(); + delete m_tap; + } + + for (auto const& command : commands) { + delete command.second; + } +} + +void SCSIDaynaPort::AddCommand(SCSIDEV::scsi_command opcode, const char* name, void (SCSIDaynaPort::*execute)(SASIDEV *)) +{ + commands[opcode] = new command_t(name, execute); +} + +bool SCSIDaynaPort::Dispatch(SCSIDEV *controller) +{ + ctrl = controller->GetCtrl(); + + if (commands.count(static_cast(ctrl->cmd[0]))) { + command_t *command = commands[static_cast(ctrl->cmd[0])]; + + LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, command->name, (unsigned int)ctrl->cmd[0]); + + (this->*command->execute)(controller); + + return true; + } + + LOGTRACE("%s Calling base class for dispatching $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->cmd[0]); + + // The base class handles the less specific commands + return Disk::Dispatch(controller); +} + +bool SCSIDaynaPort::Init(const map& params) +{ + SetParams(params.empty() ? GetDefaultParams() : params); + +#ifdef __linux__ + // TAP Driver Generation + m_tap = new CTapDriver(GetParam("interfaces")); + m_bTapEnable = m_tap->Init(); + if(!m_bTapEnable){ + LOGERROR("Unable to open the TAP interface"); + +// Not terminating on regular Linux PCs is helpful for testing +#if !defined(__x86_64__) && !defined(__X86__) + return false; +#endif + } else { + LOGDEBUG("Tap interface created"); + } + + this->Reset(); + SetReady(true); + SetReset(false); + + // Generate MAC Address + LOGTRACE("%s memset(m_mac_addr, 0x00, 6);", __PRETTY_FUNCTION__); + memset(m_mac_addr, 0x00, 6); + + // if (m_bTapEnable) { + // tap->GetMacAddr(m_mac_addr); + // m_mac_addr[5]++; + // } + // !!!!!!!!!!!!!!!!! For now, hard code the MAC address. Its annoying when it keeps changing during development! + // TODO: Remove this hard-coded address + LOGTRACE("%s m_mac_addr[0]=0x00;", __PRETTY_FUNCTION__); + m_mac_addr[0]=0x00; + m_mac_addr[1]=0x80; + m_mac_addr[2]=0x19; + m_mac_addr[3]=0x10; + m_mac_addr[4]=0x98; + m_mac_addr[5]=0xE3; +#endif // linux + + return true; +} + +void SCSIDaynaPort::Open(const Filepath& path) +{ + m_tap->OpenDump(path); +} + +//--------------------------------------------------------------------------- +// +// INQUIRY +// +//--------------------------------------------------------------------------- +int SCSIDaynaPort::Inquiry(const DWORD *cdb, BYTE *buf) +{ + int allocation_length = cdb[4] + (((DWORD)cdb[3]) << 8); + + LOGTRACE("%s Inquiry, allocation length: %d",__PRETTY_FUNCTION__, allocation_length); + + if (allocation_length > 4) { + if (allocation_length > 44) { + allocation_length = 44; + } + + // Basic data + // buf[0] ... Processor Device + // buf[1] ... Not removable + // buf[2] ... SCSI-2 compliant command system + // buf[3] ... SCSI-2 compliant Inquiry response + // buf[4] ... Inquiry additional data + // http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/pocket_scsiLINK/pocketscsilink_inq.png + memset(buf, 0, allocation_length); + buf[0] = 0x03; + buf[2] = 0x01; + buf[4] = 0x1F; + + // Padded vendor, product, revision + memcpy(&buf[8], GetPaddedName().c_str(), 28); + } + + LOGTRACE("response size is %d", allocation_length); + + return allocation_length; +} + +//--------------------------------------------------------------------------- +// +// READ +// +// Command: 08 00 00 LL LL XX (LLLL is data length, XX = c0 or 80) +// Function: Read a packet at a time from the device (standard SCSI Read) +// Type: Input; the following data is returned: +// LL LL NN NN NN NN XX XX XX ... CC CC CC CC +// where: +// LLLL is normally the length of the packet (a 2-byte +// big-endian hex value), including 4 trailing bytes +// of CRC, but excluding itself and the flag field. +// See below for special values +// NNNNNNNN is a 4-byte flag field with the following meanings: +// FFFFFFFF a packet has been dropped (?); in this case +// the length field appears to be always 4000 +// 00000010 there are more packets currently available +// in SCSI/Link memory +// 00000000 this is the last packet +// XX XX ... is the actual packet +// CCCCCCCC is the CRC +// +// Notes: +// - When all packets have been retrieved successfully, a length field +// of 0000 is returned; however, if a packet has been dropped, the +// SCSI/Link will instead return a non-zero length field with a flag +// of FFFFFFFF when there are no more packets available. This behaviour +// seems to continue until a disable/enable sequence has been issued. +// - The SCSI/Link apparently has about 6KB buffer space for packets. +// +//--------------------------------------------------------------------------- +int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t block) +{ + int rx_packet_size = 0; + scsi_resp_read_t *response = (scsi_resp_read_t*)buf; + scsi_cmd_read_6_t *command = (scsi_cmd_read_6_t*)cdb; + + ostringstream s; + s << __PRETTY_FUNCTION__ << " reading DaynaPort block " << block; + LOGTRACE("%s", s.str().c_str()); + + if (command->operation_code != 0x08) { + LOGERROR("Received unexpected cdb command: %02X. Expected 0x08", command->operation_code); + } + + int requested_length = command->transfer_length; + LOGTRACE("%s Read maximum length %d, (%04X)", __PRETTY_FUNCTION__, requested_length, requested_length); + + + // At host startup, it will send a READ(6) command with a length of 1. We should + // respond by going into the status mode with a code of 0x02 + if (requested_length == 1) { + return 0; + } + + // Some of the packets we receive will not be for us. So, we'll keep pulling messages + // until the buffer is empty, or we've read X times. (X is just a made up number) + bool send_message_to_host; + int read_count = 0; + while (read_count < MAX_READ_RETRIES) { + read_count++; + + // The first 2 bytes are reserved for the length of the packet + // The next 4 bytes are reserved for a flag field + //rx_packet_size = m_tap->Rx(response->data); + rx_packet_size = m_tap->Rx(&buf[DAYNAPORT_READ_HEADER_SZ]); + + // If we didn't receive anything, return size of 0 + if (rx_packet_size <= 0) { + LOGTRACE("%s No packet received", __PRETTY_FUNCTION__); + response->length = 0; + response->flags = e_no_more_data; + return DAYNAPORT_READ_HEADER_SZ; + } + + LOGTRACE("%s Packet Sz %d (%08X) read: %d", __PRETTY_FUNCTION__, (unsigned int) rx_packet_size, (unsigned int) rx_packet_size, read_count); + + // This is a very basic filter to prevent unnecessary packets from + // being sent to the SCSI initiator. + send_message_to_host = false; + + // The following doesn't seem to work with unicast messages. Temporarily removing the filtering + // functionality. + /////// // Check if received packet destination MAC address matches the + /////// // DaynaPort MAC. For IP packets, the mac_address will be the first 6 bytes + /////// // of the data. + /////// if (memcmp(response->data, m_mac_addr, 6) == 0) { + /////// send_message_to_host = true; + /////// } + + /////// // Check to see if this is a broadcast message + /////// if (memcmp(response->data, m_bcast_addr, 6) == 0) { + /////// send_message_to_host = true; + /////// } + + /////// // Check to see if this is an AppleTalk Message + /////// if (memcmp(response->data, m_apple_talk_addr, 6) == 0) { + /////// send_message_to_host = true; + /////// } + send_message_to_host = true; + + // TODO: We should check to see if this message is in the multicast + // configuration from SCSI command 0x0D + + if (!send_message_to_host) { + LOGDEBUG("%s Received a packet that's not for me: %02X %02X %02X %02X %02X %02X", \ + __PRETTY_FUNCTION__, + (int)response->data[0], + (int)response->data[1], + (int)response->data[2], + (int)response->data[3], + (int)response->data[4], + (int)response->data[5]); + + // If there are pending packets to be processed, we'll tell the host that the read + // length was 0. + if (!m_tap->PendingPackets()) + { + response->length = 0; + response->flags = e_no_more_data; + return DAYNAPORT_READ_HEADER_SZ; + } + } + else { + // TODO: Need to do some sort of size checking. The buffer can easily overflow, probably. + + // response->length = rx_packet_size; + // if(m_tap->PendingPackets()){ + // response->flags = e_more_data_available; + // } else { + // response->flags = e_no_more_data; + // } + buf[0] = (BYTE)((rx_packet_size >> 8) & 0xFF); + buf[1] = (BYTE)(rx_packet_size & 0xFF); + + buf[2] = 0; + buf[3] = 0; + buf[4] = 0; + if(m_tap->PendingPackets()){ + buf[5] = 0x10; + } else { + buf[5] = 0; + } + + // Return the packet size + 2 for the length + 4 for the flag field + // The CRC was already appended by the ctapdriver + return rx_packet_size + DAYNAPORT_READ_HEADER_SZ; + } + // If we got to this point, there are still messages in the queue, so + // we should loop back and get the next one. + } // end while + + response->length = 0; + response->flags = e_no_more_data; + return DAYNAPORT_READ_HEADER_SZ; +} + +//--------------------------------------------------------------------------- +// +// WRITE check +// +//--------------------------------------------------------------------------- +int SCSIDaynaPort::WriteCheck(DWORD block) +{ + // Status check + if (!CheckReady()) { + return 0; + } + + if (!m_bTapEnable){ + SetStatusCode(STATUS_NOTREADY); + return 0; + } + + // Success + return 1; +} + +//--------------------------------------------------------------------------- +// +// Write +// +// Command: 0a 00 00 LL LL XX (LLLL is data length, XX = 80 or 00) +// Function: Write a packet at a time to the device (standard SCSI Write) +// Type: Output; the format of the data to be sent depends on the value +// of XX, as follows: +// - if XX = 00, LLLL is the packet length, and the data to be sent +// must be an image of the data packet +// - if XX = 80, LLLL is the packet length + 8, and the data to be +// sent is: +// PP PP 00 00 XX XX XX ... 00 00 00 00 +// where: +// PPPP is the actual (2-byte big-endian) packet length +// XX XX ... is the actual packet +// +//--------------------------------------------------------------------------- +bool SCSIDaynaPort::Write(const DWORD *cdb, const BYTE *buf, DWORD block) +{ + BYTE data_format = cdb[5]; + WORD data_length = (WORD)cdb[4] + ((WORD)cdb[3] << 8); + + if (data_format == 0x00){ + m_tap->Tx(buf, data_length); + LOGTRACE("%s Transmitted %u bytes (00 format)", __PRETTY_FUNCTION__, data_length); + return true; + } + else if (data_format == 0x80){ + // The data length is specified in the first 2 bytes of the payload + data_length=(WORD)buf[1] + ((WORD)buf[0] << 8); + m_tap->Tx(&buf[4], data_length); + LOGTRACE("%s Transmitted %u bytes (80 format)", __PRETTY_FUNCTION__, data_length); + return true; + } + else + { + // LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)command->format); + LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)data_format); + return true; + } +} + +//--------------------------------------------------------------------------- +// +// RetrieveStats +// +// Command: 09 00 00 00 12 00 +// Function: Retrieve MAC address and device statistics +// Type: Input; returns 18 (decimal) bytes of data as follows: +// - bytes 0-5: the current hardware ethernet (MAC) address +// - bytes 6-17: three long word (4-byte) counters (little-endian). +// Notes: The contents of the three longs are typically zero, and their +// usage is unclear; they are suspected to be: +// - long #1: frame alignment errors +// - long #2: CRC errors +// - long #3: frames lost +// +//--------------------------------------------------------------------------- +int SCSIDaynaPort::RetrieveStats(const DWORD *cdb, BYTE *buffer) +{ + int allocation_length = cdb[4] + (((DWORD)cdb[3]) << 8); + LOGTRACE("%s Retrieve Stats buffer size is %d", __PRETTY_FUNCTION__, (int)allocation_length); + + // if(cdb[4] != 0x12) + // { + // LOGWARN("%s cdb[4] was not 0x12, as I expected. It was %02X.", __PRETTY_FUNCTION__, (unsigned int)cdb[4]); + // } + // memset(buffer,0,18); + // memcpy(&buffer[0],m_mac_addr,sizeof(m_mac_addr)); + // uint32_t frame_alignment_errors; + // uint32_t crc_errors; + // uint32_t frames_lost; + // // frame alignment errors + // frame_alignment_errors = htonl(0); + // memcpy(&(buffer[6]),&frame_alignment_errors,sizeof(frame_alignment_errors)); + // // CRC errors + // crc_errors = htonl(0); + // memcpy(&(buffer[10]),&crc_errors,sizeof(crc_errors)); + // // frames lost + // frames_lost = htonl(0); + // memcpy(&(buffer[14]),&frames_lost,sizeof(frames_lost)); + + for (int i = 0; i < 6; i++) { + LOGTRACE("%s CDB byte %d: %02X",__PRETTY_FUNCTION__, i, (unsigned int)cdb[i]); + } + + int response_size = sizeof(m_scsi_link_stats); + memcpy(buffer, &m_scsi_link_stats, sizeof(m_scsi_link_stats)); + + LOGTRACE("%s response size is %d", __PRETTY_FUNCTION__, (int)response_size); + + if (response_size > allocation_length) { + response_size = allocation_length; + LOGINFO("%s Truncating the inquiry response", __PRETTY_FUNCTION__) + } + + // Success + return response_size; +} + +//--------------------------------------------------------------------------- +// +// Enable or Disable the interface +// +// Command: 0e 00 00 00 00 XX (XX = 80 or 00) +// Function: Enable (80) / disable (00) Ethernet interface +// Type: No data transferred +// Notes: After issuing an Enable, the initiator should avoid sending +// any subsequent commands to the device for approximately 0.5 +// seconds +// +//--------------------------------------------------------------------------- +bool SCSIDaynaPort::EnableInterface(const DWORD *cdb) +{ + bool result; + if (cdb[5] & 0x80) { + result = m_tap->Enable(); + if (result) { + LOGINFO("The DaynaPort interface has been ENABLED."); + } + else{ + LOGWARN("Unable to enable the DaynaPort Interface"); + } + m_tap->Flush(); + } + else { + result = m_tap->Disable(); + if (result) { + LOGINFO("The DaynaPort interface has been DISABLED."); + } + else{ + LOGWARN("Unable to disable the DaynaPort Interface"); + } + } + + return result; +} + +void SCSIDaynaPort::TestUnitReady(SASIDEV *controller) +{ + // TEST UNIT READY Success + + controller->Status(); +} + +void SCSIDaynaPort::Read6(SASIDEV *controller) +{ + // Get record number and block number + uint32_t record = ctrl->cmd[1] & 0x1f; + record <<= 8; + record |= ctrl->cmd[2]; + record <<= 8; + record |= ctrl->cmd[3]; + ctrl->blocks=1; + + LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl->blocks); + + ctrl->length = Read(ctrl->cmd, ctrl->buffer, record); + LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, (int)ctrl->length); + + // Set next block + ctrl->next = record + 1; + + controller->DataIn(); +} + +void SCSIDaynaPort::Write6(SASIDEV *controller) +{ + // Reallocate buffer (because it is not transfer for each block) + if (ctrl->bufsize < DAYNAPORT_BUFFER_SIZE) { + free(ctrl->buffer); + ctrl->bufsize = DAYNAPORT_BUFFER_SIZE; + ctrl->buffer = (BYTE *)malloc(ctrl->bufsize); + } + + DWORD data_format = ctrl->cmd[5]; + + if(data_format == 0x00) { + ctrl->length = (WORD)ctrl->cmd[4] + ((WORD)ctrl->cmd[3] << 8); + } + else if (data_format == 0x80) { + ctrl->length = (WORD)ctrl->cmd[4] + ((WORD)ctrl->cmd[3] << 8) + 8; + } + else { + LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)data_format); + } + LOGTRACE("%s length: %04X (%d) format: %02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->length, (int)ctrl->length, (unsigned int)data_format); + + if (ctrl->length <= 0) { + // Failure (Error) + controller->Error(); + return; + } + + // Set next block + ctrl->blocks = 1; + ctrl->next = 1; + + controller->DataOut(); +} + +void SCSIDaynaPort::RetrieveStatistics(SASIDEV *controller) +{ + ctrl->length = RetrieveStats(ctrl->cmd, ctrl->buffer); + if (ctrl->length <= 0) { + // Failure (Error) + controller->Error(); + return; + } + + // Set next block + ctrl->blocks = 1; + ctrl->next = 1; + + controller->DataIn(); +} + +//--------------------------------------------------------------------------- +// +// Set interface mode/Set MAC address +// +// Set Interface Mode (0c) +// ----------------------- +// Command: 0c 00 00 00 FF 80 (FF = 08 or 04) +// Function: Allow interface to receive broadcast messages (FF = 04); the +// function of (FF = 08) is currently unknown. +// Type: No data transferred +// Notes: This command is accepted by firmware 1.4a & 2.0f, but has no +// effect on 2.0f, which is always capable of receiving broadcast +// messages. In 1.4a, once broadcast mode is set, it remains set +// until the interface is disabled. +// +// Set MAC Address (0c) +// -------------------- +// Command: 0c 00 00 00 FF 40 (FF = 08 or 04) +// Function: Set MAC address +// Type: Output; overrides built-in MAC address with user-specified +// 6-byte value +// Notes: This command is intended primarily for debugging/test purposes. +// Disabling the interface resets the MAC address to the built-in +// value. +// +//--------------------------------------------------------------------------- +void SCSIDaynaPort::SetInterfaceMode(SASIDEV *controller) +{ + // Check whether this command is telling us to "Set Interface Mode" or "Set MAC Address" + + ctrl->length = RetrieveStats(ctrl->cmd, ctrl->buffer); + switch(ctrl->cmd[5]){ + case SCSIDaynaPort::CMD_SCSILINK_SETMODE: + SetMode(ctrl->cmd, ctrl->buffer); + controller->Status(); + break; + break; + + case SCSIDaynaPort::CMD_SCSILINK_SETMAC: + ctrl->length = 6; + controller->DataOut(); + break; + + default: + LOGWARN("%s Unknown SetInterface command received: %02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->cmd[5]); + break; + } +} + +void SCSIDaynaPort::SetMcastAddr(SASIDEV *controller) +{ + ctrl->length = (DWORD)ctrl->cmd[4]; + if (ctrl->length == 0) { + LOGWARN("%s Not supported SetMcastAddr Command %02X", __PRETTY_FUNCTION__, (WORD)ctrl->cmd[2]); + + // Failure (Error) + controller->Error(); + return; + } + + controller->DataOut(); +} + +void SCSIDaynaPort::EnableInterface(SASIDEV *controller) +{ + bool status = EnableInterface(ctrl->cmd); + if (!status) { + // Failure (Error) + controller->Error(); + return; + } + + controller->Status(); +} + +//--------------------------------------------------------------------------- +// +// Set Mode - enable broadcast messages +// +//--------------------------------------------------------------------------- +void SCSIDaynaPort::SetMode(const DWORD *cdb, BYTE *buffer) +{ + LOGTRACE("%s Setting mode", __PRETTY_FUNCTION__); + + for (int i = 0; i < 6; i++) { + LOGTRACE("%s %d: %02X",__PRETTY_FUNCTION__, (unsigned int)i, (int)cdb[i]); + } +} + + diff --git a/src/raspberrypi/devices/scsi_daynaport.h b/src/raspberrypi/devices/scsi_daynaport.h new file mode 100644 index 00000000..2c7edaf8 --- /dev/null +++ b/src/raspberrypi/devices/scsi_daynaport.h @@ -0,0 +1,161 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2020 akuker +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ Emulation of the DaynaPort SCSI Link Ethernet interface ] +// +// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's +// Tiny SCSI Emulator +// - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT +// - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator +// +// Special thanks to @PotatoFi for loaning me his Farallon EtherMac for +// this development. (Farallon's EtherMac is a re-branded DaynaPort +// SCSI/Link-T). +// +// This does NOT include the file system functionality that is present +// in the Sharp X68000 host bridge. +// +// Note: This requires the DaynaPort SCSI Link driver. +//--------------------------------------------------------------------------- +#pragma once + +#include "os.h" +#include "disk.h" +#include "ctapdriver.h" +#include +#include +#include "../rascsi.h" + +//=========================================================================== +// +// DaynaPort SCSI Link +// +//=========================================================================== +class SCSIDaynaPort: public Disk +{ + +private: + typedef struct _command_t { + const char* name; + void (SCSIDaynaPort::*execute)(SASIDEV *); + + _command_t(const char* _name, void (SCSIDaynaPort::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { }; + } command_t; + std::map commands; + + SASIDEV::ctrl_t *ctrl; + + void AddCommand(SCSIDEV::scsi_command, const char*, void (SCSIDaynaPort::*)(SASIDEV *)); + +public: + SCSIDaynaPort(); + ~SCSIDaynaPort(); + + bool Init(const map&) override; + void Open(const Filepath& path) override; + + // Commands + int Inquiry(const DWORD *cdb, BYTE *buffer) override; + int Read(const DWORD *cdb, BYTE *buf, uint64_t block) override; + bool Write(const DWORD *cdb, const BYTE *buf, DWORD block) override; + int WriteCheck(DWORD block) override; // WRITE check + + int RetrieveStats(const DWORD *cdb, BYTE *buffer); + bool EnableInterface(const DWORD *cdb); + + void SetMacAddr(const DWORD *cdb, BYTE *buffer); // Set MAC address + void SetMode(const DWORD *cdb, BYTE *buffer); // Set the mode: whether broadcast traffic is enabled or not + + void TestUnitReady(SASIDEV *) override; + void Read6(SASIDEV *) override; + void Write6(SASIDEV *) override; + void RetrieveStatistics(SASIDEV *); + void SetInterfaceMode(SASIDEV *); + void SetMcastAddr(SASIDEV *); + void EnableInterface(SASIDEV *); + + bool Dispatch(SCSIDEV *); + + const int DAYNAPORT_BUFFER_SIZE = 0x1000000; + + static const BYTE CMD_SCSILINK_STATS = 0x09; + static const BYTE CMD_SCSILINK_ENABLE = 0x0E; + static const BYTE CMD_SCSILINK_SET = 0x0C; + static const BYTE CMD_SCSILINK_SETMODE = 0x80; + static const BYTE CMD_SCSILINK_SETMAC = 0x40; + + // When we're reading the Linux tap device, most of the messages will not be for us, so we + // need to filter through those. However, we don't want to keep re-reading the packets + // indefinitely. So, we'll pick a large-ish number that will cause the emulated DaynaPort + // to respond with "no data" after MAX_READ_RETRIES tries. + static const int MAX_READ_RETRIES = 50; + + // The READ response has a header which consists of: + // 2 bytes - payload size + // 4 bytes - status flags + static const DWORD DAYNAPORT_READ_HEADER_SZ = 2 + 4; + +private: + typedef struct __attribute__((packed)) { + BYTE operation_code; + BYTE misc_cdb_information; + BYTE logical_block_address; + uint16_t length; + BYTE format; + } scsi_cmd_daynaport_write_t; + + enum read_data_flags_t : uint32_t { + e_no_more_data = 0x00000000, + e_more_data_available = 0x00000001, + e_dropped_packets = 0xFFFFFFFF, + }; + + typedef struct __attribute__((packed)) { + uint32_t length; + read_data_flags_t flags; + BYTE pad; + BYTE data[ETH_FRAME_LEN + sizeof(uint32_t)]; // Frame length + 4 byte CRC + } scsi_resp_read_t; + + typedef struct __attribute__((packed)) { + BYTE mac_address[6]; + uint32_t frame_alignment_errors; + uint32_t crc_errors; + uint32_t frames_lost; + } scsi_resp_link_stats_t; + + scsi_resp_link_stats_t m_scsi_link_stats = { + .mac_address = { 0x00, 0x80, 0x19, 0x10, 0x98, 0xE3 },//MAC address of @PotatoFi's DayanPort + .frame_alignment_errors = 0, + .crc_errors = 0, + .frames_lost = 0, + }; + + const BYTE m_daynacom_mac_prefix[3] = { 0x00, 0x80, 0x19 }; + + CTapDriver *m_tap; + // TAP driver + bool m_bTapEnable; + // TAP valid flag + BYTE m_mac_addr[6]; + // MAC Address + static const BYTE m_bcast_addr[6]; + static const BYTE m_apple_talk_addr[6]; +}; + +typedef struct __attribute__((packed)) { + BYTE operation_code; + BYTE lba_msb_bits_4_0; + uint16_t logical_block_address; + BYTE transfer_length; + BYTE control; +} scsi_cmd_read_6_t; diff --git a/src/raspberrypi/devices/scsi_host_bridge.cpp b/src/raspberrypi/devices/scsi_host_bridge.cpp index 2cdc72fd..1bb8b62f 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.cpp +++ b/src/raspberrypi/devices/scsi_host_bridge.cpp @@ -17,29 +17,60 @@ //--------------------------------------------------------------------------- #include "scsi_host_bridge.h" -#include "xm6.h" + +#include "../rascsi.h" #include "ctapdriver.h" #include "cfilesystem.h" +#include -//=========================================================================== -// -// SCSI Host Bridge -// -//=========================================================================== +using namespace std; -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -SCSIBR::SCSIBR() : Disk() +SCSIBR::SCSIBR() : Disk("SCBR") { - // Host Bridge - disk.id = MAKEID('S', 'C', 'B', 'R'); + tap = NULL; + m_bTapEnable = false; - #if defined(RASCSI) && defined(__linux__) && !defined(BAREMETAL) + fsoptlen = 0; + fsoutlen = 0; + fsresult = 0; + packet_len = 0; + + // Create host file system + fs = new CFileSys(); + fs->Reset(); + + AddCommand(SCSIDEV::eCmdTestUnitReady, "TestUnitReady", &SCSIBR::TestUnitReady); + AddCommand(SCSIDEV::eCmdRead6, "GetMessage10", &SCSIBR::GetMessage10); + AddCommand(SCSIDEV::eCmdWrite6, "SendMessage10", &SCSIBR::SendMessage10); +} + +SCSIBR::~SCSIBR() +{ + // TAP driver release + if (tap) { + tap->Cleanup(); + delete tap; + } + + // Release host file system + if (fs) { + fs->Reset(); + delete fs; + } + + for (auto const& command : commands) { + delete command.second; + } +} + +bool SCSIBR::Init(const map& params) +{ + // Use default parameters if no parameters were provided + SetParams(params.empty() ? GetDefaultParams() : params); + +#ifdef __linux__ // TAP Driver Generation - tap = new CTapDriver(); + tap = new CTapDriver(GetParam("interfaces")); m_bTapEnable = tap->Init(); // Generate MAC Address @@ -50,34 +81,42 @@ SCSIBR::SCSIBR() : Disk() } // Packet reception flag OFF - packet_enable = FALSE; - #endif // RASCSI && !BAREMETAL + packet_enable = false; +#endif - // Create host file system - fs = new CFileSys(); - fs->Reset(); + SetReady(m_bTapEnable); + + // Not terminating on regular Linux PCs is helpful for testing +#if defined(__x86_64__) || defined(__X86__) + return true; +#else + return m_bTapEnable; +#endif } -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -SCSIBR::~SCSIBR() +void SCSIBR::AddCommand(SCSIDEV::scsi_command opcode, const char* name, void (SCSIBR::*execute)(SASIDEV *)) { - #if defined(RASCSI) && !defined(BAREMETAL) - // TAP driver release - if (tap) { - tap->Cleanup(); - delete tap; - } - #endif // RASCSI && !BAREMETAL + commands[opcode] = new command_t(name, execute); +} - // Release host file system - if (fs) { - fs->Reset(); - delete fs; +bool SCSIBR::Dispatch(SCSIDEV *controller) +{ + ctrl = controller->GetCtrl(); + + if (commands.count(static_cast(ctrl->cmd[0]))) { + command_t *command = commands[static_cast(ctrl->cmd[0])]; + + LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, command->name, (unsigned int)ctrl->cmd[0]); + + (this->*command->execute)(controller); + + return true; } + + LOGTRACE("%s Calling base class for dispatching $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->cmd[0]); + + // The base class handles the less specific commands + return Disk::Dispatch(controller); } //--------------------------------------------------------------------------- @@ -85,20 +124,15 @@ SCSIBR::~SCSIBR() // INQUIRY // //--------------------------------------------------------------------------- -int FASTCALL SCSIBR::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) +int SCSIBR::Inquiry(const DWORD *cdb, BYTE *buf) { - char rev[32]; - int size; - - ASSERT(this); ASSERT(cdb); ASSERT(buf); ASSERT(cdb[0] == 0x12); // EVPD check if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return FALSE; } @@ -109,45 +143,26 @@ int FASTCALL SCSIBR::Inquiry( // buf[4] ... Inquiry additional data memset(buf, 0, 8); buf[0] = 0x09; - - // SCSI-2 p.104 4.4.3 Incorrect logical unit handling - if (((cdb[1] >> 5) & 0x07) != disk.lun) { - buf[0] = 0x7f; - } - buf[2] = 0x02; buf[3] = 0x02; buf[4] = 36 - 5 + 8; // required + 8 byte extension - // Fill with blanks - memset(&buf[8], 0x20, buf[4] - 3); - - // Vendor name - memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); - - // Product name - memcpy(&buf[16], "RASCSI BRIDGE", 13); - - // Revision (XM6 version number) - sprintf(rev, "0%01d%01d%01d", - (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); - memcpy(&buf[32], rev, 4); + // Padded vendor, product, revision + memcpy(&buf[8], GetPaddedName().c_str(), 28); // Optional function valid flag buf[36] = '0'; - #if defined(RASCSI) && !defined(BAREMETAL) // TAP Enable if (m_bTapEnable) { buf[37] = '1'; } - #endif // RASCSI && !BAREMETAL // CFileSys Enable buf[38] = '1'; // Size of data that can be returned - size = (buf[4] + 5); + int size = (buf[4] + 5); // Limit if the other buffer is small if (size > (int)cdb[4]) { @@ -155,7 +170,6 @@ int FASTCALL SCSIBR::Inquiry( } // Success - disk.code = DISK_NOERROR; return size; } @@ -164,45 +178,29 @@ int FASTCALL SCSIBR::Inquiry( // TEST UNIT READY // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSIBR::TestUnitReady(const DWORD* /*cdb*/) +void SCSIBR::TestUnitReady(SASIDEV *controller) { - ASSERT(this); - // TEST UNIT READY Success - disk.code = DISK_NOERROR; - return TRUE; -} + + controller->Status();} //--------------------------------------------------------------------------- // // GET MESSAGE(10) // //--------------------------------------------------------------------------- -int FASTCALL SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf) +int SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf) { - int type; - int phase; - #if defined(RASCSI) && !defined(BAREMETAL) - int func; - int total_len; - int i; - #endif // RASCSI && !BAREMETAL - - ASSERT(this); - // Type - type = cdb[2]; + int type = cdb[2]; - #if defined(RASCSI) && !defined(BAREMETAL) // Function number - func = cdb[3]; - #endif // RASCSI && !BAREMETAL + int func = cdb[3]; // Phase - phase = cdb[9]; + int phase = cdb[9]; switch (type) { - #if defined(RASCSI) && !defined(BAREMETAL) case 1: // Ethernet // Do not process if TAP is invalid if (!m_bTapEnable) { @@ -236,8 +234,8 @@ int FASTCALL SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf) case 3: // Simultaneous acquisition of multiple packets (size + buffer simultaneously) // Currently the maximum number of packets is 10 // Isn't it too fast if I increase more? - total_len = 0; - for (i = 0; i < 10; i++) { + int total_len = 0; + for (int i = 0; i < 10; i++) { ReceivePacket(); *buf++ = (BYTE)(packet_len >> 8); *buf++ = (BYTE)packet_len; @@ -251,7 +249,6 @@ int FASTCALL SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf) return total_len; } break; - #endif // RASCSI && !BAREMETAL case 2: // Host Drive switch (phase) { @@ -268,7 +265,7 @@ int FASTCALL SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf) } // Error - ASSERT(FALSE); + ASSERT(false); return 0; } @@ -277,80 +274,134 @@ int FASTCALL SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf) // SEND MESSAGE(10) // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf) +bool SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf) { - int type; - int func; - int phase; - int len; - - ASSERT(this); ASSERT(cdb); ASSERT(buf); // Type - type = cdb[2]; + int type = cdb[2]; // Function number - func = cdb[3]; + int func = cdb[3]; // Phase - phase = cdb[9]; + int phase = cdb[9]; // Get the number of lights - len = cdb[6]; + int len = cdb[6]; len <<= 8; len |= cdb[7]; len <<= 8; len |= cdb[8]; switch (type) { - #if defined(RASCSI) && !defined(BAREMETAL) case 1: // Ethernet // Do not process if TAP is invalid if (!m_bTapEnable) { - return FALSE; + return false; } switch (func) { case 0: // MAC address setting SetMacAddr(buf); - return TRUE; + return true; case 1: // Send packet SendPacket(buf, len); - return TRUE; + return true; } break; - #endif // RASCSI && !BAREMETAL case 2: // Host drive switch (phase) { case 0: // issue command WriteFs(func, buf); - return TRUE; + return true; case 1: // additional data writing WriteFsOpt(buf, len); - return TRUE; + return true; } break; } // Error - ASSERT(FALSE); - return FALSE; + ASSERT(false); + return false; +} + +//--------------------------------------------------------------------------- +// +// GET MESSAGE(10) +// +//--------------------------------------------------------------------------- +void SCSIBR::GetMessage10(SASIDEV *controller) +{ + // Reallocate buffer (because it is not transfer for each block) + if (ctrl->bufsize < 0x1000000) { + free(ctrl->buffer); + ctrl->bufsize = 0x1000000; + ctrl->buffer = (BYTE *)malloc(ctrl->bufsize); + } + + ctrl->length = GetMessage10(ctrl->cmd, ctrl->buffer); + if (ctrl->length <= 0) { + // Failure (Error) + controller->Error(); + return; + } + + // Set next block + ctrl->blocks = 1; + ctrl->next = 1; + + controller->DataIn(); +} + +//--------------------------------------------------------------------------- +// +// SEND MESSAGE(10) +// +// This Send Message command is used by the X68000 host driver +// +//--------------------------------------------------------------------------- +void SCSIBR::SendMessage10(SASIDEV *controller) +{ + // Reallocate buffer (because it is not transfer for each block) + if (ctrl->bufsize < 0x1000000) { + free(ctrl->buffer); + ctrl->bufsize = 0x1000000; + ctrl->buffer = (BYTE *)malloc(ctrl->bufsize); + } + + // Set transfer amount + ctrl->length = ctrl->cmd[6]; + ctrl->length <<= 8; + ctrl->length |= ctrl->cmd[7]; + ctrl->length <<= 8; + ctrl->length |= ctrl->cmd[8]; + + if (ctrl->length <= 0) { + // Failure (Error) + controller->Error(); + return; + } + + // Set next block + ctrl->blocks = 1; + ctrl->next = 1; + + controller->DataOut(); } -#if defined(RASCSI) && !defined(BAREMETAL) //--------------------------------------------------------------------------- // // Get MAC Address // //--------------------------------------------------------------------------- -int FASTCALL SCSIBR::GetMacAddr(BYTE *mac) +int SCSIBR::GetMacAddr(BYTE *mac) { - ASSERT(this); ASSERT(mac); memcpy(mac, mac_addr, 6); @@ -362,9 +413,8 @@ int FASTCALL SCSIBR::GetMacAddr(BYTE *mac) // Set MAC Address // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::SetMacAddr(BYTE *mac) +void SCSIBR::SetMacAddr(BYTE *mac) { - ASSERT(this); ASSERT(mac); memcpy(mac_addr, mac, 6); @@ -375,11 +425,10 @@ void FASTCALL SCSIBR::SetMacAddr(BYTE *mac) // Receive Packet // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::ReceivePacket() +void SCSIBR::ReceivePacket() { static const BYTE bcast_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - ASSERT(this); ASSERT(tap); // previous packet has not been received @@ -406,7 +455,7 @@ void FASTCALL SCSIBR::ReceivePacket() // Store in receive buffer if (packet_len > 0) { - packet_enable = TRUE; + packet_enable = true; } } @@ -415,16 +464,13 @@ void FASTCALL SCSIBR::ReceivePacket() // Get Packet // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::GetPacketBuf(BYTE *buf) +void SCSIBR::GetPacketBuf(BYTE *buf) { - int len; - - ASSERT(this); ASSERT(tap); ASSERT(buf); // Size limit - len = packet_len; + int len = packet_len; if (len > 2048) { len = 2048; } @@ -433,7 +479,7 @@ void FASTCALL SCSIBR::GetPacketBuf(BYTE *buf) memcpy(buf, packet_buf, len); // Received - packet_enable = FALSE; + packet_enable = false; } //--------------------------------------------------------------------------- @@ -441,24 +487,21 @@ void FASTCALL SCSIBR::GetPacketBuf(BYTE *buf) // Send Packet // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::SendPacket(BYTE *buf, int len) +void SCSIBR::SendPacket(BYTE *buf, int len) { - ASSERT(this); ASSERT(tap); ASSERT(buf); tap->Tx(buf, len); } -#endif // RASCSI && !BAREMETAL //--------------------------------------------------------------------------- // // $40 - Device Boot // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_InitDevice(BYTE *buf) +void SCSIBR::FS_InitDevice(BYTE *buf) { - ASSERT(this); ASSERT(fs); ASSERT(buf); @@ -471,23 +514,17 @@ void FASTCALL SCSIBR::FS_InitDevice(BYTE *buf) // $41 - Directory Check // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_CheckDir(BYTE *buf) +void SCSIBR::FS_CheckDir(BYTE *buf) { - DWORD nUnit; - Human68k::namests_t *pNamests; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); - pNamests = (Human68k::namests_t*)&buf[i]; + Human68k::namests_t *pNamests = (Human68k::namests_t*)&buf[i]; i += sizeof(Human68k::namests_t); fsresult = fs->CheckDir(nUnit, pNamests); @@ -498,23 +535,17 @@ void FASTCALL SCSIBR::FS_CheckDir(BYTE *buf) // $42 - Create Directory // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_MakeDir(BYTE *buf) +void SCSIBR::FS_MakeDir(BYTE *buf) { - DWORD nUnit; - Human68k::namests_t *pNamests; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); - pNamests = (Human68k::namests_t*)&buf[i]; + Human68k::namests_t *pNamests = (Human68k::namests_t*)&buf[i]; i += sizeof(Human68k::namests_t); fsresult = fs->MakeDir(nUnit, pNamests); @@ -525,23 +556,17 @@ void FASTCALL SCSIBR::FS_MakeDir(BYTE *buf) // $43 - Remove Directory // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_RemoveDir(BYTE *buf) +void SCSIBR::FS_RemoveDir(BYTE *buf) { - DWORD nUnit; - Human68k::namests_t *pNamests; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); - pNamests = (Human68k::namests_t*)&buf[i]; + Human68k::namests_t *pNamests = (Human68k::namests_t*)&buf[i]; i += sizeof(Human68k::namests_t); fsresult = fs->RemoveDir(nUnit, pNamests); @@ -552,27 +577,20 @@ void FASTCALL SCSIBR::FS_RemoveDir(BYTE *buf) // $44 - Rename // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Rename(BYTE *buf) +void SCSIBR::FS_Rename(BYTE *buf) { - DWORD nUnit; - Human68k::namests_t *pNamests; - Human68k::namests_t* pNamestsNew; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); - pNamests = (Human68k::namests_t*)&buf[i]; + Human68k::namests_t *pNamests = (Human68k::namests_t*)&buf[i]; i += sizeof(Human68k::namests_t); - pNamestsNew = (Human68k::namests_t*)&buf[i]; + Human68k::namests_t *pNamestsNew = (Human68k::namests_t*)&buf[i]; i += sizeof(Human68k::namests_t); fsresult = fs->Rename(nUnit, pNamests, pNamestsNew); @@ -583,23 +601,17 @@ void FASTCALL SCSIBR::FS_Rename(BYTE *buf) // $45 - Delete File // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Delete(BYTE *buf) +void SCSIBR::FS_Delete(BYTE *buf) { - DWORD nUnit; - Human68k::namests_t *pNamests; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); - pNamests = (Human68k::namests_t*)&buf[i]; + Human68k::namests_t *pNamests = (Human68k::namests_t*)&buf[i]; i += sizeof(Human68k::namests_t); fsresult = fs->Delete(nUnit, pNamests); @@ -610,28 +622,21 @@ void FASTCALL SCSIBR::FS_Delete(BYTE *buf) // $46 - Get / Set file attributes // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Attribute(BYTE *buf) +void SCSIBR::FS_Attribute(BYTE *buf) { - DWORD nUnit; - Human68k::namests_t *pNamests; - DWORD nHumanAttribute; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); - pNamests = (Human68k::namests_t*)&buf[i]; + Human68k::namests_t *pNamests = (Human68k::namests_t*)&buf[i]; i += sizeof(Human68k::namests_t); dp = (DWORD*)&buf[i]; - nHumanAttribute = ntohl(*dp); + DWORD nHumanAttribute = ntohl(*dp); i += sizeof(DWORD); fsresult = fs->Attribute(nUnit, pNamests, nHumanAttribute); @@ -642,32 +647,24 @@ void FASTCALL SCSIBR::FS_Attribute(BYTE *buf) // $47 - File Search // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Files(BYTE *buf) +void SCSIBR::FS_Files(BYTE *buf) { - DWORD nUnit; - DWORD nKey; - Human68k::namests_t *pNamests; - Human68k::files_t *files; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); + DWORD nKey = ntohl(*dp); i += sizeof(DWORD); - pNamests = (Human68k::namests_t*)&buf[i]; + Human68k::namests_t *pNamests = (Human68k::namests_t*)&buf[i]; i += sizeof(Human68k::namests_t); - files = (Human68k::files_t*)&buf[i]; + Human68k::files_t *files = (Human68k::files_t*)&buf[i]; i += sizeof(Human68k::files_t); files->sector = ntohl(files->sector); @@ -696,28 +693,21 @@ void FASTCALL SCSIBR::FS_Files(BYTE *buf) // $48 - File next search // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_NFiles(BYTE *buf) +void SCSIBR::FS_NFiles(BYTE *buf) { - DWORD nUnit; - DWORD nKey; - Human68k::files_t *files; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); + DWORD nKey = ntohl(*dp); i += sizeof(DWORD); - files = (Human68k::files_t*)&buf[i]; + Human68k::files_t *files = (Human68k::files_t*)&buf[i]; i += sizeof(Human68k::files_t); files->sector = ntohl(files->sector); @@ -746,43 +736,32 @@ void FASTCALL SCSIBR::FS_NFiles(BYTE *buf) // $49 - File Creation // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Create(BYTE *buf) +void SCSIBR::FS_Create(BYTE *buf) { - DWORD nUnit; - DWORD nKey; - Human68k::namests_t *pNamests; - Human68k::fcb_t *pFcb; - DWORD nAttribute; - BOOL bForce; - DWORD *dp; - BOOL *bp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); + DWORD nKey = ntohl(*dp); i += sizeof(DWORD); - pNamests = (Human68k::namests_t*)&buf[i]; + Human68k::namests_t *pNamests = (Human68k::namests_t*)&buf[i]; i += sizeof(Human68k::namests_t); - pFcb = (Human68k::fcb_t*)&buf[i]; + Human68k::fcb_t *pFcb = (Human68k::fcb_t*)&buf[i]; i += sizeof(Human68k::fcb_t); dp = (DWORD*)&buf[i]; - nAttribute = ntohl(*dp); + DWORD nAttribute = ntohl(*dp); i += sizeof(DWORD); - bp = (BOOL*)&buf[i]; - bForce = ntohl(*bp); + BOOL *bp = (BOOL*)&buf[i]; + DWORD bForce = ntohl(*bp); i += sizeof(BOOL); pFcb->fileptr = ntohl(pFcb->fileptr); @@ -811,32 +790,24 @@ void FASTCALL SCSIBR::FS_Create(BYTE *buf) // $4A - Open File // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Open(BYTE *buf) +void SCSIBR::FS_Open(BYTE *buf) { - DWORD nUnit; - DWORD nKey; - Human68k::namests_t *pNamests; - Human68k::fcb_t *pFcb; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); + DWORD nKey = ntohl(*dp); i += sizeof(DWORD); - pNamests = (Human68k::namests_t*)&buf[i]; + Human68k::namests_t *pNamests = (Human68k::namests_t*)&buf[i]; i += sizeof(Human68k::namests_t); - pFcb = (Human68k::fcb_t*)&buf[i]; + Human68k::fcb_t *pFcb = (Human68k::fcb_t*)&buf[i]; i += sizeof(Human68k::fcb_t); pFcb->fileptr = ntohl(pFcb->fileptr); @@ -865,28 +836,21 @@ void FASTCALL SCSIBR::FS_Open(BYTE *buf) // $4B - Close File // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Close(BYTE *buf) +void SCSIBR::FS_Close(BYTE *buf) { - DWORD nUnit; - DWORD nKey; - Human68k::fcb_t *pFcb; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); + DWORD nKey = ntohl(*dp); i += sizeof(DWORD); - pFcb = (Human68k::fcb_t*)&buf[i]; + Human68k::fcb_t *pFcb = (Human68k::fcb_t*)&buf[i]; i += sizeof(Human68k::fcb_t); pFcb->fileptr = ntohl(pFcb->fileptr); @@ -915,28 +879,21 @@ void FASTCALL SCSIBR::FS_Close(BYTE *buf) // $4C - Read File // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Read(BYTE *buf) +void SCSIBR::FS_Read(BYTE *buf) { - DWORD nKey; - Human68k::fcb_t *pFcb; - DWORD nSize; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nKey = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nKey = ntohl(*dp); i += sizeof(DWORD); - pFcb = (Human68k::fcb_t*)&buf[i]; + Human68k::fcb_t *pFcb = (Human68k::fcb_t*)&buf[i]; i += sizeof(Human68k::fcb_t); dp = (DWORD*)&buf[i]; - nSize = ntohl(*dp); + DWORD nSize = ntohl(*dp); i += sizeof(DWORD); pFcb->fileptr = ntohl(pFcb->fileptr); @@ -967,28 +924,21 @@ void FASTCALL SCSIBR::FS_Read(BYTE *buf) // $4D - Write file // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Write(BYTE *buf) +void SCSIBR::FS_Write(BYTE *buf) { - DWORD nKey; - Human68k::fcb_t *pFcb; - DWORD nSize; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nKey = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nKey = ntohl(*dp); i += sizeof(DWORD); - pFcb = (Human68k::fcb_t*)&buf[i]; + Human68k::fcb_t *pFcb = (Human68k::fcb_t*)&buf[i]; i += sizeof(Human68k::fcb_t); dp = (DWORD*)&buf[i]; - nSize = ntohl(*dp); + DWORD nSize = ntohl(*dp); i += sizeof(DWORD); pFcb->fileptr = ntohl(pFcb->fileptr); @@ -1017,34 +967,25 @@ void FASTCALL SCSIBR::FS_Write(BYTE *buf) // $4E - Seek file // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Seek(BYTE *buf) +void SCSIBR::FS_Seek(BYTE *buf) { - DWORD nKey; - Human68k::fcb_t *pFcb; - DWORD nMode; - int nOffset; - DWORD *dp; - int *ip; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nKey = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nKey = ntohl(*dp); i += sizeof(DWORD); - pFcb = (Human68k::fcb_t*)&buf[i]; + Human68k::fcb_t *pFcb = (Human68k::fcb_t*)&buf[i]; i += sizeof(Human68k::fcb_t); dp = (DWORD*)&buf[i]; - nMode = ntohl(*dp); + DWORD nMode = ntohl(*dp); i += sizeof(DWORD); - ip = (int*)&buf[i]; - nOffset = ntohl(*ip); + int *ip = (int*)&buf[i]; + int nOffset = ntohl(*ip); i += sizeof(int); pFcb->fileptr = ntohl(pFcb->fileptr); @@ -1073,33 +1014,25 @@ void FASTCALL SCSIBR::FS_Seek(BYTE *buf) // $4F - File Timestamp Get / Set // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_TimeStamp(BYTE *buf) +void SCSIBR::FS_TimeStamp(BYTE *buf) { - DWORD nUnit; - DWORD nKey; - Human68k::fcb_t *pFcb; - DWORD nHumanTime; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); + DWORD nKey = ntohl(*dp); i += sizeof(DWORD); - pFcb = (Human68k::fcb_t*)&buf[i]; + Human68k::fcb_t *pFcb = (Human68k::fcb_t*)&buf[i]; i += sizeof(Human68k::fcb_t); dp = (DWORD*)&buf[i]; - nHumanTime = ntohl(*dp); + DWORD nHumanTime = ntohl(*dp); i += sizeof(DWORD); pFcb->fileptr = ntohl(pFcb->fileptr); @@ -1128,22 +1061,17 @@ void FASTCALL SCSIBR::FS_TimeStamp(BYTE *buf) // $50 - Get Capacity // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_GetCapacity(BYTE *buf) +void SCSIBR::FS_GetCapacity(BYTE *buf) { - DWORD nUnit; - Human68k::capacity_t cap; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); + Human68k::capacity_t cap; fsresult = fs->GetCapacity(nUnit, &cap); cap.freearea = htons(cap.freearea); @@ -1160,23 +1088,17 @@ void FASTCALL SCSIBR::FS_GetCapacity(BYTE *buf) // $51 - Drive status inspection/control // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_CtrlDrive(BYTE *buf) +void SCSIBR::FS_CtrlDrive(BYTE *buf) { - DWORD nUnit; - Human68k::ctrldrive_t *pCtrlDrive; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); - pCtrlDrive = (Human68k::ctrldrive_t*)&buf[i]; + Human68k::ctrldrive_t *pCtrlDrive = (Human68k::ctrldrive_t*)&buf[i]; i += sizeof(Human68k::ctrldrive_t); fsresult = fs->CtrlDrive(nUnit, pCtrlDrive); @@ -1190,22 +1112,17 @@ void FASTCALL SCSIBR::FS_CtrlDrive(BYTE *buf) // $52 - Get DPB // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_GetDPB(BYTE *buf) +void SCSIBR::FS_GetDPB(BYTE *buf) { - DWORD nUnit; - Human68k::dpb_t dpb; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); + Human68k::dpb_t dpb; fsresult = fs->GetDPB(nUnit, &dpb); dpb.sector_size = htons(dpb.sector_size); @@ -1224,29 +1141,22 @@ void FASTCALL SCSIBR::FS_GetDPB(BYTE *buf) // $53 - Read Sector // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_DiskRead(BYTE *buf) +void SCSIBR::FS_DiskRead(BYTE *buf) { - DWORD nUnit; - DWORD nSector; - DWORD nSize; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); dp = (DWORD*)&buf[i]; - nSector = ntohl(*dp); + DWORD nSector = ntohl(*dp); i += sizeof(DWORD); dp = (DWORD*)&buf[i]; - nSize = ntohl(*dp); + DWORD nSize = ntohl(*dp); i += sizeof(DWORD); fsresult = fs->DiskRead(nUnit, fsout, nSector, nSize); @@ -1258,19 +1168,14 @@ void FASTCALL SCSIBR::FS_DiskRead(BYTE *buf) // $54 - Write Sector // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_DiskWrite(BYTE *buf) +void SCSIBR::FS_DiskWrite(BYTE *buf) { - DWORD nUnit; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); fsresult = fs->DiskWrite(nUnit); @@ -1281,28 +1186,21 @@ void FASTCALL SCSIBR::FS_DiskWrite(BYTE *buf) // $55 - IOCTRL // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Ioctrl(BYTE *buf) +void SCSIBR::FS_Ioctrl(BYTE *buf) { - DWORD nUnit; - DWORD nFunction; - Human68k::ioctrl_t *pIoctrl; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); dp = (DWORD*)&buf[i]; - nFunction = ntohl(*dp); + DWORD nFunction = ntohl(*dp); i += sizeof(DWORD); - pIoctrl = (Human68k::ioctrl_t*)&buf[i]; + Human68k::ioctrl_t *pIoctrl = (Human68k::ioctrl_t*)&buf[i]; i += sizeof(Human68k::ioctrl_t); switch (nFunction) { @@ -1335,19 +1233,14 @@ void FASTCALL SCSIBR::FS_Ioctrl(BYTE *buf) // $56 - Flush // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Flush(BYTE *buf) +void SCSIBR::FS_Flush(BYTE *buf) { - DWORD nUnit; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); fsresult = fs->Flush(nUnit); @@ -1358,19 +1251,14 @@ void FASTCALL SCSIBR::FS_Flush(BYTE *buf) // $57 - Check Media // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_CheckMedia(BYTE *buf) +void SCSIBR::FS_CheckMedia(BYTE *buf) { - DWORD nUnit; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); fsresult = fs->CheckMedia(nUnit); @@ -1381,19 +1269,14 @@ void FASTCALL SCSIBR::FS_CheckMedia(BYTE *buf) // $58 - Lock // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Lock(BYTE *buf) +void SCSIBR::FS_Lock(BYTE *buf) { - DWORD nUnit; - DWORD *dp; - int i; - - ASSERT(this); ASSERT(fs); ASSERT(buf); - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); + int i = 0; + DWORD *dp = (DWORD*)buf; + DWORD nUnit = ntohl(*dp); i += sizeof(DWORD); fsresult = fs->Lock(nUnit); @@ -1404,14 +1287,11 @@ void FASTCALL SCSIBR::FS_Lock(BYTE *buf) // Read Filesystem (result code) // //--------------------------------------------------------------------------- -int FASTCALL SCSIBR::ReadFsResult(BYTE *buf) +int SCSIBR::ReadFsResult(BYTE *buf) { - DWORD *dp; - - ASSERT(this); ASSERT(buf); - dp = (DWORD*)buf; + DWORD *dp = (DWORD*)buf; *dp = htonl(fsresult); return sizeof(DWORD); } @@ -1421,9 +1301,8 @@ int FASTCALL SCSIBR::ReadFsResult(BYTE *buf) // Read Filesystem (return data) // //--------------------------------------------------------------------------- -int FASTCALL SCSIBR::ReadFsOut(BYTE *buf) +int SCSIBR::ReadFsOut(BYTE *buf) { - ASSERT(this); ASSERT(buf); memcpy(buf, fsout, fsoutlen); @@ -1435,9 +1314,8 @@ int FASTCALL SCSIBR::ReadFsOut(BYTE *buf) // Read file system (return option data) // //--------------------------------------------------------------------------- -int FASTCALL SCSIBR::ReadFsOpt(BYTE *buf) +int SCSIBR::ReadFsOpt(BYTE *buf) { - ASSERT(this); ASSERT(buf); memcpy(buf, fsopt, fsoptlen); @@ -1449,16 +1327,14 @@ int FASTCALL SCSIBR::ReadFsOpt(BYTE *buf) // Write Filesystem // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::WriteFs(int func, BYTE *buf) +void SCSIBR::WriteFs(int func, BYTE *buf) { - ASSERT(this); ASSERT(buf); fsresult = FS_FATAL_INVALIDCOMMAND; fsoutlen = 0; fsoptlen = 0; - // コマンド分岐 func &= 0x1f; switch (func) { case 0x00: return FS_InitDevice(buf); // $40 - start device @@ -1494,9 +1370,7 @@ void FASTCALL SCSIBR::WriteFs(int func, BYTE *buf) // File system write (input option data) // //--------------------------------------------------------------------------- -void FASTCALL SCSIBR::WriteFsOpt(BYTE *buf, int num) +void SCSIBR::WriteFsOpt(BYTE *buf, int num) { - ASSERT(this); - memcpy(fsopt, buf, num); } diff --git a/src/raspberrypi/devices/scsi_host_bridge.h b/src/raspberrypi/devices/scsi_host_bridge.h index a5ef6871..873b0cf5 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.h +++ b/src/raspberrypi/devices/scsi_host_bridge.h @@ -17,80 +17,96 @@ //--------------------------------------------------------------------------- #pragma once -#include "xm6.h" #include "os.h" #include "disk.h" +#include +#include "../rascsi.h" //=========================================================================== // // SCSI Host Bridge // //=========================================================================== -#if defined(RASCSI) && !defined(BAREMETAL) class CTapDriver; -#endif // RASCSI && !BAREMETAL class CFileSys; + class SCSIBR : public Disk { -public: - // Basic Functions - SCSIBR(); // Constructor - virtual ~SCSIBR(); // Destructor - - // commands - int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command - BOOL FASTCALL TestUnitReady(const DWORD *cdb); // TEST UNIT READY command - int FASTCALL GetMessage10(const DWORD *cdb, BYTE *buf); // GET MESSAGE10 command - BOOL FASTCALL SendMessage10(const DWORD *cdb, BYTE *buf); // SEND MESSAGE10 command private: - #if defined(RASCSI) && !defined(BAREMETAL) - int FASTCALL GetMacAddr(BYTE *buf); // Get MAC address - void FASTCALL SetMacAddr(BYTE *buf); // Set MAC address - void FASTCALL ReceivePacket(); // Receive a packet - void FASTCALL GetPacketBuf(BYTE *buf); // Get a packet - void FASTCALL SendPacket(BYTE *buf, int len); // Send a packet + typedef struct _command_t { + const char* name; + void (SCSIBR::*execute)(SASIDEV *); + + _command_t(const char* _name, void (SCSIBR::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { }; + } command_t; + std::map commands; + + SASIDEV::ctrl_t *ctrl; + + void AddCommand(SCSIDEV::scsi_command, const char*, void (SCSIBR::*)(SASIDEV *)); + +public: + SCSIBR(); + ~SCSIBR(); + + bool Init(const map&) override; + bool Dispatch(SCSIDEV *) override; + + // Commands + int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command + int GetMessage10(const DWORD *cdb, BYTE *buf); // GET MESSAGE10 command + bool SendMessage10(const DWORD *cdb, BYTE *buf); // SEND MESSAGE10 command + void TestUnitReady(SASIDEV *) override; + void GetMessage10(SASIDEV *); + void SendMessage10(SASIDEV *); + +private: + int GetMacAddr(BYTE *buf); // Get MAC address + void SetMacAddr(BYTE *buf); // Set MAC address + void ReceivePacket(); // Receive a packet + void GetPacketBuf(BYTE *buf); // Get a packet + void SendPacket(BYTE *buf, int len); // Send a packet CTapDriver *tap; // TAP driver - BOOL m_bTapEnable; // TAP valid flag + bool m_bTapEnable; // TAP valid flag BYTE mac_addr[6]; // MAC Addres int packet_len; // Receive packet size BYTE packet_buf[0x1000]; // Receive packet buffer - BOOL packet_enable; // Received packet valid - #endif // RASCSI && !BAREMETAL + bool packet_enable; // Received packet valid - int FASTCALL ReadFsResult(BYTE *buf); // Read filesystem (result code) - int FASTCALL ReadFsOut(BYTE *buf); // Read filesystem (return data) - int FASTCALL ReadFsOpt(BYTE *buf); // Read file system (optional data) - void FASTCALL WriteFs(int func, BYTE *buf); // File system write (execute) - void FASTCALL WriteFsOpt(BYTE *buf, int len); // File system write (optional data) + int ReadFsResult(BYTE *buf); // Read filesystem (result code) + int ReadFsOut(BYTE *buf); // Read filesystem (return data) + int ReadFsOpt(BYTE *buf); // Read file system (optional data) + void WriteFs(int func, BYTE *buf); // File system write (execute) + void WriteFsOpt(BYTE *buf, int len); // File system write (optional data) // Command handlers - void FASTCALL FS_InitDevice(BYTE *buf); // $40 - boot - void FASTCALL FS_CheckDir(BYTE *buf); // $41 - directory check - void FASTCALL FS_MakeDir(BYTE *buf); // $42 - create directory - void FASTCALL FS_RemoveDir(BYTE *buf); // $43 - delete directory - void FASTCALL FS_Rename(BYTE *buf); // $44 - change filename - void FASTCALL FS_Delete(BYTE *buf); // $45 - delete file - void FASTCALL FS_Attribute(BYTE *buf); // $46 - get/set file attributes - void FASTCALL FS_Files(BYTE *buf); // $47 - file search - void FASTCALL FS_NFiles(BYTE *buf); // $48 - find next file - void FASTCALL FS_Create(BYTE *buf); // $49 - create file - void FASTCALL FS_Open(BYTE *buf); // $4A - open file - void FASTCALL FS_Close(BYTE *buf); // $4B - close file - void FASTCALL FS_Read(BYTE *buf); // $4C - read file - void FASTCALL FS_Write(BYTE *buf); // $4D - write file - void FASTCALL FS_Seek(BYTE *buf); // $4E - seek file - void FASTCALL FS_TimeStamp(BYTE *buf); // $4F - get/set file time - void FASTCALL FS_GetCapacity(BYTE *buf); // $50 - get capacity - void FASTCALL FS_CtrlDrive(BYTE *buf); // $51 - drive status check/control - void FASTCALL FS_GetDPB(BYTE *buf); // $52 - get DPB - void FASTCALL FS_DiskRead(BYTE *buf); // $53 - read sector - void FASTCALL FS_DiskWrite(BYTE *buf); // $54 - write sector - void FASTCALL FS_Ioctrl(BYTE *buf); // $55 - IOCTRL - void FASTCALL FS_Flush(BYTE *buf); // $56 - flush cache - void FASTCALL FS_CheckMedia(BYTE *buf); // $57 - check media - void FASTCALL FS_Lock(BYTE *buf); // $58 - get exclusive control + void FS_InitDevice(BYTE *buf); // $40 - boot + void FS_CheckDir(BYTE *buf); // $41 - directory check + void FS_MakeDir(BYTE *buf); // $42 - create directory + void FS_RemoveDir(BYTE *buf); // $43 - delete directory + void FS_Rename(BYTE *buf); // $44 - change filename + void FS_Delete(BYTE *buf); // $45 - delete file + void FS_Attribute(BYTE *buf); // $46 - get/set file attributes + void FS_Files(BYTE *buf); // $47 - file search + void FS_NFiles(BYTE *buf); // $48 - find next file + void FS_Create(BYTE *buf); // $49 - create file + void FS_Open(BYTE *buf); // $4A - open file + void FS_Close(BYTE *buf); // $4B - close file + void FS_Read(BYTE *buf); // $4C - read file + void FS_Write(BYTE *buf); // $4D - write file + void FS_Seek(BYTE *buf); // $4E - seek file + void FS_TimeStamp(BYTE *buf); // $4F - get/set file time + void FS_GetCapacity(BYTE *buf); // $50 - get capacity + void FS_CtrlDrive(BYTE *buf); // $51 - drive status check/control + void FS_GetDPB(BYTE *buf); // $52 - get DPB + void FS_DiskRead(BYTE *buf); // $53 - read sector + void FS_DiskWrite(BYTE *buf); // $54 - write sector + void FS_Ioctrl(BYTE *buf); // $55 - IOCTRL + void FS_Flush(BYTE *buf); // $56 - flush cache + void FS_CheckMedia(BYTE *buf); // $57 - check media + void FS_Lock(BYTE *buf); // $58 - get exclusive control CFileSys *fs; // File system accessor DWORD fsresult; // File system access result code diff --git a/src/raspberrypi/devices/scsicd.cpp b/src/raspberrypi/devices/scsicd.cpp index 8e91c1c4..9d654d0c 100644 --- a/src/raspberrypi/devices/scsicd.cpp +++ b/src/raspberrypi/devices/scsicd.cpp @@ -10,13 +10,15 @@ // Licensed under the BSD 3-Clause License. // See LICENSE file in the project root folder. // -// [ SCSI Hard Disk for Apple Macintosh ] +// [ SCSI CD-ROM for Apple Macintosh ] // //--------------------------------------------------------------------------- -#include "xm6.h" #include "scsicd.h" #include "fileio.h" +#include "exceptions.h" +#include +#include "../rascsi.h" //=========================================================================== // @@ -37,14 +39,14 @@ CDTrack::CDTrack(SCSICD *scsicd) cdrom = scsicd; // Track defaults to disabled - valid = FALSE; + valid = false; // Initialize other data track_no = -1; first_lba = 0; last_lba = 0; - audio = FALSE; - raw = FALSE; + audio = false; + raw = false; } //--------------------------------------------------------------------------- @@ -61,9 +63,8 @@ CDTrack::~CDTrack() // Init // //--------------------------------------------------------------------------- -BOOL FASTCALL CDTrack::Init(int track, DWORD first, DWORD last) +void CDTrack::Init(int track, DWORD first, DWORD last) { - ASSERT(this); ASSERT(!valid); ASSERT(track >= 1); ASSERT(first < last); @@ -75,8 +76,6 @@ BOOL FASTCALL CDTrack::Init(int track, DWORD first, DWORD last) // Remember LBA first_lba = first; last_lba = last; - - return TRUE; } //--------------------------------------------------------------------------- @@ -84,9 +83,8 @@ BOOL FASTCALL CDTrack::Init(int track, DWORD first, DWORD last) // Set Path // //--------------------------------------------------------------------------- -void FASTCALL CDTrack::SetPath(BOOL cdda, const Filepath& path) +void CDTrack::SetPath(bool cdda, const Filepath& path) { - ASSERT(this); ASSERT(valid); // CD-DA or data @@ -101,9 +99,8 @@ void FASTCALL CDTrack::SetPath(BOOL cdda, const Filepath& path) // Get Path // //--------------------------------------------------------------------------- -void FASTCALL CDTrack::GetPath(Filepath& path) const +void CDTrack::GetPath(Filepath& path) const { - ASSERT(this); ASSERT(valid); // Return the path (by reference) @@ -115,9 +112,8 @@ void FASTCALL CDTrack::GetPath(Filepath& path) const // Add Index // //--------------------------------------------------------------------------- -void FASTCALL CDTrack::AddIndex(int index, DWORD lba) +void CDTrack::AddIndex(int index, DWORD lba) { - ASSERT(this); ASSERT(valid); ASSERT(index > 0); ASSERT(first_lba <= lba); @@ -132,9 +128,8 @@ void FASTCALL CDTrack::AddIndex(int index, DWORD lba) // Gets the start of LBA // //--------------------------------------------------------------------------- -DWORD FASTCALL CDTrack::GetFirst() const +DWORD CDTrack::GetFirst() const { - ASSERT(this); ASSERT(valid); ASSERT(first_lba < last_lba); @@ -146,9 +141,8 @@ DWORD FASTCALL CDTrack::GetFirst() const // Get the end of LBA // //--------------------------------------------------------------------------- -DWORD FASTCALL CDTrack::GetLast() const +DWORD CDTrack::GetLast() const { - ASSERT(this); ASSERT(valid); ASSERT(first_lba < last_lba); @@ -160,9 +154,8 @@ DWORD FASTCALL CDTrack::GetLast() const // Get the number of blocks // //--------------------------------------------------------------------------- -DWORD FASTCALL CDTrack::GetBlocks() const +DWORD CDTrack::GetBlocks() const { - ASSERT(this); ASSERT(valid); ASSERT(first_lba < last_lba); @@ -175,9 +168,8 @@ DWORD FASTCALL CDTrack::GetBlocks() const // Get track number // //--------------------------------------------------------------------------- -int FASTCALL CDTrack::GetTrackNo() const +int CDTrack::GetTrackNo() const { - ASSERT(this); ASSERT(valid); ASSERT(track_no >= 1); @@ -189,27 +181,25 @@ int FASTCALL CDTrack::GetTrackNo() const // Is valid block // //--------------------------------------------------------------------------- -BOOL FASTCALL CDTrack::IsValid(DWORD lba) const +bool CDTrack::IsValid(DWORD lba) const { - ASSERT(this); - // FALSE if the track itself is invalid if (!valid) { - return FALSE; + return false; } // If the block is BEFORE the first block if (lba < first_lba) { - return FALSE; + return false; } // If the block is AFTER the last block if (last_lba < lba) { - return FALSE; + return false; } // This track is valid - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -217,38 +207,13 @@ BOOL FASTCALL CDTrack::IsValid(DWORD lba) const // Is audio track // //--------------------------------------------------------------------------- -BOOL FASTCALL CDTrack::IsAudio() const +bool CDTrack::IsAudio() const { - ASSERT(this); ASSERT(valid); return audio; } -//=========================================================================== -// -// CD-DA Buffer -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -CDDABuf::CDDABuf() -{ -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -CDDABuf::~CDDABuf() -{ -} - //=========================================================================== // // SCSI CD-ROM @@ -260,30 +225,24 @@ CDDABuf::~CDDABuf() // Constructor // //--------------------------------------------------------------------------- -SCSICD::SCSICD() : Disk() +SCSICD::SCSICD() : Disk("SCCD"), ScsiMmcCommands(), FileSupport() { - int i; - - // SCSI CD-ROM - disk.id = MAKEID('S', 'C', 'C', 'D'); - - // removable, write protected - disk.removable = TRUE; - disk.writep = TRUE; - // NOT in raw format - rawfile = FALSE; + rawfile = false; // Frame initialization frame = 0; // Track initialization - for (i = 0; i < TrackMax; i++) { + for (int i = 0; i < TrackMax; i++) { track[i] = NULL; } tracks = 0; dataindex = -1; audioindex = -1; + + AddCommand(SCSIDEV::eCmdReadToc, "ReadToc", &SCSICD::ReadToc); + AddCommand(SCSIDEV::eCmdGetEventStatusNotification, "GetEventStatusNotification", &SCSICD::GetEventStatusNotification); } //--------------------------------------------------------------------------- @@ -295,186 +254,107 @@ SCSICD::~SCSICD() { // Clear track ClearTrack(); + + for (auto const& command : commands) { + delete command.second; + } } -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Load -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::Load(Fileio *fio, int ver) +void SCSICD::AddCommand(SCSIDEV::scsi_command opcode, const char* name, void (SCSICD::*execute)(SASIDEV *)) { - DWORD sz; - disk_t buf; - DWORD padding; - Filepath path; - - ASSERT(this); - ASSERT(fio); - ASSERT(ver >= 0x0200); - - // Prior to version 2.03, the disk was not saved - if (ver <= 0x0202) { - return TRUE; - } - - // load size, match - if (!fio->Read(&sz, sizeof(sz))) { - return FALSE; - } - if (sz != 52) { - return FALSE; - } - - // load into buffer - PROP_IMPORT(fio, buf.id); - PROP_IMPORT(fio, buf.ready); - PROP_IMPORT(fio, buf.writep); - PROP_IMPORT(fio, buf.readonly); - PROP_IMPORT(fio, buf.removable); - PROP_IMPORT(fio, buf.lock); - PROP_IMPORT(fio, buf.attn); - PROP_IMPORT(fio, buf.reset); - PROP_IMPORT(fio, buf.size); - PROP_IMPORT(fio, buf.blocks); - PROP_IMPORT(fio, buf.lun); - PROP_IMPORT(fio, buf.code); - PROP_IMPORT(fio, padding); - - // Load path - if (!path.Load(fio, ver)) { - return FALSE; - } - - // Always eject - Eject(TRUE); - - // move only if IDs match - if (disk.id != buf.id) { - // It was not a CD-ROM when saving. Maintain eject status - return TRUE; - } - - // Try to reopen - if (!Open(path, FALSE)) { - // Cannot reopen. Maintain eject status - return TRUE; - } - - // Disk cache is created in Open. Move property only - if (!disk.readonly) { - disk.writep = buf.writep; - } - disk.lock = buf.lock; - disk.attn = buf.attn; - disk.reset = buf.reset; - disk.lun = buf.lun; - disk.code = buf.code; - - // Discard the disk cache again - if (disk.dcache) { - delete disk.dcache; - disk.dcache = NULL; - } - disk.dcache = NULL; - - // Tentative - disk.blocks = track[0]->GetBlocks(); - if (disk.blocks > 0) { - // Recreate the disk cache - track[0]->GetPath(path); - disk.dcache = new DiskCache(path, disk.size, disk.blocks); - disk.dcache->SetRawMode(rawfile); - - // Reset data index - dataindex = 0; - } - - return TRUE; + commands[opcode] = new command_t(name, execute); +} + +bool SCSICD::Dispatch(SCSIDEV *controller) +{ + ctrl = controller->GetCtrl(); + + if (commands.count(static_cast(ctrl->cmd[0]))) { + command_t *command = commands[static_cast(ctrl->cmd[0])]; + + LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, command->name, (unsigned int)ctrl->cmd[0]); + + (this->*command->execute)(controller); + + return true; + } + + LOGTRACE("%s Calling base class for dispatching $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->cmd[0]); + + // The base class handles the less specific commands + return Disk::Dispatch(controller); } -#endif // RASCSI //--------------------------------------------------------------------------- // // Open // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::Open(const Filepath& path, BOOL attn) +void SCSICD::Open(const Filepath& path) { - Fileio fio; - off64_t size; - TCHAR file[5]; + off_t size; - ASSERT(this); - ASSERT(!disk.ready); + ASSERT(!IsReady()); // Initialization, track clear - disk.blocks = 0; - rawfile = FALSE; + SetBlockCount(0); + rawfile = false; ClearTrack(); // Open as read-only + Fileio fio; if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; + throw file_not_found_exception("Can't open CD-ROM file"); } + // Default sector size is 2048 bytes + SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 2048, false); + // Close and transfer for physical CD access if (path.GetPath()[0] == _T('\\')) { // Close fio.Close(); // Open physical CD - if (!OpenPhysical(path)) { - return FALSE; - } + OpenPhysical(path); } else { // Get file size size = fio.GetFileSize(); if (size <= 4) { fio.Close(); - return FALSE; + throw io_exception("CD-ROM file size must be at least 4 bytes"); } // Judge whether it is a CUE sheet or an ISO file + TCHAR file[5]; fio.Read(file, 4); file[4] = '\0'; fio.Close(); // If it starts with FILE, consider it as a CUE sheet - if (xstrncasecmp(file, _T("FILE"), 4) == 0) { + if (!strncasecmp(file, _T("FILE"), 4)) { // Open as CUE - if (!OpenCue(path)) { - return FALSE; - } + OpenCue(path); } else { // Open as ISO - if (!OpenIso(path)) { - return FALSE; - } + OpenIso(path); } } // Successful opening - ASSERT(disk.blocks > 0); - disk.size = 11; + ASSERT(GetBlockCount() > 0); - // Call the base class Disk::Open(path); + FileSupport::SetPath(path); // Set RAW flag ASSERT(disk.dcache); disk.dcache->SetRawMode(rawfile); - // Since it is a ROM media, writing is not possible - disk.writep = TRUE; - // Attention if ready - if (disk.ready && attn) { - disk.attn = TRUE; + if (IsReady()) { + SetAttn(true); } - - return TRUE; } //--------------------------------------------------------------------------- @@ -482,104 +362,85 @@ BOOL FASTCALL SCSICD::Open(const Filepath& path, BOOL attn) // Open (CUE) // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::OpenCue(const Filepath& /*path*/) +void SCSICD::OpenCue(const Filepath& /*path*/) { - ASSERT(this); - - // Always fail - return FALSE; + throw io_exception("Opening CUE CD-ROM files is not supported"); } //--------------------------------------------------------------------------- // -// オープン(ISO) +// Open (ISO) // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::OpenIso(const Filepath& path) +void SCSICD::OpenIso(const Filepath& path) { - Fileio fio; - off64_t size; - BYTE header[12]; - BYTE sync[12]; - - ASSERT(this); - // Open as read-only + Fileio fio; if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; + throw io_exception("Can't open ISO CD-ROM file"); } // Get file size - size = fio.GetFileSize(); + off_t size = fio.GetFileSize(); if (size < 0x800) { fio.Close(); - return FALSE; + throw io_exception("ISO CD-ROM file size must be at least 2048 bytes"); } // Read the first 12 bytes and close + BYTE header[12]; if (!fio.Read(header, sizeof(header))) { fio.Close(); - return FALSE; + throw io_exception("Can't read header of ISO CD-ROM file"); } // Check if it is RAW format + BYTE sync[12]; memset(sync, 0xff, sizeof(sync)); sync[0] = 0x00; sync[11] = 0x00; - rawfile = FALSE; + rawfile = false; if (memcmp(header, sync, sizeof(sync)) == 0) { // 00,FFx10,00, so it is presumed to be RAW format if (!fio.Read(header, 4)) { fio.Close(); - return FALSE; + throw io_exception("Can't read header of raw ISO CD-ROM file"); } // Supports MODE1/2048 or MODE1/2352 only if (header[3] != 0x01) { // Different mode fio.Close(); - return FALSE; + throw io_exception("Illegal raw ISO CD-ROM file header"); } // Set to RAW file - rawfile = TRUE; + rawfile = true; } fio.Close(); if (rawfile) { - // Size must be a multiple of 2536 and less than 700MB - if (size % 0x930) { - return FALSE; - } - if (size > 912579600) { - return FALSE; + // Size must be a multiple of 2536 + if (size % 2536) { + stringstream error; + error << "Raw ISO CD-ROM file size must be a multiple of 2536 bytes but is " << size << " bytes"; + throw io_exception(error.str()); } // Set the number of blocks - disk.blocks = (DWORD)(size / 0x930); + SetBlockCount((DWORD)(size / 0x930)); } else { - // Size must be a multiple of 2048 and less than 700MB - if (size & 0x7ff) { - return FALSE; - } - if (size > 0x2bed5000) { - return FALSE; - } - // Set the number of blocks - disk.blocks = (DWORD)(size >> 11); + SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount())); } // Create only one data track ASSERT(!track[0]); track[0] = new CDTrack(this); - track[0]->Init(1, 0, disk.blocks - 1); - track[0]->SetPath(FALSE, path); + track[0]->Init(1, 0, GetBlockCount() - 1); + track[0]->SetPath(false, path); tracks = 1; dataindex = 0; - - // Successful opening - return TRUE; } //--------------------------------------------------------------------------- @@ -587,49 +448,49 @@ BOOL FASTCALL SCSICD::OpenIso(const Filepath& path) // Open (Physical) // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::OpenPhysical(const Filepath& path) +void SCSICD::OpenPhysical(const Filepath& path) { - Fileio fio; - off64_t size; - - ASSERT(this); - // Open as read-only + Fileio fio; if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; + throw io_exception("Can't open CD-ROM file"); } // Get size - size = fio.GetFileSize(); + off_t size = fio.GetFileSize(); if (size < 0x800) { fio.Close(); - return FALSE; + throw io_exception("CD-ROM file size must be at least 2048 bytes"); } // Close fio.Close(); - // Size must be a multiple of 2048 and less than 700MB - if (size & 0x7ff) { - return FALSE; - } - if (size > 0x2bed5000) { - return FALSE; - } + // Effective size must be a multiple of 512 + size = (size / 512) * 512; // Set the number of blocks - disk.blocks = (DWORD)(size >> 11); + SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount())); // Create only one data track ASSERT(!track[0]); track[0] = new CDTrack(this); - track[0]->Init(1, 0, disk.blocks - 1); - track[0]->SetPath(FALSE, path); + track[0]->Init(1, 0, GetBlockCount() - 1); + track[0]->SetPath(false, path); tracks = 1; dataindex = 0; +} - // Successful opening - return TRUE; +void SCSICD::ReadToc(SASIDEV *controller) +{ + ctrl->length = ReadToc(ctrl->cmd, ctrl->buffer); + if (ctrl->length <= 0) { + // Failure (Error) + controller->Error(); + return; + } + + controller->DataIn(); } //--------------------------------------------------------------------------- @@ -637,20 +498,14 @@ BOOL FASTCALL SCSICD::OpenPhysical(const Filepath& path) // INQUIRY // //--------------------------------------------------------------------------- -int FASTCALL SCSICD::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) +int SCSICD::Inquiry(const DWORD *cdb, BYTE *buf) { - char rev[32]; - int size; - - ASSERT(this); ASSERT(cdb); ASSERT(buf); - ASSERT(cdb[0] == 0x12); // EVPD check if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return FALSE; } @@ -662,12 +517,6 @@ int FASTCALL SCSICD::Inquiry( // buf[4] ... Inquiry additional data memset(buf, 0, 8); buf[0] = 0x05; - - // SCSI-2 p.104 4.4.3 Incorrect logical unit handling - if (((cdb[1] >> 5) & 0x07) != disk.lun) { - buf[0] = 0x7f; - } - buf[1] = 0x80; buf[2] = 0x02; buf[3] = 0x02; @@ -676,16 +525,9 @@ int FASTCALL SCSICD::Inquiry( // Fill with blanks memset(&buf[8], 0x20, buf[4] - 3); - // Vendor name - memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); + // Padded vendor, product, revision + memcpy(&buf[8], GetPaddedName().c_str(), 28); - // Product name - memcpy(&buf[16], "CD-ROM CDU-55S", 14); - - // Revision (XM6 version number) - sprintf(rev, "0%01d%01d%01d", - (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); - memcpy(&buf[32], rev, 4); // // The following code worked with the modified Apple CD-ROM drivers. Need to // test with the original code to see if it works as well.... @@ -710,15 +552,13 @@ int FASTCALL SCSICD::Inquiry( // memcpy(&buf[37],"1999/01/01",10); // Size of data that can be returned - size = (buf[4] + 5); + int size = (buf[4] + 5); // Limit if the other buffer is small if (size > (int)cdb[4]) { size = (int)cdb[4]; } - // Success - disk.code = DISK_NOERROR; return size; } @@ -727,12 +567,8 @@ int FASTCALL SCSICD::Inquiry( // READ // //--------------------------------------------------------------------------- -int FASTCALL SCSICD::Read(BYTE *buf, DWORD block) +int SCSICD::Read(const DWORD *cdb, BYTE *buf, uint64_t block) { - int index; - Filepath path; - - ASSERT(this); ASSERT(buf); // Status check @@ -741,11 +577,11 @@ int FASTCALL SCSICD::Read(BYTE *buf, DWORD block) } // Search for the track - index = SearchTrack(block); + int index = SearchTrack(block); // if invalid, out of range if (index < 0) { - disk.code = DISK_INVALIDLBA; + SetStatusCode(STATUS_INVALIDLBA); return 0; } ASSERT(track[index]); @@ -757,12 +593,13 @@ int FASTCALL SCSICD::Read(BYTE *buf, DWORD block) disk.dcache = NULL; // Reset the number of blocks - disk.blocks = track[index]->GetBlocks(); - ASSERT(disk.blocks > 0); + SetBlockCount(track[index]->GetBlocks()); + ASSERT(GetBlockCount() > 0); // Recreate the disk cache + Filepath path; track[index]->GetPath(path); - disk.dcache = new DiskCache(path, disk.size, disk.blocks); + disk.dcache = new DiskCache(path, GetSectorSizeShiftCount(), GetBlockCount()); disk.dcache->SetRawMode(rawfile); // Reset data index @@ -771,7 +608,7 @@ int FASTCALL SCSICD::Read(BYTE *buf, DWORD block) // Base class ASSERT(dataindex >= 0); - return Disk::Read(buf, block); + return Disk::Read(cdb, buf, block); } //--------------------------------------------------------------------------- @@ -779,19 +616,9 @@ int FASTCALL SCSICD::Read(BYTE *buf, DWORD block) // READ TOC // //--------------------------------------------------------------------------- -int FASTCALL SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) +int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) { - int last; - int index; - int length; - int loop; - int i; - BOOL msf; - DWORD lba; - - ASSERT(this); ASSERT(cdb); - ASSERT(cdb[0] == 0x43); ASSERT(buf); // Check if ready @@ -804,29 +631,25 @@ int FASTCALL SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) ASSERT(track[0]); // Get allocation length, clear buffer - length = cdb[7] << 8; + int length = cdb[7] << 8; length |= cdb[8]; memset(buf, 0, length); // Get MSF Flag - if (cdb[1] & 0x02) { - msf = TRUE; - } else { - msf = FALSE; - } + bool msf = cdb[1] & 0x02; // Get and check the last track number - last = track[tracks - 1]->GetTrackNo(); + int last = track[tracks - 1]->GetTrackNo(); if ((int)cdb[6] > last) { // Except for AA if (cdb[6] != 0xaa) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return 0; } } // Check start index - index = 0; + int index = 0; if (cdb[6] != 0x00) { // Advance the track until the track numbers match while (track[index]) { @@ -845,7 +668,7 @@ int FASTCALL SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) buf[2] = (BYTE)track[0]->GetTrackNo(); buf[3] = (BYTE)last; buf[6] = 0xaa; - lba = track[tracks - 1]->GetLast() + 1; + DWORD lba = track[tracks - 1]->GetLast() + 1; if (msf) { LBAtoMSF(lba, &buf[8]); } else { @@ -856,13 +679,13 @@ int FASTCALL SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) } // Otherwise, error - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return 0; } } // Number of track descriptors returned this time (number of loops) - loop = last - track[index]->GetTrackNo() + 1; + int loop = last - track[index]->GetTrackNo() + 1; ASSERT(loop >= 1); // Create header @@ -873,7 +696,7 @@ int FASTCALL SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) buf += 4; // Loop.... - for (i = 0; i < loop; i++) { + for (int i = 0; i < loop; i++) { // ADR and Control if (track[index]->IsAudio()) { // audio track @@ -903,43 +726,16 @@ int FASTCALL SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) return length; } -//--------------------------------------------------------------------------- -// -// PLAY AUDIO -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::PlayAudio(const DWORD* /*cdb*/) +void SCSICD::GetEventStatusNotification(SASIDEV *controller) { - ASSERT(this); + if (!ctrl->cmd[1] & 0x01) { + // Asynchronous notification is optional and not supported by rascsi + controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_FIELD_IN_CDB); + return; + } - disk.code = DISK_INVALIDCDB; - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO MSF -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::PlayAudioMSF(const DWORD* /*cdb*/) -{ - ASSERT(this); - - disk.code = DISK_INVALIDCDB; - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO TRACK -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::PlayAudioTrack(const DWORD* /*cdb*/) -{ - ASSERT(this); - - disk.code = DISK_INVALIDCDB; - return FALSE; + LOGTRACE("Received request for event polling, which is currently not supported"); + controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_FIELD_IN_CDB); } //--------------------------------------------------------------------------- @@ -947,18 +743,12 @@ BOOL FASTCALL SCSICD::PlayAudioTrack(const DWORD* /*cdb*/) // LBA→MSF Conversion // //--------------------------------------------------------------------------- -void FASTCALL SCSICD::LBAtoMSF(DWORD lba, BYTE *msf) const +void SCSICD::LBAtoMSF(DWORD lba, BYTE *msf) const { - DWORD m; - DWORD s; - DWORD f; - - ASSERT(this); - // 75 and 75*60 get the remainder - m = lba / (75 * 60); - s = lba % (75 * 60); - f = s % 75; + DWORD m = lba / (75 * 60); + DWORD s = lba % (75 * 60); + DWORD f = s % 75; s /= 75; // The base point is M=0, S=2, F=0 @@ -978,45 +768,15 @@ void FASTCALL SCSICD::LBAtoMSF(DWORD lba, BYTE *msf) const msf[3] = (BYTE)f; } -//--------------------------------------------------------------------------- -// -// MSF→LBA Conversion -// -//--------------------------------------------------------------------------- -DWORD FASTCALL SCSICD::MSFtoLBA(const BYTE *msf) const -{ - DWORD lba; - - ASSERT(this); - ASSERT(msf[2] < 60); - ASSERT(msf[3] < 75); - - // 1, 75, add up in multiples of 75*60 - lba = msf[1]; - lba *= 60; - lba += msf[2]; - lba *= 75; - lba += msf[3]; - - // Since the base point is M=0, S=2, F=0, subtract 150 - lba -= 150; - - return lba; -} - //--------------------------------------------------------------------------- // // Clear Track // //--------------------------------------------------------------------------- -void FASTCALL SCSICD::ClearTrack() +void SCSICD::ClearTrack() { - int i; - - ASSERT(this); - // delete the track object - for (i = 0; i < TrackMax; i++) { + for (int i = 0; i < TrackMax; i++) { if (track[i]) { delete track[i]; track[i] = NULL; @@ -1037,14 +797,10 @@ void FASTCALL SCSICD::ClearTrack() // * Returns -1 if not found // //--------------------------------------------------------------------------- -int FASTCALL SCSICD::SearchTrack(DWORD lba) const +int SCSICD::SearchTrack(DWORD lba) const { - int i; - - ASSERT(this); - // Track loop - for (i = 0; i < tracks; i++) { + for (int i = 0; i < tracks; i++) { // Listen to the track ASSERT(track[i]); if (track[i]->IsValid(lba)) { @@ -1056,34 +812,3 @@ int FASTCALL SCSICD::SearchTrack(DWORD lba) const return -1; } -//--------------------------------------------------------------------------- -// -// Next Frame -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::NextFrame() -{ - ASSERT(this); - ASSERT((frame >= 0) && (frame < 75)); - - // set the frame in the range 0-74 - frame = (frame + 1) % 75; - - // FALSE after one lap - if (frame != 0) { - return TRUE; - } else { - return FALSE; - } -} - -//--------------------------------------------------------------------------- -// -// Get CD-DA buffer -// -//--------------------------------------------------------------------------- -void FASTCALL SCSICD::GetBuf( - DWORD* /*buffer*/, int /*samples*/, DWORD /*rate*/) -{ - ASSERT(this); -} diff --git a/src/raspberrypi/devices/scsicd.h b/src/raspberrypi/devices/scsicd.h index ab703aab..7e738b15 100644 --- a/src/raspberrypi/devices/scsicd.h +++ b/src/raspberrypi/devices/scsicd.h @@ -10,7 +10,7 @@ // Licensed under the BSD 3-Clause License. // See LICENSE file in the project root folder. // -// [ SCSI Hard Disk for Apple Macintosh ] +// [ SCSI CD-ROM for Apple Macintosh ] // //--------------------------------------------------------------------------- #pragma once @@ -18,6 +18,8 @@ #include "os.h" #include "disk.h" #include "filepath.h" +#include "interfaces/scsi_mmc_commands.h" +#include "interfaces/scsi_primary_commands.h" //--------------------------------------------------------------------------- @@ -35,128 +37,91 @@ class SCSICD; class CDTrack { public: - // Basic Functions - CDTrack(SCSICD *scsicd); // Constructor - virtual ~CDTrack(); // Destructor - BOOL FASTCALL Init(int track, DWORD first, DWORD last); // Initialization + CDTrack(SCSICD *scsicd); + virtual ~CDTrack(); + + void Init(int track, DWORD first, DWORD last); // Properties - void FASTCALL SetPath(BOOL cdda, const Filepath& path); // Set the path - void FASTCALL GetPath(Filepath& path) const; // Get the path - void FASTCALL AddIndex(int index, DWORD lba); // Add index - DWORD FASTCALL GetFirst() const; // Get the start LBA - DWORD FASTCALL GetLast() const; // Get the last LBA - DWORD FASTCALL GetBlocks() const; // Get the number of blocks - int FASTCALL GetTrackNo() const; // Get the track number - BOOL FASTCALL IsValid(DWORD lba) const; // Is this a valid LBA? - BOOL FASTCALL IsAudio() const; // Is this an audio track? + void SetPath(bool cdda, const Filepath& path); // Set the path + void GetPath(Filepath& path) const; // Get the path + void AddIndex(int index, DWORD lba); // Add index + DWORD GetFirst() const; // Get the start LBA + DWORD GetLast() const; // Get the last LBA + DWORD GetBlocks() const; // Get the number of blocks + int GetTrackNo() const; // Get the track number + bool IsValid(DWORD lba) const; // Is this a valid LBA? + bool IsAudio() const; // Is this an audio track? private: SCSICD *cdrom; // Parent device - BOOL valid; // Valid track + bool valid; // Valid track int track_no; // Track number DWORD first_lba; // First LBA DWORD last_lba; // Last LBA - BOOL audio; // Audio track flag - BOOL raw; // RAW data flag + bool audio; // Audio track flag + bool raw; // RAW data flag Filepath imgpath; // Image file path }; -//=========================================================================== -// -// CD-DA Buffer -// -//=========================================================================== -class CDDABuf -{ -public: - // Basic Functions - CDDABuf(); // Constructor - virtual ~CDDABuf(); // Destructor - #if 0 - BOOL Init(); // Initialization - BOOL FASTCALL Load(const Filepath& path); // Load - BOOL FASTCALL Save(const Filepath& path); // Save - - // API - void FASTCALL Clear(); // Clear the buffer - BOOL FASTCALL Open(Filepath& path); // File specification - BOOL FASTCALL GetBuf(DWORD *buffer, int frames); // Get the buffer - BOOL FASTCALL IsValid(); // Check if Valid - BOOL FASTCALL ReadReq(); // Read Request - BOOL FASTCALL IsEnd() const; // Finish check - -private: - Filepath wavepath; // Wave path - BOOL valid; // Open result (is it valid?) - DWORD *buf; // Data buffer - DWORD read; // Read pointer - DWORD write; // Write pointer - DWORD num; // Valid number of data - DWORD rest; // Remaining file size -#endif -}; - //=========================================================================== // // SCSI CD-ROM // //=========================================================================== -class SCSICD : public Disk +class SCSICD : public Disk, public ScsiMmcCommands, public FileSupport { +private: + typedef struct _command_t { + const char* name; + void (SCSICD::*execute)(SASIDEV *); + + _command_t(const char* _name, void (SCSICD::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { }; + } command_t; + std::map commands; + + SASIDEV::ctrl_t *ctrl; + + void AddCommand(SCSIDEV::scsi_command, const char*, void (SCSICD::*)(SASIDEV *)); + public: - // Number of tracks enum { TrackMax = 96 // Maximum number of tracks }; public: - // Basic Functions - SCSICD(); // Constructor - virtual ~SCSICD(); // Destructor - BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); // Open - #ifndef RASCSI - BOOL FASTCALL Load(Fileio *fio, int ver); // Load - #endif // RASCSI + SCSICD(); + ~SCSICD(); - // commands - int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command - int FASTCALL Read(BYTE *buf, DWORD block); // READ command - int FASTCALL ReadToc(const DWORD *cdb, BYTE *buf); // READ TOC command - BOOL FASTCALL PlayAudio(const DWORD *cdb); // PLAY AUDIO command - BOOL FASTCALL PlayAudioMSF(const DWORD *cdb); // PLAY AUDIO MSF command - BOOL FASTCALL PlayAudioTrack(const DWORD *cdb); // PLAY AUDIO TRACK command + bool Dispatch(SCSIDEV *) override; - // CD-DA - BOOL FASTCALL NextFrame(); // Frame notification - void FASTCALL GetBuf(DWORD *buffer, int samples, DWORD rate); // Get CD-DA buffer + void Open(const Filepath& path) override; - // LBA-MSF変換 - void FASTCALL LBAtoMSF(DWORD lba, BYTE *msf) const; // LBA→MSF conversion - DWORD FASTCALL MSFtoLBA(const BYTE *msf) const; // MSF→LBA conversion + // Commands + int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command + int Read(const DWORD *cdb, BYTE *buf, uint64_t block) override; // READ command + int ReadToc(const DWORD *cdb, BYTE *buf); // READ TOC command private: // Open - BOOL FASTCALL OpenCue(const Filepath& path); // Open(CUE) - BOOL FASTCALL OpenIso(const Filepath& path); // Open(ISO) - BOOL FASTCALL OpenPhysical(const Filepath& path); // Open(Physical) - BOOL rawfile; // RAW flag + void OpenCue(const Filepath& path); // Open(CUE) + void OpenIso(const Filepath& path); // Open(ISO) + void OpenPhysical(const Filepath& path); // Open(Physical) + + void ReadToc(SASIDEV *); + void GetEventStatusNotification(SASIDEV *); + + void LBAtoMSF(DWORD lba, BYTE *msf) const; // LBA→MSF conversion + + bool rawfile; // RAW flag // Track management - void FASTCALL ClearTrack(); // Clear the track - int FASTCALL SearchTrack(DWORD lba) const; // Track search + void ClearTrack(); // Clear the track + int SearchTrack(DWORD lba) const; // Track search CDTrack* track[TrackMax]; // Track opbject references int tracks; // Effective number of track objects int dataindex; // Current data track int audioindex; // Current audio track int frame; // Frame number - - #if 0 - CDDABuf da_buf; // CD-DA buffer - int da_num; // Number of CD-DA tracks - int da_cur; // CD-DA current track - int da_next; // CD-DA next track - BOOL da_req; // CD-DA data request - #endif }; diff --git a/src/raspberrypi/devices/scsihd.cpp b/src/raspberrypi/devices/scsihd.cpp index 67e8bf68..31e1e3f0 100644 --- a/src/raspberrypi/devices/scsihd.cpp +++ b/src/raspberrypi/devices/scsihd.cpp @@ -14,8 +14,12 @@ // //--------------------------------------------------------------------------- #include "scsihd.h" -#include "xm6.h" #include "fileio.h" +#include "exceptions.h" +#include +#include "../rascsi.h" + +#define DEFAULT_PRODUCT "SCSI HD" //=========================================================================== // @@ -28,10 +32,40 @@ // Constructor // //--------------------------------------------------------------------------- -SCSIHD::SCSIHD() : Disk() +SCSIHD::SCSIHD(bool removable) : Disk(removable ? "SCRM" : "SCHD") { - // SCSI Hard Disk - disk.id = MAKEID('S', 'C', 'H', 'D'); +} + +void SCSIHD::FinalizeSetup(const Filepath &path, off_t size) +{ + // 2TB is the current maximum + if (size > 2LL * 1024 * 1024 * 1024 * 1024) { + throw io_exception("File size must not exceed 2 TB"); + } + + // For non-removable media drives set the default product name based on the drive capacity + if (!IsRemovable()) { + uint64_t capacity = GetBlockCount() * GetSectorSizeInBytes(); + string unit; + if (capacity >= 1000000) { + capacity /= 1000000; + unit = "MB"; + } + else { + capacity /= 1000; + unit = "KB"; + } + stringstream product; + product << DEFAULT_PRODUCT << " " << capacity << " " << unit; + SetProduct(product.str(), false); + } + + SetReadOnly(false); + SetProtectable(true); + SetProtected(false); + + Disk::Open(path); + FileSupport::SetPath(path); } //--------------------------------------------------------------------------- @@ -39,15 +73,15 @@ SCSIHD::SCSIHD() : Disk() // Reset // //--------------------------------------------------------------------------- -void FASTCALL SCSIHD::Reset() +void SCSIHD::Reset() { // Unlock and release attention - disk.lock = FALSE; - disk.attn = FALSE; + SetLocked(false); + SetAttn(false); // No reset, clear code - disk.reset = FALSE; - disk.code = 0x00; + SetReset(false); + SetStatusCode(STATUS_NOERROR); } //--------------------------------------------------------------------------- @@ -55,44 +89,28 @@ void FASTCALL SCSIHD::Reset() // Open // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSIHD::Open(const Filepath& path, BOOL /*attn*/) +void SCSIHD::Open(const Filepath& path) { + ASSERT(!IsReady()); + + // Open as read-only Fileio fio; - off64_t size; - - ASSERT(this); - ASSERT(!disk.ready); - - // read open required if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; + throw file_not_found_exception("Can't open SCSI hard disk file"); } // Get file size - size = fio.GetFileSize(); + off_t size = fio.GetFileSize(); fio.Close(); - // Must be 512 bytes - if (size & 0x1ff) { - return FALSE; - } + // Sector size (default 512 bytes) and number of blocks + SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512, false); + SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount())); - // 10MB or more - if (size < 0x9f5400) { - return FALSE; - } - // 2TB according to xm6i - // There is a similar one in wxw/wxw_cfg.cpp - if (size > 2LL * 1024 * 1024 * 1024 * 1024) { - return FALSE; - } + // Effective size must be a multiple of the sector size + size = (size / GetSectorSizeInBytes()) * GetSectorSizeInBytes(); - // sector size and number of blocks - disk.size = 9; - disk.blocks = (DWORD)(size >> 9); - - // Call base class - return Disk::Open(path); + FinalizeSetup(path, size); } //--------------------------------------------------------------------------- @@ -100,102 +118,58 @@ BOOL FASTCALL SCSIHD::Open(const Filepath& path, BOOL /*attn*/) // INQUIRY // //--------------------------------------------------------------------------- -int FASTCALL SCSIHD::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) +int SCSIHD::Inquiry(const DWORD *cdb, BYTE *buf) { - char vendor[32]; - char product[32]; - char rev[32]; - int size; - - ASSERT(this); ASSERT(cdb); ASSERT(buf); - ASSERT(cdb[0] == 0x12); // EVPD check if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return 0; } // Ready check (Error if no image file) - if (!disk.ready) { - disk.code = DISK_NOTREADY; + if (!IsReady()) { + SetStatusCode(STATUS_NOTREADY); return 0; } // Basic data // buf[0] ... Direct Access Device + // buf[1] ... Bit 7 set means removable // buf[2] ... SCSI-2 compliant command system // buf[3] ... SCSI-2 compliant Inquiry response // buf[4] ... Inquiry additional data memset(buf, 0, 8); - - // SCSI-2 p.104 4.4.3 Incorrect logical unit handling - if (((cdb[1] >> 5) & 0x07) != disk.lun) { - buf[0] = 0x7f; - } - + buf[1] = IsRemovable() ? 0x80 : 0x00; buf[2] = 0x02; buf[3] = 0x02; buf[4] = 122 + 3; // Value close to real HDD - // Fill with blanks - memset(&buf[8], 0x20, buf[4] - 3); - - // Determine vendor name/product name - sprintf(vendor, BENDER_SIGNATURE); - size = disk.blocks >> 11; - if (size < 300) - sprintf(product, "PRODRIVE LPS%dS", size); - else if (size < 600) - sprintf(product, "MAVERICK%dS", size); - else if (size < 800) - sprintf(product, "LIGHTNING%dS", size); - else if (size < 1000) - sprintf(product, "TRAILBRAZER%dS", size); - else if (size < 2000) - sprintf(product, "FIREBALL%dS", size); - else - sprintf(product, "FBSE%d.%dS", size / 1000, (size % 1000) / 100); - - // Vendor name - memcpy(&buf[8], vendor, strlen(vendor)); - - // Product name - memcpy(&buf[16], product, strlen(product)); - - // Revision - sprintf(rev, "0%01d%01d%01d", - (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); - memcpy(&buf[32], rev, 4); + // Padded vendor, product, revision + memcpy(&buf[8], GetPaddedName().c_str(), 28); // Size of data that can be returned - size = (buf[4] + 5); + int size = (buf[4] + 5); // Limit if the other buffer is small if (size > (int)cdb[4]) { size = (int)cdb[4]; } - // Success - disk.code = DISK_NOERROR; return size; } //--------------------------------------------------------------------------- // // MODE SELECT -// *Not affected by disk.code // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) +bool SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) { - BYTE page; int size; - ASSERT(this); ASSERT(buf); ASSERT(length >= 0); @@ -204,13 +178,13 @@ BOOL FASTCALL SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) // Mode Parameter header if (length >= 12) { // Check the block length bytes - size = 1 << disk.size; + size = 1 << GetSectorSizeShiftCount(); if (buf[9] != (BYTE)(size >> 16) || buf[10] != (BYTE)(size >> 8) || buf[11] != (BYTE)size) { // currently does not allow changing sector length - disk.code = DISK_INVALIDPRM; - return FALSE; + SetStatusCode(STATUS_INVALIDPRM); + return false; } buf += 12; length -= 12; @@ -219,29 +193,30 @@ BOOL FASTCALL SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) // Parsing the page while (length > 0) { // Get page - page = buf[0]; + BYTE page = buf[0]; switch (page) { // format device case 0x03: // check the number of bytes in the physical sector - size = 1 << disk.size; + size = 1 << GetSectorSizeShiftCount(); if (buf[0xc] != (BYTE)(size >> 8) || buf[0xd] != (BYTE)size) { // currently does not allow changing sector length - disk.code = DISK_INVALIDPRM; - return FALSE; + SetStatusCode(STATUS_INVALIDPRM); + return false; } break; // CD-ROM Parameters + // TODO Move to scsicd.cpp // According to the SONY CDU-541 manual, Page code 8 is supposed // to set the Logical Block Adress Format, as well as the // inactivity timer multiplier case 0x08: // Debug code for Issue #2: // https://github.com/akuker/RASCSI/issues/2 - printf("[Unhandled page code] Received mode page code 8 with total length %d\n ", length); + LOGWARN("[Unhandled page code] Received mode page code 8 with total length %d\n ", length); for (int i = 0; i= (off64_t)sizeof(hdr)) { - if (!fio.Read(hdr, sizeof(hdr))) { + // NEC root sector + BYTE root_sector[512]; + if (size >= (off_t)sizeof(root_sector)) { + if (!fio.Read(root_sector, sizeof(root_sector))) { fio.Close(); - return FALSE; + throw io_exception("Can't read NEC hard disk file root sector"); } } fio.Close(); - // 512バイト単位であること - if (size & 0x1ff) { - return FALSE; - } + // Effective size must be a multiple of 512 + size = (size / 512) * 512; - // 10MB以上 - if (size < 0x9f5400) { - return FALSE; - } - // xm6iに準じて2TB - // よく似たものが wxw/wxw_cfg.cpp にもある - if (size > 2LL * 1024 * 1024 * 1024 * 1024) { - return FALSE; - } + int image_size = 0; + int sector_size = 0; - // 拡張子別にパラメータを決定 - ext = path.GetFileExt(); - if (xstrcasecmp(ext, _T(".HDN")) == 0) { - // デフォルト設定としてセクタサイズ512,セクタ数25,ヘッド数8を想定 - imgoffset = 0; - imgsize = size; - sectorsize = 512; + // Determine parameters by extension + const char *ext = path.GetFileExt(); + + // PC-9801-55 NEC genuine? + if (!strcasecmp(ext, ".hdn")) { + // Assuming sector size 512, number of sectors 25, number of heads 8 as default settings + disk.image_offset = 0; + image_size = size; + sector_size = 512; sectors = 25; heads = 8; cylinders = (int)(size >> 9); cylinders >>= 3; cylinders /= 25; - } else if (xstrcasecmp(ext, _T(".HDI")) == 0) { // Anex86 HD image? - imgoffset = getDwordLE(&hdr[4 + 4]); - imgsize = getDwordLE(&hdr[4 + 4 + 4]); - sectorsize = getDwordLE(&hdr[4 + 4 + 4 + 4]); - sectors = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4]); - heads = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4 + 4]); - cylinders = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4 + 4 + 4]); - } else if (xstrcasecmp(ext, _T(".NHD")) == 0 && - memcmp(hdr, "T98HDDIMAGE.R0\0", 15) == 0) { // T98Next HD image? - imgoffset = getDwordLE(&hdr[0x10 + 0x100]); - cylinders = getDwordLE(&hdr[0x10 + 0x100 + 4]); - heads = getWordLE(&hdr[0x10 + 0x100 + 4 + 4]); - sectors = getWordLE(&hdr[0x10 + 0x100 + 4 + 4 + 2]); - sectorsize = getWordLE(&hdr[0x10 + 0x100 + 4 + 4 + 2 + 2]); - imgsize = (off64_t)cylinders * heads * sectors * sectorsize; + } + // Anex86 HD image? + else if (!strcasecmp(ext, ".hdi")) { + disk.image_offset = getDwordLE(&root_sector[8]); + image_size = getDwordLE(&root_sector[12]); + sector_size = getDwordLE(&root_sector[16]); + sectors = getDwordLE(&root_sector[20]); + heads = getDwordLE(&root_sector[24]); + cylinders = getDwordLE(&root_sector[28]); + } + // T98Next HD image? + else if (!strcasecmp(ext, ".nhd")) { + if (!memcmp(root_sector, "T98HDDIMAGE.R0\0", 15)) { + disk.image_offset = getDwordLE(&root_sector[0x110]); + cylinders = getDwordLE(&root_sector[0x114]); + heads = getWordLE(&root_sector[0x118]); + sectors = getWordLE(&root_sector[0x11a]); + sector_size = getWordLE(&root_sector[0x11c]); + image_size = (off_t)cylinders * heads * sectors * sector_size; + } + else { + throw io_exception("Invalid NEC image file format"); + } } - // セクタサイズは256または512をサポート - if (sectorsize != 256 && sectorsize != 512) { - return FALSE; + // Image size consistency check + if (disk.image_offset + image_size > size || (image_size % sector_size != 0)) { + throw io_exception("Image size consistency check failed"); } - // イメージサイズの整合性チェック - if (imgoffset + imgsize > size || (imgsize % sectorsize != 0)) { - return FALSE; - } - - // セクタサイズ - for(disk.size = 16; disk.size > 0; --(disk.size)) { - if ((1 << disk.size) == sectorsize) + // Calculate sector size + for (size = 16; size > 0; --size) { + if ((1 << size) == sector_size) break; } - if (disk.size <= 0 || disk.size > 16) { - return FALSE; + if (size <= 0 || size > 16) { + throw io_exception("Invalid NEC disk size"); } + SetSectorSizeShiftCount(size); - // ブロック数 - disk.blocks = (DWORD)(imgsize >> disk.size); - disk.imgoffset = imgoffset; + // Number of blocks + SetBlockCount(image_size >> disk.size); - // Call the base class - return Disk::Open(path); + FinalizeSetup(path, size); } -//--------------------------------------------------------------------------- -// -// INQUIRY -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD_NEC::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) +int SCSIHD_NEC::Inquiry(const DWORD *cdb, BYTE *buf) { - int size; + int size = SCSIHD::Inquiry(cdb, buf); - // 基底クラス - size = SCSIHD::Inquiry(cdb, buf, major, minor); - - // 基底クラスでエラーなら終了 - if (size == 0) { - return 0; - } - - // SCSI1相当に変更 + // This drive is a SCSI-1 SCCS drive buf[2] = 0x01; buf[3] = 0x01; - // Replace Vendor name - buf[8] = 'N'; - buf[9] = 'E'; - buf[10] = 'C'; - return size; } -//--------------------------------------------------------------------------- -// -// エラーページ追加 -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD_NEC::AddError(BOOL change, BYTE *buf) +int SCSIHD_NEC::AddErrorPage(bool change, BYTE *buf) { - ASSERT(this); ASSERT(buf); // Set the message length buf[0] = 0x01; buf[1] = 0x06; - // No changeable area - if (change) { - return 8; - } - - // リトライカウントは0、リミットタイムは装置内部のデフォルト値を使用 + // The retry count is 0, and the limit time uses the default value inside the device. return 8; } -//--------------------------------------------------------------------------- -// -// フォーマットページ追加 -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD_NEC::AddFormat(BOOL change, BYTE *buf) +int SCSIHD_NEC::AddFormatPage(bool change, BYTE *buf) { - int size; - - ASSERT(this); ASSERT(buf); // Set the message length buf[0] = 0x80 | 0x03; buf[1] = 0x16; - // 物理セクタのバイト数は変更可能に見せる(実際には変更できないが) + // Make the number of bytes in the physical sector appear mutable (although it cannot actually be) if (change) { buf[0xc] = 0xff; buf[0xd] = 0xff; return 24; } - if (disk.ready) { - // 1ゾーンのトラック数を設定(PC-9801-55はこの値を見ているようだ) + if (IsReady()) { + // Set the number of tracks in one zone (PC-9801-55 seems to see this value) buf[0x2] = (BYTE)(heads >> 8); buf[0x3] = (BYTE)heads; - // 1トラックのセクタ数を設定 + // Set the number of sectors per track buf[0xa] = (BYTE)(sectors >> 8); buf[0xb] = (BYTE)sectors; - // 物理セクタのバイト数を設定 - size = 1 << disk.size; + // Set the number of bytes in the physical sector + int size = 1 << disk.size; buf[0xc] = (BYTE)(size >> 8); buf[0xd] = (BYTE)size; } - // リムーバブル属性を設定(昔の名残) - if (disk.removable) { + // Set removable attributes (remains of the old days) + if (IsRemovable()) { buf[20] = 0x20; } return 24; } -//--------------------------------------------------------------------------- -// -// ドライブページ追加 -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD_NEC::AddDrive(BOOL change, BYTE *buf) +int SCSIHD_NEC::AddDrivePage(bool change, BYTE *buf) { - ASSERT(this); ASSERT(buf); // Set the message length @@ -277,17 +205,13 @@ int FASTCALL SCSIHD_NEC::AddDrive(BOOL change, BYTE *buf) buf[1] = 0x12; // No changeable area - if (change) { - return 20; - } - - if (disk.ready) { - // シリンダ数を設定 + if (!change && IsReady()) { + // Set the number of cylinders buf[0x2] = (BYTE)(cylinders >> 16); buf[0x3] = (BYTE)(cylinders >> 8); buf[0x4] = (BYTE)cylinders; - // ヘッド数を設定 + // Set the number of heads buf[0x5] = (BYTE)heads; } diff --git a/src/raspberrypi/devices/scsihd_nec.h b/src/raspberrypi/devices/scsihd_nec.h index 01518957..82e2baa7 100644 --- a/src/raspberrypi/devices/scsihd_nec.h +++ b/src/raspberrypi/devices/scsihd_nec.h @@ -19,29 +19,27 @@ //=========================================================================== // -// SCSI hard disk (PC-9801-55 NEC genuine /Anex86/T98Next) +// SCSI hard disk (PC-9801-55 NEC genuine / Anex86 / T98Next) // //=========================================================================== class SCSIHD_NEC : public SCSIHD { public: - // Basic Functions - SCSIHD_NEC(); // Constructor - BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); // Open + SCSIHD_NEC(); + ~SCSIHD_NEC() {}; - // commands - int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command + void Open(const Filepath& path) override; - // Internal processing - int FASTCALL AddError(BOOL change, BYTE *buf); // Add error - int FASTCALL AddFormat(BOOL change, BYTE *buf); // Add format - int FASTCALL AddDrive(BOOL change, BYTE *buf); // Add drive + // Commands + int Inquiry(const DWORD *cdb, BYTE *buf) override; + + int AddErrorPage(bool change, BYTE *buf) override; + int AddFormatPage(bool change, BYTE *buf) override; + int AddDrivePage(bool change, BYTE *buf) override; private: - int cylinders; // Number of cylinders - int heads; // Number of heads - int sectors; // Number of sectors - int sectorsize; // Sector size - off64_t imgoffset; // Image offset - off64_t imgsize; // Image size -}; \ No newline at end of file + // Geometry data + int cylinders; + int heads; + int sectors; +}; diff --git a/src/raspberrypi/devices/scsimo.cpp b/src/raspberrypi/devices/scsimo.cpp index c6849d65..b75f4f67 100644 --- a/src/raspberrypi/devices/scsimo.cpp +++ b/src/raspberrypi/devices/scsimo.cpp @@ -15,8 +15,11 @@ //--------------------------------------------------------------------------- #include "scsimo.h" -#include "xm6.h" + +#include "../rascsi.h" #include "fileio.h" +#include "exceptions.h" +#include //=========================================================================== // @@ -29,13 +32,8 @@ // Constructor // //--------------------------------------------------------------------------- -SCSIMO::SCSIMO() : Disk() +SCSIMO::SCSIMO() : Disk("SCMO") { - // SCSI magneto-optical disk - disk.id = MAKEID('S', 'C', 'M', 'O'); - - // Set as removable - disk.removable = TRUE; } //--------------------------------------------------------------------------- @@ -43,167 +41,61 @@ SCSIMO::SCSIMO() : Disk() // Open // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSIMO::Open(const Filepath& path, BOOL attn) +void SCSIMO::Open(const Filepath& path) { - Fileio fio; - off64_t size; - - ASSERT(this); - ASSERT(!disk.ready); + ASSERT(!IsReady()); // Open as read-only + Fileio fio; + if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; + throw file_not_found_exception("Can't open MO file"); } // Get file size - size = fio.GetFileSize(); + off_t size = fio.GetFileSize(); fio.Close(); - switch (size) { - // 128MB - case 0x797f400: - disk.size = 9; - disk.blocks = 248826; - break; - - // 230MB - case 0xd9eea00: - disk.size = 9; - disk.blocks = 446325; - break; - - // 540MB - case 0x1fc8b800: - disk.size = 9; - disk.blocks = 1041500; - break; - - // 640MB - case 0x25e28000: - disk.size = 11; - disk.blocks = 310352; - break; - - // Other (this is an error) - default: - return FALSE; + // For some priorities there are hard-coded, well-defined sector sizes and block counts + if (!SetGeometryForCapacity(size)) { + // Sector size (default 512 bytes) and number of blocks + SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512, true); + SetBlockCount(size >> GetSectorSizeShiftCount()); } - // Call the base class + // Effective size must be a multiple of the sector size + size = (size / GetSectorSizeInBytes()) * GetSectorSizeInBytes(); + + SetReadOnly(false); + SetProtectable(true); + SetProtected(false); + Disk::Open(path); + FileSupport::SetPath(path); // Attention if ready - if (disk.ready && attn) { - disk.attn = TRUE; + if (IsReady()) { + SetAttn(true); } - - return TRUE; } -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Load -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSIMO::Load(Fileio *fio, int ver) -{ - DWORD sz; - disk_t buf; - DWORD padding; - Filepath path; - - ASSERT(this); - ASSERT(fio); - ASSERT(ver >= 0x0200); - - // Prior to version 2.03, the disk was not saved - if (ver <= 0x0202) { - return TRUE; - } - - // load size, match - if (!fio->Read(&sz, sizeof(sz))) { - return FALSE; - } - if (sz != 52) { - return FALSE; - } - - // load into buffer - PROP_IMPORT(fio, buf.id); - PROP_IMPORT(fio, buf.ready); - PROP_IMPORT(fio, buf.writep); - PROP_IMPORT(fio, buf.readonly); - PROP_IMPORT(fio, buf.removable); - PROP_IMPORT(fio, buf.lock); - PROP_IMPORT(fio, buf.attn); - PROP_IMPORT(fio, buf.reset); - PROP_IMPORT(fio, buf.size); - PROP_IMPORT(fio, buf.blocks); - PROP_IMPORT(fio, buf.lun); - PROP_IMPORT(fio, buf.code); - PROP_IMPORT(fio, padding); - - // Load path - if (!path.Load(fio, ver)) { - return FALSE; - } - - // Always eject - Eject(TRUE); - - // Move only if IDs match - if (disk.id != buf.id) { - // Not MO at the time of save. Maintain eject status - return TRUE; - } - - // Re-try opening - if (!Open(path, FALSE)) { - // Cannot reopen. Maintain eject status - return TRUE; - } - - // Disk cache is created in Open. Move property only - if (!disk.readonly) { - disk.writep = buf.writep; - } - disk.lock = buf.lock; - disk.attn = buf.attn; - disk.reset = buf.reset; - disk.lun = buf.lun; - disk.code = buf.code; - - // loaded successfully - return TRUE; -} -#endif // RASCSI - //--------------------------------------------------------------------------- // // INQUIRY // //--------------------------------------------------------------------------- -int FASTCALL SCSIMO::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) +int SCSIMO::Inquiry(const DWORD *cdb, BYTE *buf) { - int size; - char rev[32]; - - ASSERT(this); ASSERT(cdb); ASSERT(buf); - ASSERT(cdb[0] == 0x12); // EVPD check if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return FALSE; } - // 基本データ + // Basic data // buf[0] ... Optical Memory Device // buf[1] ... Removable // buf[2] ... SCSI-2 compliant command system @@ -211,56 +103,34 @@ int FASTCALL SCSIMO::Inquiry( // buf[4] ... Inquiry additional data memset(buf, 0, 8); buf[0] = 0x07; - - // SCSI-2 p.104 4.4.3 Incorrect logical unit handling - if (((cdb[1] >> 5) & 0x07) != disk.lun) { - buf[0] = 0x7f; - } - buf[1] = 0x80; buf[2] = 0x02; buf[3] = 0x02; buf[4] = 36 - 5; // required - // Fill with blanks - memset(&buf[8], 0x20, buf[4] - 3); - - // Vendor name - memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); - - // Product name - memcpy(&buf[16], "M2513A", 6); - - // Revision (XM6 version number) - sprintf(rev, "0%01d%01d%01d", - (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); - memcpy(&buf[32], rev, 4); + // Padded vendor, product, revision + memcpy(&buf[8], GetPaddedName().c_str(), 28); // Size return data - size = (buf[4] + 5); + int size = (buf[4] + 5); // Limit the size if the buffer is too small if (size > (int)cdb[4]) { size = (int)cdb[4]; } - // Success - disk.code = DISK_NOERROR; return size; } //--------------------------------------------------------------------------- // // MODE SELECT -// *Not affected by disk.code // //--------------------------------------------------------------------------- -BOOL FASTCALL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) +bool SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) { - int page; int size; - ASSERT(this); ASSERT(buf); ASSERT(length >= 0); @@ -269,12 +139,12 @@ BOOL FASTCALL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) // Mode Parameter header if (length >= 12) { // Check the block length (in bytes) - size = 1 << disk.size; + size = 1 << GetSectorSizeShiftCount(); if (buf[9] != (BYTE)(size >> 16) || buf[10] != (BYTE)(size >> 8) || buf[11] != (BYTE)size) { // Currently does not allow changing sector length - disk.code = DISK_INVALIDPRM; - return FALSE; + SetStatusCode(STATUS_INVALIDPRM); + return false; } buf += 12; length -= 12; @@ -283,18 +153,18 @@ BOOL FASTCALL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) // Parsing the page while (length > 0) { // Get the page - page = buf[0]; + int page = buf[0]; switch (page) { // format device case 0x03: // Check the number of bytes in the physical sector - size = 1 << disk.size; + size = 1 << GetSectorSizeShiftCount(); if (buf[0xc] != (BYTE)(size >> 8) || buf[0xd] != (BYTE)size) { // Currently does not allow changing sector length - disk.code = DISK_INVALIDPRM; - return FALSE; + SetStatusCode(STATUS_INVALIDPRM); + return false; } break; // vendor unique format @@ -315,9 +185,7 @@ BOOL FASTCALL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) } // Do not generate an error for the time being (MINIX) - disk.code = DISK_NOERROR; - - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -325,9 +193,8 @@ BOOL FASTCALL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) // Vendor Unique Format Page 20h (MO) // //--------------------------------------------------------------------------- -int FASTCALL SCSIMO::AddVendor(int page, BOOL change, BYTE *buf) +int SCSIMO::AddVendor(int page, BOOL change, BYTE *buf) { - ASSERT(this); ASSERT(buf); // Page code 20h @@ -367,50 +234,55 @@ int FASTCALL SCSIMO::AddVendor(int page, BOOL change, BYTE *buf) further information: http://r2089.blog36.fc2.com/blog-entry-177.html */ - if (disk.ready) { + if (IsReady()) { unsigned spare = 0; unsigned bands = 0; + uint64_t blocks = GetBlockCount(); - if (disk.size == 9) switch (disk.blocks) { - // 128MB - case 248826: - spare = 1024; - bands = 1; - break; + if (GetSectorSizeInBytes() == 512) { + switch (blocks) { + // 128MB + case 248826: + spare = 1024; + bands = 1; + break; - // 230MB - case 446325: - spare = 1025; - bands = 10; - break; + // 230MB + case 446325: + spare = 1025; + bands = 10; + break; - // 540MB - case 1041500: - spare = 2250; - bands = 18; - break; + // 540MB + case 1041500: + spare = 2250; + bands = 18; + break; + } } - if (disk.size == 11) switch (disk.blocks) { - // 640MB - case 310352: - spare = 2244; - bands = 11; - break; + if (GetSectorSizeInBytes() == 2048) { + switch (blocks) { + // 640MB + case 310352: + spare = 2244; + bands = 11; + break; - // 1.3GB (lpproj: not tested with real device) - case 605846: - spare = 4437; - bands = 18; - break; + // 1.3GB (lpproj: not tested with real device) + case 605846: + spare = 4437; + bands = 18; + break; + } } buf[2] = 0; // format mode buf[3] = 0; // type of format - buf[4] = (BYTE)(disk.blocks >> 24); - buf[5] = (BYTE)(disk.blocks >> 16); - buf[6] = (BYTE)(disk.blocks >> 8); - buf[7] = (BYTE)disk.blocks; + buf[4] = (BYTE)(blocks >> 24); + buf[5] = (BYTE)(blocks >> 16); + buf[6] = (BYTE)(blocks >> 8); + buf[7] = (BYTE)blocks; buf[8] = (BYTE)(spare >> 8); buf[9] = (BYTE)spare; buf[10] = (BYTE)(bands >> 8); diff --git a/src/raspberrypi/devices/scsimo.h b/src/raspberrypi/devices/scsimo.h index fdf8629a..5530bad7 100644 --- a/src/raspberrypi/devices/scsimo.h +++ b/src/raspberrypi/devices/scsimo.h @@ -19,25 +19,18 @@ #include "disk.h" #include "filepath.h" -//=========================================================================== -// -// SCSI magneto-optical disk -// -//=========================================================================== -class SCSIMO : public Disk +class SCSIMO : public Disk, public FileSupport { public: - // Basic Functions - SCSIMO(); // Constructor - BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); // Open - #ifndef RASCSI - BOOL FASTCALL Load(Fileio *fio, int ver); // Load - #endif // RASCSI + SCSIMO(); + ~SCSIMO() {}; - // commands - int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command - BOOL FASTCALL ModeSelect(const DWORD *cdb, const BYTE *buf, int length); // MODE SELECT(6) command + void Open(const Filepath& path); + + // Commands + int Inquiry(const DWORD *cdb, BYTE *buf) override; + bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override; // Internal processing - int FASTCALL AddVendor(int page, BOOL change, BYTE *buf); // Add vendor special page -}; \ No newline at end of file + int AddVendor(int page, BOOL change, BYTE *buf); // Add vendor special page +}; diff --git a/src/raspberrypi/exceptions.h b/src/raspberrypi/exceptions.h new file mode 100644 index 00000000..e9c7e431 --- /dev/null +++ b/src/raspberrypi/exceptions.h @@ -0,0 +1,49 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +// Various exceptions +// +//--------------------------------------------------------------------------- + +#pragma once + +#include +#include + +using namespace std; + +class illegal_argument_exception final : public exception { +private: + string msg; + +public: + illegal_argument_exception(const string& _msg) : msg(_msg) {} + illegal_argument_exception() {}; + + const string& getmsg() const { + return msg; + } +}; + +class io_exception : public exception { +private: + string msg; + +public: + io_exception(const string& _msg) : msg(_msg) {} + virtual ~io_exception() {} + + const string& getmsg() const { + return msg; + } +}; + +class file_not_found_exception : public io_exception { +public: + file_not_found_exception(const string& msg) : io_exception(msg) {} + ~file_not_found_exception() {} +}; diff --git a/src/raspberrypi/fileio.cpp b/src/raspberrypi/fileio.cpp index f5e03e2d..066abddd 100644 --- a/src/raspberrypi/fileio.cpp +++ b/src/raspberrypi/fileio.cpp @@ -4,159 +4,114 @@ // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2010-2020 GIMONS -// [ ファイルI/O(RaSCSI用サブセット) ] +// [ File I/O (Subset for RaSCSI) ] // //--------------------------------------------------------------------------- #include "os.h" -#include "xm6.h" #include "filepath.h" #include "fileio.h" +#include "rascsi.h" //=========================================================================== // -// ファイルI/O +// File I/O // //=========================================================================== -#ifndef BAREMETAL -//--------------------------------------------------------------------------- -// -// コンストラクタ -// -//--------------------------------------------------------------------------- Fileio::Fileio() { - // ワーク初期化 + // Initialize work handle = -1; } -//--------------------------------------------------------------------------- -// -// デストラクタ -// -//--------------------------------------------------------------------------- Fileio::~Fileio() { ASSERT(handle == -1); - // Releaseでの安全策 + // Safety measure for Release Close(); } -//--------------------------------------------------------------------------- -// -// ロード -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Load(const Filepath& path, void *buffer, int size) +BOOL Fileio::Load(const Filepath& path, void *buffer, int size) { - ASSERT(this); ASSERT(buffer); ASSERT(size > 0); ASSERT(handle < 0); - // オープン if (!Open(path, ReadOnly)) { return FALSE; } - // 読み込み if (!Read(buffer, size)) { Close(); return FALSE; } - // クローズ Close(); return TRUE; } -//--------------------------------------------------------------------------- -// -// セーブ -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Save(const Filepath& path, void *buffer, int size) +BOOL Fileio::Save(const Filepath& path, void *buffer, int size) { - ASSERT(this); ASSERT(buffer); ASSERT(size > 0); ASSERT(handle < 0); - // オープン if (!Open(path, WriteOnly)) { return FALSE; } - // 書き込み if (!Write(buffer, size)) { Close(); return FALSE; } - // クローズ Close(); return TRUE; } -//--------------------------------------------------------------------------- -// -// オープン -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO) +BOOL Fileio::Open(const char *fname, OpenMode mode, BOOL directIO) { mode_t omode; - ASSERT(this); ASSERT(fname); ASSERT(handle < 0); - // ヌル文字列からの読み込みは必ず失敗させる + // Always fail a read from a null array if (fname[0] == _T('\0')) { handle = -1; return FALSE; } - // デフォルトモード + // Default mode omode = directIO ? O_DIRECT : 0; - // モード別 switch (mode) { - // 読み込みのみ case ReadOnly: handle = open(fname, O_RDONLY | omode); break; - // 書き込みのみ case WriteOnly: handle = open(fname, O_CREAT | O_WRONLY | O_TRUNC | omode, 0666); break; - // 読み書き両方 case ReadWrite: - // CD-ROMからの読み込みはRWが成功してしまう + // Make sure RW does not succeed when reading from CD-ROM if (access(fname, 0x06) != 0) { return FALSE; } handle = open(fname, O_RDWR | omode); break; - // アペンド - case Append: - handle = open(fname, O_CREAT | O_WRONLY | O_APPEND | omode, 0666); - break; - - // それ以外 default: ASSERT(FALSE); break; } - // 結果評価 + // Evaluate results if (handle == -1) { return FALSE; } @@ -165,75 +120,44 @@ BOOL FASTCALL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO) return TRUE; } -//--------------------------------------------------------------------------- -// -// オープン -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Open(LPCTSTR fname, OpenMode mode) +BOOL Fileio::Open(const char *fname, OpenMode mode) { - ASSERT(this); return Open(fname, mode, FALSE); } -//--------------------------------------------------------------------------- -// -// オープン -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Open(const Filepath& path, OpenMode mode) +BOOL Fileio::Open(const Filepath& path, OpenMode mode) { - ASSERT(this); return Open(path.GetPath(), mode); } -//--------------------------------------------------------------------------- -// -// オープン -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::OpenDIO(LPCTSTR fname, OpenMode mode) +BOOL Fileio::OpenDIO(const char *fname, OpenMode mode) { - ASSERT(this); - // O_DIRECT付きでオープン + // Open with included O_DIRECT if (!Open(fname, mode, TRUE)) { - // 通常モードリトライ(tmpfs等) + // Normal mode retry (tmpfs etc.) return Open(fname, mode, FALSE); } return TRUE; } -//--------------------------------------------------------------------------- -// -// オープン -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::OpenDIO(const Filepath& path, OpenMode mode) +BOOL Fileio::OpenDIO(const Filepath& path, OpenMode mode) { - ASSERT(this); return OpenDIO(path.GetPath(), mode); } -//--------------------------------------------------------------------------- -// -// 読み込み -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Read(void *buffer, int size) +BOOL Fileio::Read(void *buffer, int size) { int count; - ASSERT(this); ASSERT(buffer); ASSERT(size > 0); ASSERT(handle >= 0); - // 読み込み count = read(handle, buffer, size); if (count != size) { return FALSE; @@ -242,21 +166,14 @@ BOOL FASTCALL Fileio::Read(void *buffer, int size) return TRUE; } -//--------------------------------------------------------------------------- -// -// 書き込み -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Write(const void *buffer, int size) +BOOL Fileio::Write(const void *buffer, int size) { int count; - ASSERT(this); ASSERT(buffer); ASSERT(size > 0); ASSERT(handle >= 0); - // 書き込み count = write(handle, buffer, size); if (count != size) { return FALSE; @@ -265,18 +182,12 @@ BOOL FASTCALL Fileio::Write(const void *buffer, int size) return TRUE; } -//--------------------------------------------------------------------------- -// -// シーク -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Seek(off64_t offset, BOOL relative) +BOOL Fileio::Seek(off_t offset, BOOL relative) { - ASSERT(this); ASSERT(handle >= 0); ASSERT(offset >= 0); - // 相対シークならオフセットに現在値を追加 + // Add current position in case of relative seek if (relative) { offset += GetFilePos(); } @@ -288,330 +199,42 @@ BOOL FASTCALL Fileio::Seek(off64_t offset, BOOL relative) return TRUE; } -//--------------------------------------------------------------------------- -// -// ファイルサイズ取得 -// -//--------------------------------------------------------------------------- -off64_t FASTCALL Fileio::GetFileSize() +off_t Fileio::GetFileSize() { - off64_t cur; - off64_t end; + off_t cur; + off_t end; - ASSERT(this); ASSERT(handle >= 0); - // ファイル位置を64bitで取得 + // Get file position in 64bit cur = GetFilePos(); - // ファイルサイズを64bitで取得 + // Get file size in64bitで end = lseek(handle, 0, SEEK_END); - // 位置を元に戻す + // Return to start position Seek(cur); return end; } -//--------------------------------------------------------------------------- -// -// ファイル位置取得 -// -//--------------------------------------------------------------------------- -off64_t FASTCALL Fileio::GetFilePos() const +off_t Fileio::GetFilePos() const { - off64_t pos; + off_t pos; - ASSERT(this); ASSERT(handle >= 0); - // ファイル位置を64bitで取得 + // Get file position in 64bit pos = lseek(handle, 0, SEEK_CUR); return pos; } -//--------------------------------------------------------------------------- -// -// クローズ -// -//--------------------------------------------------------------------------- -void FASTCALL Fileio::Close() +void Fileio::Close() { - ASSERT(this); if (handle != -1) { close(handle); handle = -1; } } -#else -//--------------------------------------------------------------------------- -// -// コンストラクタ -// -//--------------------------------------------------------------------------- -Fileio::Fileio() -{ - // ワーク初期化 - handle.obj.fs = 0; -} - -//--------------------------------------------------------------------------- -// -// デストラクタ -// -//--------------------------------------------------------------------------- -Fileio::~Fileio() -{ - ASSERT(!handle.obj.fs); - - // Releaseでの安全策 - Close(); -} - -//--------------------------------------------------------------------------- -// -// ロード -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Load(const Filepath& path, void *buffer, int size) -{ - ASSERT(this); - ASSERT(buffer); - ASSERT(size > 0); - ASSERT(!handle.obj.fs); - - // オープン - if (!Open(path, ReadOnly)) { - return FALSE; - } - - // 読み込み - if (!Read(buffer, size)) { - Close(); - return FALSE; - } - - // クローズ - Close(); - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// セーブ -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Save(const Filepath& path, void *buffer, int size) -{ - ASSERT(this); - ASSERT(buffer); - ASSERT(size > 0); - ASSERT(!handle.obj.fs); - - // オープン - if (!Open(path, WriteOnly)) { - return FALSE; - } - - // 書き込み - if (!Write(buffer, size)) { - Close(); - return FALSE; - } - - // クローズ - Close(); - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// オープン -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Open(LPCTSTR fname, OpenMode mode) -{ - FRESULT fr; - Filepath fpath; - ASSERT(this); - ASSERT(fname); - ASSERT(!handle.obj.fs); - - // ヌル文字列からの読み込みは必ず失敗させる - if (fname[0] == _T('\0')) { - return FALSE; - } - - // モード別 - switch (mode) { - // 読み込みのみ - case ReadOnly: - fr = f_open(&handle, fname, FA_READ); - break; - - // 書き込みのみ - case WriteOnly: - fr = f_open(&handle, fname, FA_CREATE_ALWAYS | FA_WRITE); - break; - - // 読み書き両方 - case ReadWrite: - fr = f_open(&handle, fname, FA_READ | FA_WRITE); - break; - - // アペンド - case Append: - fr = f_open(&handle, fname, FA_OPEN_APPEND | FA_WRITE); - break; - - // それ以外 - default: - fr = FR_NO_PATH; - ASSERT(FALSE); - break; - } - - // 結果評価 - if (fr != FR_OK) { - return FALSE; - } - - // オープン成功 - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// オープン -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Open(const Filepath& path, OpenMode mode) -{ - ASSERT(this); - ASSERT(!handle.obj.fs); - - return Open(path.GetPath(), mode); -} - -//--------------------------------------------------------------------------- -// -// 読み込み -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Read(void *buffer, int size) -{ - FRESULT fr; - UINT count; - - ASSERT(this); - ASSERT(buffer); - ASSERT(size > 0); - ASSERT(handle.obj.fs); - - // 読み込み - fr = f_read(&handle, buffer, size, &count); - if (fr != FR_OK || count != (unsigned int)size) { - return FALSE; - } - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// 書き込み -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Write(const void *buffer, int size) -{ - FRESULT fr; - UINT count; - - ASSERT(this); - ASSERT(buffer); - ASSERT(size > 0); - ASSERT(handle.obj.fs); - - // 書き込み - fr = f_write(&handle, buffer, size, &count); - if (fr != FR_OK || count != (unsigned int)size) { - return FALSE; - } - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// シーク -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Fileio::Seek(off64_t offset, BOOL relative) -{ - FRESULT fr; - - ASSERT(this); - ASSERT(offset >= 0); - ASSERT(handle.obj.fs); - - // 相対シークならオフセットに現在値を追加 - if (relative) { - offset += f_tell(&handle); - } - - fr = f_lseek(&handle, offset); - if (fr != FR_OK) { - return FALSE; - } - - if (f_tell(&handle) != (DWORD)offset) { - return FALSE; - } - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// ファイルサイズ取得 -// -//--------------------------------------------------------------------------- -off64_t FASTCALL Fileio::GetFileSize() -{ - ASSERT(this); - ASSERT(handle.obj.fs); - - return f_size(&handle); -} - -//--------------------------------------------------------------------------- -// -// ファイル位置取得 -// -//--------------------------------------------------------------------------- -off64_t FASTCALL Fileio::GetFilePos() const -{ - ASSERT(this); - ASSERT(handle.obj.fs); - - return f_tell(&handle); -} - -//--------------------------------------------------------------------------- -// -// クローズ -// -//--------------------------------------------------------------------------- -void FASTCALL Fileio::Close() -{ - ASSERT(this); - - if (handle.obj.fs) { - f_close(&handle); - } -} -#endif //BAREMETAL \ No newline at end of file diff --git a/src/raspberrypi/fileio.h b/src/raspberrypi/fileio.h index da574712..f0815d08 100644 --- a/src/raspberrypi/fileio.h +++ b/src/raspberrypi/fileio.h @@ -4,7 +4,7 @@ // // Copyright (C) 2001-2005 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2013-2020 GIMONS -// [ ファイルI/O(RaSCSI用サブセット) ] +// [ File I/O (Subset for RaSCSI) ] // //--------------------------------------------------------------------------- @@ -13,13 +13,9 @@ #include "filepath.h" -#ifdef BAREMETAL -#include "ff.h" -#endif // BAREMETAL - //=========================================================================== // -// マクロ(Load,Save用) +// Macros (for Load, Save) // //=========================================================================== #define PROP_IMPORT(f, p) \ @@ -34,67 +30,40 @@ //=========================================================================== // -// ファイルI/O +// File I/O // //=========================================================================== class Fileio { public: enum OpenMode { - ReadOnly, // 読み込みのみ - WriteOnly, // 書き込みのみ - ReadWrite, // 読み書き両方 - Append // アペンド + ReadOnly, + WriteOnly, + ReadWrite }; public: Fileio(); - // コンストラクタ virtual ~Fileio(); - // デストラクタ - BOOL FASTCALL Load(const Filepath& path, void *buffer, int size); - // ROM,RAMロード - BOOL FASTCALL Save(const Filepath& path, void *buffer, int size); - // RAMセーブ + BOOL Load(const Filepath& path, void *buffer, int size); // Load ROM, RAM + BOOL Save(const Filepath& path, void *buffer, int size); // Save RAM - BOOL FASTCALL Open(LPCTSTR fname, OpenMode mode); - // オープン - BOOL FASTCALL Open(const Filepath& path, OpenMode mode); - // オープン -#ifndef BAREMETAL - BOOL FASTCALL OpenDIO(LPCTSTR fname, OpenMode mode); - // オープン - BOOL FASTCALL OpenDIO(const Filepath& path, OpenMode mode); - // オープン -#endif // BAREMETAL - BOOL FASTCALL Seek(off64_t offset, BOOL relative = FALSE); - // シーク - BOOL FASTCALL Read(void *buffer, int size); - // 読み込み - BOOL FASTCALL Write(const void *buffer, int size); - // 書き込み - off64_t FASTCALL GetFileSize(); - // ファイルサイズ取得 - off64_t FASTCALL GetFilePos() const; - // ファイル位置取得 - void FASTCALL Close(); - // クローズ -#ifndef BAREMETAL - BOOL FASTCALL IsValid() const { return (BOOL)(handle != -1); } -#else - BOOL FASTCALL IsValid() const { return (BOOL)(handle.obj.fs != 0); } -#endif // BAREMETAL - // 有効チェック + BOOL Open(const char *fname, OpenMode mode); + BOOL Open(const Filepath& path, OpenMode mode); + BOOL OpenDIO(const char *fname, OpenMode mode); + BOOL OpenDIO(const Filepath& path, OpenMode mode); + BOOL Seek(off_t offset, BOOL relative = FALSE); + BOOL Read(void *buffer, int size); + BOOL Write(const void *buffer, int size); + off_t GetFileSize(); + off_t GetFilePos() const; + void Close(); + BOOL IsValid() const { return (BOOL)(handle != -1); } private: -#ifndef BAREMETAL - BOOL FASTCALL Open(LPCTSTR fname, OpenMode mode, BOOL directIO); - // オープン + BOOL Open(const char *fname, OpenMode mode, BOOL directIO); - int handle; // ファイルハンドル -#else - FIL handle; // ファイルハンドル -#endif // BAREMETAL + int handle; // File handle }; #endif // fileio_h diff --git a/src/raspberrypi/filepath.cpp b/src/raspberrypi/filepath.cpp index 13638897..65555218 100644 --- a/src/raspberrypi/filepath.cpp +++ b/src/raspberrypi/filepath.cpp @@ -4,64 +4,43 @@ // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2012-2020 GIMONS -// [ ファイルパス(サブセット) ] +// [ File path (subset) ] // //--------------------------------------------------------------------------- #include "os.h" -#include "xm6.h" #include "filepath.h" #include "fileio.h" +#include "rascsi.h" //=========================================================================== // -// ファイルパス +// File path // //=========================================================================== -//--------------------------------------------------------------------------- -// -// コンストラクタ -// -//--------------------------------------------------------------------------- Filepath::Filepath() { - // クリア + // Clear Clear(); } -//--------------------------------------------------------------------------- -// -// デストラクタ -// -//--------------------------------------------------------------------------- Filepath::~Filepath() { } -//--------------------------------------------------------------------------- -// -// 代入演算子 -// -//--------------------------------------------------------------------------- Filepath& Filepath::operator=(const Filepath& path) { - // パス設定(内部でSplitされる) + // Set path (split internally) SetPath(path.GetPath()); return *this; } -//--------------------------------------------------------------------------- -// -// クリア -// -//--------------------------------------------------------------------------- -void FASTCALL Filepath::Clear() +void Filepath::Clear() { - ASSERT(this); - // パスおよび各部分をクリア + // Clear the path and each part m_szPath[0] = _T('\0'); m_szDir[0] = _T('\0'); m_szFile[0] = _T('\0'); @@ -70,124 +49,41 @@ void FASTCALL Filepath::Clear() //--------------------------------------------------------------------------- // -// ファイル設定(ユーザ) MBCS用 +// File settings (user) for MBCS // //--------------------------------------------------------------------------- -void FASTCALL Filepath::SetPath(LPCSTR path) +void Filepath::SetPath(const char *path) { - ASSERT(this); ASSERT(path); ASSERT(strlen(path) < _MAX_PATH); - // パス名コピー - strcpy(m_szPath, (LPTSTR)path); + // Copy pathname + strcpy(m_szPath, (char *)path); - // 分離 + // Split Split(); } -#ifdef BAREMETAL //--------------------------------------------------------------------------- // -// 互換関数(dirname) 結果は直ぐにコピーせよ +// Split paths // //--------------------------------------------------------------------------- -static char dirtmp[2]; -char* dirname(char *path) +void Filepath::Split() { - char *p; - if( path == NULL || *path == '\0' ) { - dirtmp[0] = '.'; - dirtmp[1] = '\0'; - return dirtmp; - } - - p = path + strlen(path) - 1; - while( *p == '/' ) { - if( p == path ) - return path; - *p-- = '\0'; - } - - while( p >= path && *p != '/' ) { - p--; - } - - if (p < path) { - dirtmp[0] = '.'; - dirtmp[1] = '\0'; - return dirtmp; - } - - if (p == path) { - dirtmp[0] = '/'; - dirtmp[1] = '\0'; - return dirtmp; - } - - *p = 0; - return path; -} - -//--------------------------------------------------------------------------- -// -// 互換関数(basename) 結果は直ぐにコピーせよ -// -//--------------------------------------------------------------------------- -static char basetmp[2]; -char* basename(char *path) -{ - char *p; - if( path == NULL || *path == '\0' ) { - basetmp[0] = '/'; - basetmp[1] = '\0'; - return basetmp; - } - - p = path + strlen(path) - 1; - while( *p == '/' ) { - if( p == path ) { - return path; - } - *p-- = '\0'; - } - - while( p >= path && *p != '/' ) { - p--; - } - - return p + 1; -} -#endif // BAREMETAL - -//--------------------------------------------------------------------------- -// -// パス分離 -// -//--------------------------------------------------------------------------- -void FASTCALL Filepath::Split() -{ - LPTSTR pDir; - LPTSTR pDirName; - LPTSTR pBase; - LPTSTR pBaseName; - LPTSTR pExtName; - - ASSERT(this); - - // パーツを初期化 + // Initialize the parts m_szDir[0] = _T('\0'); m_szFile[0] = _T('\0'); m_szExt[0] = _T('\0'); - // 分離 - pDir = strdup(m_szPath); - pDirName = dirname(pDir); - pBase = strdup(m_szPath); - pBaseName = basename(pBase); - pExtName = strrchr(pBaseName, '.'); + // Split + char *pDir = strdup(m_szPath); + char *pDirName = dirname(pDir); + char *pBase = strdup(m_szPath); + char *pBaseName = basename(pBase); + char *pExtName = strrchr(pBaseName, '.'); - // 転送 + // Transmit if (pDirName) { strcpy(m_szDir, pDirName); strcat(m_szDir, "/"); @@ -201,118 +97,36 @@ void FASTCALL Filepath::Split() strcpy(m_szFile, pBaseName); } - // 解放 + // Release free(pDir); free(pBase); } //--------------------------------------------------------------------------- // -// パス合成 +// File name + extension acquisition +// The returned pointer is temporary. Copy immediately. // //--------------------------------------------------------------------------- -void FASTCALL Filepath::Make() +const char *Filepath::GetFileExt() const { - ASSERT(this); - // パス初期化 - m_szPath[0] = _T('\0'); - - // 合成 - strncat(m_szPath, m_szDir, ARRAY_SIZE(m_szPath) - strlen(m_szPath)); - strncat(m_szPath, m_szFile, ARRAY_SIZE(m_szPath) - strlen(m_szPath)); - strncat(m_szPath, m_szExt, ARRAY_SIZE(m_szPath) - strlen(m_szPath)); -} - -//--------------------------------------------------------------------------- -// -// クリアされているか -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Filepath::IsClear() const -{ - // Clear()の逆 - if ((m_szPath[0] == _T('\0')) && - (m_szDir[0] == _T('\0')) && - (m_szFile[0] == _T('\0')) && - (m_szExt[0] == _T('\0'))) { - // 確かに、クリアされている - return TRUE; - } - - // クリアされていない - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// ショート名取得 -// ※返されるポインタは一時的なもの。すぐコピーすること -// ※FDIDiskのdisk.nameとの関係で、文字列は最大59文字+終端とすること -// -//--------------------------------------------------------------------------- -const char* FASTCALL Filepath::GetShort() const -{ - char szFile[_MAX_FNAME]; - char szExt[_MAX_EXT]; - - ASSERT(this); - - // TCHAR文字列からchar文字列へ変換 - strcpy(szFile, m_szFile); - strcpy(szExt, m_szExt); - - // 固定バッファへ合成 - strcpy(ShortName, szFile); - strcat(ShortName, szExt); - - // strlenで調べたとき、最大59になるように細工 - ShortName[59] = '\0'; - - // const charとして返す - return (const char*)ShortName; -} - -//--------------------------------------------------------------------------- -// -// ファイル名+拡張子取得 -// ※返されるポインタは一時的なもの。すぐコピーすること -// -//--------------------------------------------------------------------------- -LPCTSTR FASTCALL Filepath::GetFileExt() const -{ - ASSERT(this); - - // 固定バッファへ合成 + // Merge into static buffer strcpy(FileExt, m_szExt); - // LPCTSTRとして返す - return (LPCTSTR)FileExt; + // Return as LPCTSTR + return (const char *)FileExt; } -//--------------------------------------------------------------------------- -// -// パス比較 -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Filepath::CmpPath(const Filepath& path) const +BOOL Filepath::Save(Fileio *fio, int /*ver*/) { - // パスが完全一致していればTRUE - if (strcmp(path.GetPath(), GetPath()) == 0) { - return TRUE; - } + ASSERT(fio); - return FALSE; + return TRUE; } -//--------------------------------------------------------------------------- -// -// セーブ -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Filepath::Save(Fileio *fio, int /*ver*/) +BOOL Filepath::Load(Fileio *fio, int /*ver*/) { - ASSERT(this); ASSERT(fio); return TRUE; @@ -320,27 +134,7 @@ BOOL FASTCALL Filepath::Save(Fileio *fio, int /*ver*/) //--------------------------------------------------------------------------- // -// ロード -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Filepath::Load(Fileio *fio, int /*ver*/) -{ - ASSERT(this); - ASSERT(fio); - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// ショート名 -// -//--------------------------------------------------------------------------- -char Filepath::ShortName[_MAX_FNAME + _MAX_DIR]; - -//--------------------------------------------------------------------------- -// -// ファイル名+拡張子 +// Filename and extension // //--------------------------------------------------------------------------- TCHAR Filepath::FileExt[_MAX_FNAME + _MAX_DIR]; diff --git a/src/raspberrypi/filepath.h b/src/raspberrypi/filepath.h index 93ef98a1..c8c947e8 100644 --- a/src/raspberrypi/filepath.h +++ b/src/raspberrypi/filepath.h @@ -4,80 +4,49 @@ // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2012-2020 GIMONS -// [ ファイルパス(サブセット) ] +// [ File path (subset) ] // //--------------------------------------------------------------------------- -#if !defined(filepath_h) -#define filepath_h +#pragma once + +#include "os.h" class Fileio; //--------------------------------------------------------------------------- // -// 定数定義 +// Constant definition // //--------------------------------------------------------------------------- #define FILEPATH_MAX _MAX_PATH //=========================================================================== // -// ファイルパス -// ※代入演算子を用意すること +// File path +// Assignment operators are prepared here // //=========================================================================== class Filepath { public: Filepath(); - // コンストラクタ virtual ~Filepath(); - // デストラクタ Filepath& operator=(const Filepath& path); - // 代入 - void FASTCALL Clear(); - // クリア - void FASTCALL SetPath(LPCSTR path); - // ファイル設定(ユーザ) MBCS用 - BOOL FASTCALL IsClear() const; - // クリアされているか - LPCTSTR FASTCALL GetPath() const { return m_szPath; } - // パス名取得 - const char* FASTCALL GetShort() const; - // ショート名取得(const char*) - LPCTSTR FASTCALL GetFileExt() const; - // ショート名取得(LPCTSTR) - BOOL FASTCALL CmpPath(const Filepath& path) const; - // パス比較 - - BOOL FASTCALL Save(Fileio *fio, int ver); - // セーブ - BOOL FASTCALL Load(Fileio *fio, int ver); - // ロード + void Clear(); + void SetPath(const char *path); // File settings (user) for MBCS + const char *GetPath() const { return m_szPath; } // Get path name + const char *GetFileExt() const; // Get short name (LPCTSTR) + BOOL Save(Fileio *fio, int ver); + BOOL Load(Fileio *fio, int ver); private: - void FASTCALL Split(); - // パス分割 - void FASTCALL Make(); - // パス合成 - void FASTCALL SetCurDir(); - // カレントディレクトリ設定 - TCHAR m_szPath[_MAX_PATH]; - // ファイルパス - TCHAR m_szDrive[_MAX_DRIVE]; - // ドライブ - TCHAR m_szDir[_MAX_DIR]; - // ディレクトリ - TCHAR m_szFile[_MAX_FNAME]; - // ファイル - TCHAR m_szExt[_MAX_EXT]; - // 拡張子 + void Split(); // Split the path + TCHAR m_szPath[_MAX_PATH]; // File path + TCHAR m_szDir[_MAX_DIR]; // Directory + TCHAR m_szFile[_MAX_FNAME]; // File + TCHAR m_szExt[_MAX_EXT]; // Extension - static char ShortName[_MAX_FNAME + _MAX_DIR]; - // ショート名(char) - static TCHAR FileExt[_MAX_FNAME + _MAX_DIR]; - // ショート名(TCHAR) + static TCHAR FileExt[_MAX_FNAME + _MAX_DIR]; // Short name (TCHAR) }; - -#endif // filepath_h diff --git a/src/raspberrypi/gpiobus.cpp b/src/raspberrypi/gpiobus.cpp index 6996bcb3..fc24ef16 100644 --- a/src/raspberrypi/gpiobus.cpp +++ b/src/raspberrypi/gpiobus.cpp @@ -12,12 +12,13 @@ // //--------------------------------------------------------------------------- +#include + #include "os.h" -#include "xm6.h" #include "gpiobus.h" #include "log.h" +#include "rascsi.h" -#ifndef BAREMETAL #ifdef __linux__ //--------------------------------------------------------------------------- // @@ -80,70 +81,27 @@ DWORD bcm_host_get_peripheral_address(void) return address; } #endif // __NetBSD__ -#endif // BAREMETAL -#ifdef BAREMETAL -// IO base address -extern uint32_t RPi_IO_Base_Addr; - -// Core frequency -extern uint32_t RPi_Core_Freq; - -#ifdef USE_SEL_EVENT_ENABLE -//--------------------------------------------------------------------------- -// -// Interrupt control function -// -//--------------------------------------------------------------------------- -extern "C" { -extern uintptr_t setIrqFuncAddress (void(*ARMaddress)(void)); -extern void EnableInterrupts (void); -extern void DisableInterrupts (void); -extern void WaitForInterrupts (void); -} - -//--------------------------------------------------------------------------- -// -// Interrupt handler -// -//--------------------------------------------------------------------------- -static GPIOBUS *self; -extern "C" -void IrqHandler() -{ - // Clear interrupt - self->ClearSelectEvent(); -} -#endif // USE_SEL_EVENT_ENABLE -#endif // BAREMETAL - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- GPIOBUS::GPIOBUS() { -#if defined(USE_SEL_EVENT_ENABLE) && defined(BAREMETAL) - self = this; -#endif // USE_SEL_EVENT_ENABLE && BAREMETAL + actmode = TARGET; + baseaddr = 0; + gicc = 0; + gicd = 0; + gpio = 0; + level = 0; + pads = 0; + irpctl = 0; + qa7regs = 0; + signals = 0; + rpitype = 0; } -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- GPIOBUS::~GPIOBUS() { } -//--------------------------------------------------------------------------- -// -// 初期化 -// -//--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::Init(mode_e mode) +BOOL GPIOBUS::Init(mode_e mode) { #if defined(__x86_64__) || defined(__X86__) // When we're running on x86, there is no hardware to talk to, so just return. @@ -153,21 +111,14 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) int i; int j; int pullmode; -#ifndef BAREMETAL int fd; #ifdef USE_SEL_EVENT_ENABLE struct epoll_event ev; #endif // USE_SEL_EVENT_ENABLE -#endif // BAREMETAL // Save operation mode actmode = mode; -#ifdef BAREMETAL - // Get the base address - baseaddr = RPi_IO_Base_Addr; - map = (void*)baseaddr; -#else // Get the base address baseaddr = (DWORD)bcm_host_get_peripheral_address(); @@ -179,13 +130,13 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) } // Map peripheral region memory - map = mmap(NULL, 0x1000100, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseaddr); + map = mmap(NULL, 0x1000100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseaddr); if (map == MAP_FAILED) { + LOGERROR("Error: Unable to map memory"); close(fd); return FALSE; } -#endif + // Determine the type of raspberry pi from the base address if (baseaddr == 0xfe000000) { rpitype = 4; @@ -213,24 +164,10 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) irpctl = (DWORD *)map; irpctl += IRPT_OFFSET / sizeof(DWORD); -#ifndef BAREMETAL // Quad-A7 control qa7regs = (DWORD *)map; qa7regs += QA7_OFFSET / sizeof(DWORD); -#endif // BAREMETAL -#ifdef BAREMETAL - // Map GIC memory - if (rpitype == 4) { - map = (void*)ARM_GICD_BASE; - gicd = (DWORD *)map; - map = (void*)ARM_GICC_BASE; - gicc = (DWORD *)map; - } else { - gicd = NULL; - gicc = NULL; - } -#else // Map GIC memory if (rpitype == 4) { map = mmap(NULL, 8192, @@ -247,7 +184,6 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) gicc = NULL; } close(fd); -#endif // BAREMETAL // Set Drive Strength to 16mA DrvConfig(7); @@ -292,7 +228,6 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) // Initialize SEL signal interrupt #ifdef USE_SEL_EVENT_ENABLE -#ifndef BAREMETAL // GPIO chip open fd = open("/dev/gpiochip0", 0); if (fd == -1) { @@ -375,7 +310,6 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) // Enable interrupts irpctl[IRPT_ENB_IRQ_2] = (1 << (GPIO_IRQ % 32)); } -#endif // BAREMETAL #endif // USE_SEL_EVENT_ENABLE // Create work table @@ -390,12 +324,7 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) } -//--------------------------------------------------------------------------- -// -// Cleanup -// -//--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::Cleanup() +void GPIOBUS::Cleanup() { #if defined(__x86_64__) || defined(__X86__) return; @@ -405,9 +334,7 @@ void FASTCALL GPIOBUS::Cleanup() // Release SEL signal interrupt #ifdef USE_SEL_EVENT_ENABLE -#ifndef BAREMETAL close(selevreq.fd); -#endif // BAREMETAL #endif // USE_SEL_EVENT_ENABLE // Set control signals @@ -434,12 +361,7 @@ void FASTCALL GPIOBUS::Cleanup() #endif // ifdef __x86_64__ || __X86__ } -//--------------------------------------------------------------------------- -// -// Reset -// -//--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::Reset() +void GPIOBUS::Reset() { #if defined(__x86_64__) || defined(__X86__) return; @@ -530,7 +452,7 @@ void FASTCALL GPIOBUS::Reset() // ENB signal setting // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetENB(BOOL ast) +void GPIOBUS::SetENB(BOOL ast) { PinSetSignal(PIN_ENB, ast ? ENB_ON : ENB_OFF); } @@ -540,7 +462,7 @@ void FASTCALL GPIOBUS::SetENB(BOOL ast) // Get BSY signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetBSY() +bool GPIOBUS::GetBSY() { return GetSignal(PIN_BSY); } @@ -550,7 +472,7 @@ BOOL FASTCALL GPIOBUS::GetBSY() // Set BSY signal // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetBSY(BOOL ast) +void GPIOBUS::SetBSY(bool ast) { // Set BSY signal SetSignal(PIN_BSY, ast); @@ -589,7 +511,7 @@ void FASTCALL GPIOBUS::SetBSY(BOOL ast) // Get SEL signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetSEL() +BOOL GPIOBUS::GetSEL() { return GetSignal(PIN_SEL); } @@ -599,7 +521,7 @@ BOOL FASTCALL GPIOBUS::GetSEL() // Set SEL signal // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetSEL(BOOL ast) +void GPIOBUS::SetSEL(BOOL ast) { if (actmode == INITIATOR && ast) { // Turn on ACTIVE signal @@ -615,7 +537,7 @@ void FASTCALL GPIOBUS::SetSEL(BOOL ast) // Get ATN signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetATN() +BOOL GPIOBUS::GetATN() { return GetSignal(PIN_ATN); } @@ -625,7 +547,7 @@ BOOL FASTCALL GPIOBUS::GetATN() // Get ATN signal // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetATN(BOOL ast) +void GPIOBUS::SetATN(BOOL ast) { SetSignal(PIN_ATN, ast); } @@ -635,7 +557,7 @@ void FASTCALL GPIOBUS::SetATN(BOOL ast) // Get ACK signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetACK() +BOOL GPIOBUS::GetACK() { return GetSignal(PIN_ACK); } @@ -645,7 +567,7 @@ BOOL FASTCALL GPIOBUS::GetACK() // Set ACK signal // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetACK(BOOL ast) +void GPIOBUS::SetACK(BOOL ast) { SetSignal(PIN_ACK, ast); } @@ -655,7 +577,7 @@ void FASTCALL GPIOBUS::SetACK(BOOL ast) // Get ACK signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetACT() +BOOL GPIOBUS::GetACT() { return GetSignal(PIN_ACT); } @@ -665,7 +587,7 @@ BOOL FASTCALL GPIOBUS::GetACT() // Set ACK signal // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetACT(BOOL ast) +void GPIOBUS::SetACT(BOOL ast) { SetSignal(PIN_ACT, ast); } @@ -675,7 +597,7 @@ void FASTCALL GPIOBUS::SetACT(BOOL ast) // Get RST signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetRST() +BOOL GPIOBUS::GetRST() { return GetSignal(PIN_RST); } @@ -685,7 +607,7 @@ BOOL FASTCALL GPIOBUS::GetRST() // Set RST signal // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetRST(BOOL ast) +void GPIOBUS::SetRST(BOOL ast) { SetSignal(PIN_RST, ast); } @@ -695,7 +617,7 @@ void FASTCALL GPIOBUS::SetRST(BOOL ast) // Get MSG signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetMSG() +BOOL GPIOBUS::GetMSG() { return GetSignal(PIN_MSG); } @@ -705,7 +627,7 @@ BOOL FASTCALL GPIOBUS::GetMSG() // Set MSG signal // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetMSG(BOOL ast) +void GPIOBUS::SetMSG(BOOL ast) { SetSignal(PIN_MSG, ast); } @@ -715,7 +637,7 @@ void FASTCALL GPIOBUS::SetMSG(BOOL ast) // Get CD signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetCD() +BOOL GPIOBUS::GetCD() { return GetSignal(PIN_CD); } @@ -725,7 +647,7 @@ BOOL FASTCALL GPIOBUS::GetCD() // Set CD Signal // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetCD(BOOL ast) +void GPIOBUS::SetCD(BOOL ast) { SetSignal(PIN_CD, ast); } @@ -735,7 +657,7 @@ void FASTCALL GPIOBUS::SetCD(BOOL ast) // Get IO Signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetIO() +BOOL GPIOBUS::GetIO() { BOOL ast; ast = GetSignal(PIN_IO); @@ -775,7 +697,7 @@ BOOL FASTCALL GPIOBUS::GetIO() // Set IO signal // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetIO(BOOL ast) +void GPIOBUS::SetIO(BOOL ast) { SetSignal(PIN_IO, ast); @@ -813,7 +735,7 @@ void FASTCALL GPIOBUS::SetIO(BOOL ast) // Get REQ signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetREQ() +BOOL GPIOBUS::GetREQ() { return GetSignal(PIN_REQ); } @@ -823,7 +745,7 @@ BOOL FASTCALL GPIOBUS::GetREQ() // Set REQ signal // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetREQ(BOOL ast) +void GPIOBUS::SetREQ(BOOL ast) { SetSignal(PIN_REQ, ast); } @@ -833,7 +755,7 @@ void FASTCALL GPIOBUS::SetREQ(BOOL ast) // Get data signals // //--------------------------------------------------------------------------- -BYTE FASTCALL GPIOBUS::GetDAT() +BYTE GPIOBUS::GetDAT() { DWORD data; @@ -856,7 +778,7 @@ BYTE FASTCALL GPIOBUS::GetDAT() // Set data signals // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetDAT(BYTE dat) +void GPIOBUS::SetDAT(BYTE dat) { // Write to port #if SIGNAL_CONTROL_MODE == 0 @@ -896,7 +818,7 @@ void FASTCALL GPIOBUS::SetDAT(BYTE dat) // Get data parity signal // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetDP() +BOOL GPIOBUS::GetDP() { return GetSignal(PIN_DP); } @@ -906,7 +828,7 @@ BOOL FASTCALL GPIOBUS::GetDP() // Receive command handshake // //--------------------------------------------------------------------------- -int FASTCALL GPIOBUS::CommandHandShake(BYTE *buf) +int GPIOBUS::CommandHandShake(BYTE *buf) { int i; BOOL ret; @@ -951,12 +873,7 @@ int FASTCALL GPIOBUS::CommandHandShake(BYTE *buf) goto irq_enable_exit; } - // Distinguise whether the command is 6 bytes or 10 bytes - if (*buf >= 0x20 && *buf <= 0x7D) { - count = 10; - } else { - count = 6; - } + count = GetCommandByteCount(*buf); // Increment buffer pointer buf++; @@ -1007,7 +924,7 @@ irq_enable_exit: // Data reception handshake // //--------------------------------------------------------------------------- -int FASTCALL GPIOBUS::ReceiveHandShake(BYTE *buf, int count) +int GPIOBUS::ReceiveHandShake(BYTE *buf, int count) { int i; BOOL ret; @@ -1109,7 +1026,7 @@ int FASTCALL GPIOBUS::ReceiveHandShake(BYTE *buf, int count) // Data transmission handshake // //--------------------------------------------------------------------------- -int FASTCALL GPIOBUS::SendHandShake(BYTE *buf, int count) +int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes) { int i; BOOL ret; @@ -1120,6 +1037,11 @@ int FASTCALL GPIOBUS::SendHandShake(BYTE *buf, int count) if (actmode == TARGET) { for (i = 0; i < count; i++) { + if(i==delay_after_bytes){ + LOGTRACE("%s DELAYING for %dus after %d bytes", __PRETTY_FUNCTION__, SCSI_DELAY_SEND_DATA_DAYNAPORT_US, (int)delay_after_bytes); + SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US); + } + // Set the DATA signals SetDAT(*buf); @@ -1158,6 +1080,11 @@ int FASTCALL GPIOBUS::SendHandShake(BYTE *buf, int count) phase = Aquire() & GPIO_MCI; for (i = 0; i < count; i++) { + if(i==delay_after_bytes){ + LOGTRACE("%s DELAYING for %dus after %d bytes", __PRETTY_FUNCTION__, SCSI_DELAY_SEND_DATA_DAYNAPORT_US, (int)delay_after_bytes); + SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US); + } + // Set the DATA signals SetDAT(*buf); @@ -1213,30 +1140,22 @@ int FASTCALL GPIOBUS::SendHandShake(BYTE *buf, int count) // SEL signal event polling // //--------------------------------------------------------------------------- -int FASTCALL GPIOBUS::PollSelectEvent() +int GPIOBUS::PollSelectEvent() { // clear errno errno = 0; - -#ifdef BAREMETAL - // Enable interrupts - EnableInterrupts(); - - // Wait for interrupts - WaitForInterrupts(); - - // Disable interrupts - DisableInterrupts(); -#else struct epoll_event epev; struct gpioevent_data gpev; if (epoll_wait(epfd, &epev, 1, -1) <= 0) { + LOGWARN("%s epoll_wait failed", __PRETTY_FUNCTION__); return -1; } - (void)read(selevreq.fd, &gpev, sizeof(gpev)); -#endif // BAREMETAL + if (read(selevreq.fd, &gpev, sizeof(gpev)) < 0) { + LOGWARN("%s read failed", __PRETTY_FUNCTION__); + return -1; + } return 0; } @@ -1246,23 +1165,8 @@ int FASTCALL GPIOBUS::PollSelectEvent() // Cancel SEL signal event // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::ClearSelectEvent() +void GPIOBUS::ClearSelectEvent() { -#ifdef BAREMETAL - DWORD irq; - - // Clear event - gpio[GPIO_EDS_0] = 1 << PIN_SEL; - - // Response to GIC - if (rpitype == 4) { - // IRQ number - irq = gicc[GICC_IAR] & 0x3FF; - - // Interrupt response - gicc[GICC_EOIR] = irq; - } -#endif // BAREMETAL } #endif // USE_SEL_EVENT_ENABLE @@ -1284,7 +1188,7 @@ const int GPIOBUS::SignalTable[19] = { // Create work table // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::MakeTable(void) +void GPIOBUS::MakeTable(void) { const int pintbl[] = { PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, @@ -1387,7 +1291,7 @@ void FASTCALL GPIOBUS::MakeTable(void) // Control signal setting // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetControl(int pin, BOOL ast) +void GPIOBUS::SetControl(int pin, BOOL ast) { PinSetSignal(pin, ast); } @@ -1397,7 +1301,7 @@ void FASTCALL GPIOBUS::SetControl(int pin, BOOL ast) // Input/output mode setting // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetMode(int pin, int mode) +void GPIOBUS::SetMode(int pin, int mode) { int index; int shift; @@ -1425,7 +1329,7 @@ void FASTCALL GPIOBUS::SetMode(int pin, int mode) // Get input signal value // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::GetSignal(int pin) +BOOL GPIOBUS::GetSignal(int pin) { return (signals >> pin) & 1; } @@ -1435,7 +1339,7 @@ BOOL FASTCALL GPIOBUS::GetSignal(int pin) // Set output signal value // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::SetSignal(int pin, BOOL ast) +void GPIOBUS::SetSignal(int pin, BOOL ast) { #if SIGNAL_CONTROL_MODE == 0 int index; @@ -1472,7 +1376,7 @@ void FASTCALL GPIOBUS::SetSignal(int pin, BOOL ast) // Wait for signal change // //--------------------------------------------------------------------------- -BOOL FASTCALL GPIOBUS::WaitSignal(int pin, BOOL ast) +BOOL GPIOBUS::WaitSignal(int pin, BOOL ast) { DWORD now; DWORD timeout; @@ -1506,9 +1410,8 @@ BOOL FASTCALL GPIOBUS::WaitSignal(int pin, BOOL ast) // Disable IRQ // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::DisableIRQ() +void GPIOBUS::DisableIRQ() { -#ifndef BAREMETAL if (rpitype == 4) { // RPI4 is disabled by GICC giccpmr = gicc[GICC_PMR]; @@ -1523,7 +1426,6 @@ void FASTCALL GPIOBUS::DisableIRQ() irptenb = irpctl[IRPT_ENB_IRQ_1]; irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf; } -#endif // BAREMETAL } //--------------------------------------------------------------------------- @@ -1531,9 +1433,8 @@ void FASTCALL GPIOBUS::DisableIRQ() // Enable IRQ // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::EnableIRQ() +void GPIOBUS::EnableIRQ() { -#ifndef BAREMETAL if (rpitype == 4) { // RPI4 enables interrupts via the GICC gicc[GICC_PMR] = giccpmr; @@ -1544,7 +1445,6 @@ void FASTCALL GPIOBUS::EnableIRQ() // Restart the system timer interrupt with the interrupt controller irpctl[IRPT_ENB_IRQ_1] = irptenb & 0xf; } -#endif // BAREMETAL } //--------------------------------------------------------------------------- @@ -1552,7 +1452,7 @@ void FASTCALL GPIOBUS::EnableIRQ() // Pin direction setting (input/output) // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::PinConfig(int pin, int mode) +void GPIOBUS::PinConfig(int pin, int mode) { int index; DWORD mask; @@ -1572,7 +1472,7 @@ void FASTCALL GPIOBUS::PinConfig(int pin, int mode) // Pin pull-up/pull-down setting // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::PullConfig(int pin, int mode) +void GPIOBUS::PullConfig(int pin, int mode) { int shift; DWORD bits; @@ -1620,7 +1520,7 @@ void FASTCALL GPIOBUS::PullConfig(int pin, int mode) // Set output pin // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::PinSetSignal(int pin, BOOL ast) +void GPIOBUS::PinSetSignal(int pin, BOOL ast) { // Check for invalid pin if (pin < 0) { @@ -1639,7 +1539,7 @@ void FASTCALL GPIOBUS::PinSetSignal(int pin, BOOL ast) // Set the signal drive strength // //--------------------------------------------------------------------------- -void FASTCALL GPIOBUS::DrvConfig(DWORD drive) +void GPIOBUS::DrvConfig(DWORD drive) { DWORD data; @@ -1653,7 +1553,7 @@ void FASTCALL GPIOBUS::DrvConfig(DWORD drive) // Generic Phase Acquisition (Doesn't read GPIO) // //--------------------------------------------------------------------------- -BUS::phase_t FASTCALL GPIOBUS::GetPhaseRaw(DWORD raw_data) +BUS::phase_t GPIOBUS::GetPhaseRaw(DWORD raw_data) { DWORD mci; @@ -1679,6 +1579,26 @@ BUS::phase_t FASTCALL GPIOBUS::GetPhaseRaw(DWORD raw_data) return GetPhase(mci); } +//--------------------------------------------------------------------------- +// +// Get the number of bytes for a command +// +// TODO The command length should be determined based on the bytes transferred in the COMMAND phase +// +//--------------------------------------------------------------------------- +int GPIOBUS::GetCommandByteCount(BYTE opcode) { + if (opcode == 0x88 || opcode == 0x8A || opcode == 0x8F || opcode == 0x91 || opcode == 0x9E) { + return 16; + } + else if (opcode == 0xA0) { + return 12; + } + else if (opcode >= 0x20 && opcode <= 0x7D) { + return 10; + } else { + return 6; + } +} //--------------------------------------------------------------------------- // @@ -1706,9 +1626,8 @@ volatile DWORD SysTimer::corefreq; // Initialize the system timer // //--------------------------------------------------------------------------- -void FASTCALL SysTimer::Init(DWORD *syst, DWORD *armt) +void SysTimer::Init(DWORD *syst, DWORD *armt) { -#ifndef BAREMETAL // RPI Mailbox property interface // Get max clock rate // Tag: 0x00030004 @@ -1722,7 +1641,6 @@ void FASTCALL SysTimer::Init(DWORD *syst, DWORD *armt) // 0x000000004: CORE DWORD maxclock[32] = { 32, 0, 0x00030004, 8, 0, 4, 0, 0 }; int fd; -#endif // BAREMETAL // Save the base address systaddr = syst; @@ -1732,9 +1650,6 @@ void FASTCALL SysTimer::Init(DWORD *syst, DWORD *armt) armtaddr[ARMT_CTRL] = 0x00000282; // Get the core frequency -#ifdef BAREMETAL - corefreq = RPi_Core_Freq / 1000000; -#else corefreq = 0; fd = open("/dev/vcio", O_RDONLY); if (fd >= 0) { @@ -1742,7 +1657,6 @@ void FASTCALL SysTimer::Init(DWORD *syst, DWORD *armt) corefreq = maxclock[6] / 1000000; } close(fd); -#endif // BAREMETAL } //--------------------------------------------------------------------------- @@ -1750,7 +1664,7 @@ void FASTCALL SysTimer::Init(DWORD *syst, DWORD *armt) // Get system timer low byte // //--------------------------------------------------------------------------- -DWORD FASTCALL SysTimer::GetTimerLow() { +DWORD SysTimer::GetTimerLow() { return systaddr[SYST_CLO]; } @@ -1759,7 +1673,7 @@ DWORD FASTCALL SysTimer::GetTimerLow() { // Get system timer high byte // //--------------------------------------------------------------------------- -DWORD FASTCALL SysTimer::GetTimerHigh() { +DWORD SysTimer::GetTimerHigh() { return systaddr[SYST_CHI]; } @@ -1768,7 +1682,7 @@ DWORD FASTCALL SysTimer::GetTimerHigh() { // Sleep in nanoseconds // //--------------------------------------------------------------------------- -void FASTCALL SysTimer::SleepNsec(DWORD nsec) +void SysTimer::SleepNsec(DWORD nsec) { DWORD diff; DWORD start; @@ -1798,7 +1712,7 @@ void FASTCALL SysTimer::SleepNsec(DWORD nsec) // Sleep in microseconds // //--------------------------------------------------------------------------- -void FASTCALL SysTimer::SleepUsec(DWORD usec) +void SysTimer::SleepUsec(DWORD usec) { DWORD now; diff --git a/src/raspberrypi/gpiobus.h b/src/raspberrypi/gpiobus.h index 0bce816b..cc1dcfee 100644 --- a/src/raspberrypi/gpiobus.h +++ b/src/raspberrypi/gpiobus.h @@ -12,6 +12,7 @@ #if !defined(gpiobus_h) #define gpiobus_h +#include "rascsi.h" #include "scsi.h" //--------------------------------------------------------------------------- @@ -188,7 +189,7 @@ #ifdef CONNECT_TYPE_AIBOM // -// RaSCSI Adapter Aibomu version +// RaSCSI Adapter Aibom version // #define CONNECT_DESC "AIBOM PRODUCTS version" // Startup message @@ -233,7 +234,7 @@ #ifdef CONNECT_TYPE_GAMERNIUM // -// RaSCSI Adapter GAMERnium.com版 +// RaSCSI Adapter GAMERnium.com version // #define CONNECT_DESC "GAMERnium.com version"// Startup message @@ -298,7 +299,7 @@ //--------------------------------------------------------------------------- // -// Constant declarations(GPIO) +// Constant declarations (GPIO) // //--------------------------------------------------------------------------- #define SYST_OFFSET 0x00003000 @@ -375,7 +376,7 @@ //--------------------------------------------------------------------------- // -// Constant declarations(GIC) +// Constant declarations (GIC) // //--------------------------------------------------------------------------- #define ARM_GICD_BASE 0xFF841000 @@ -400,7 +401,7 @@ //--------------------------------------------------------------------------- // -// Constant declarations(GIC IRQ) +// Constant declarations (GIC IRQ) // //--------------------------------------------------------------------------- #define GIC_IRQLOCAL0 (16 + 14) @@ -419,7 +420,7 @@ //--------------------------------------------------------------------------- // -// Constant declarations(SCSI) +// Constant declarations (SCSI) // //--------------------------------------------------------------------------- #define IN GPIO_INPUT @@ -433,6 +434,35 @@ // //--------------------------------------------------------------------------- #define GPIO_DATA_SETTLING 100 // Data bus stabilization time (ns) +// SCSI Bus timings taken from: +// https://www.staff.uni-mainz.de/tacke/scsi/SCSI2-05.html +#define SCSI_DELAY_ARBITRATION_DELAY_NS 2400 +#define SCSI_DELAY_ASSERTION_PERIOD_NS 90 +#define SCSI_DELAY_BUS_CLEAR_DELAY_NS 800 +#define SCSI_DELAY_BUS_FREE_DELAY_NS 800 +#define SCSI_DELAY_BUS_SET_DELAY_NS 1800 +#define SCSI_DELAY_BUS_SETTLE_DELAY_NS 400 +#define SCSI_DELAY_CABLE_SKEW_DELAY_NS 10 +#define SCSI_DELAY_DATA_RELEASE_DELAY_NS 400 +#define SCSI_DELAY_DESKEW_DELAY_NS 45 +#define SCSI_DELAY_DISCONNECTION_DELAY_US 200 +#define SCSI_DELAY_HOLD_TIME_NS 45 +#define SCSI_DELAY_NEGATION_PERIOD_NS 90 +#define SCSI_DELAY_POWER_ON_TO_SELECTION_TIME_S 10 // (recommended) +#define SCSI_DELAY_RESET_TO_SELECTION_TIME_US (250*1000) // (recommended) +#define SCSI_DELAY_RESET_HOLD_TIME_US 25 +#define SCSI_DELAY_SELECTION_ABORT_TIME_US 200 +#define SCSI_DELAY_SELECTION_TIMEOUT_DELAY_NS (250*1000) // (recommended) +#define SCSI_DELAY_FAST_ASSERTION_PERIOD_NS 30 +#define SCSI_DELAY_FAST_CABLE_SKEW_DELAY_NS 5 +#define SCSI_DELAY_FAST_DESKEW_DELAY_NS 20 +#define SCSI_DELAY_FAST_HOLD_TIME_NS 10 +#define SCSI_DELAY_FAST_NEGATION_PERIOD_NS 30 + +// The DaynaPort SCSI Link do a short delay in the middle of transfering +// a packet. This is the number of uS that will be delayed between the +// header and the actual data. +#define SCSI_DELAY_SEND_DATA_DAYNAPORT_US 100 //--------------------------------------------------------------------------- // @@ -447,11 +477,11 @@ public: // Constructor virtual ~GPIOBUS(); // Destructor - BOOL FASTCALL Init(mode_e mode = TARGET); + BOOL Init(mode_e mode = TARGET); // Initialization - void FASTCALL Reset(); + void Reset(); // Reset - void FASTCALL Cleanup(); + void Cleanup(); // Cleanup //--------------------------------------------------------------------------- @@ -477,111 +507,113 @@ public: #endif // ifdef __x86_64__ || __X86__ } - void FASTCALL SetENB(BOOL ast); + void SetENB(BOOL ast); // Set ENB signal - BOOL FASTCALL GetBSY(); + bool GetBSY(); // Get BSY signal - void FASTCALL SetBSY(BOOL ast); + void SetBSY(bool ast); // Set BSY signal - BOOL FASTCALL GetSEL(); + BOOL GetSEL(); // Get SEL signal - void FASTCALL SetSEL(BOOL ast); + void SetSEL(BOOL ast); // Set SEL signal - BOOL FASTCALL GetATN(); + BOOL GetATN(); // Get ATN signal - void FASTCALL SetATN(BOOL ast); + void SetATN(BOOL ast); // Set ATN signal - BOOL FASTCALL GetACK(); + BOOL GetACK(); // Get ACK signal - void FASTCALL SetACK(BOOL ast); + void SetACK(BOOL ast); // Set ACK signal - BOOL FASTCALL GetACT(); + BOOL GetACT(); // Get ACT signal - void FASTCALL SetACT(BOOL ast); + void SetACT(BOOL ast); // Set ACT signal - BOOL FASTCALL GetRST(); + BOOL GetRST(); // Get RST signal - void FASTCALL SetRST(BOOL ast); + void SetRST(BOOL ast); // Set RST signal - BOOL FASTCALL GetMSG(); + BOOL GetMSG(); // Get MSG signal - void FASTCALL SetMSG(BOOL ast); + void SetMSG(BOOL ast); // Set MSG signal - BOOL FASTCALL GetCD(); + BOOL GetCD(); // Get CD signal - void FASTCALL SetCD(BOOL ast); + void SetCD(BOOL ast); // Set CD signal - BOOL FASTCALL GetIO(); + BOOL GetIO(); // Get IO signal - void FASTCALL SetIO(BOOL ast); + void SetIO(BOOL ast); // Set IO signal - BOOL FASTCALL GetREQ(); + BOOL GetREQ(); // Get REQ signal - void FASTCALL SetREQ(BOOL ast); + void SetREQ(BOOL ast); // Set REQ signal - BYTE FASTCALL GetDAT(); + BYTE GetDAT(); // Get DAT signal - void FASTCALL SetDAT(BYTE dat); + void SetDAT(BYTE dat); // Set DAT signal - BOOL FASTCALL GetDP(); + BOOL GetDP(); // Get Data parity signal - int FASTCALL CommandHandShake(BYTE *buf); + int CommandHandShake(BYTE *buf); // Command receive handshake - int FASTCALL ReceiveHandShake(BYTE *buf, int count); + int ReceiveHandShake(BYTE *buf, int count); // Data receive handshake - int FASTCALL SendHandShake(BYTE *buf, int count); + int SendHandShake(BYTE *buf, int count, int delay_after_bytes); // Data transmission handshake - static BUS::phase_t FASTCALL GetPhaseRaw(DWORD raw_data); - // Get the phase based on raw data + static BUS::phase_t GetPhaseRaw(DWORD raw_data); + // Get the phase based on raw data -#ifdef USE_SEL_EVENT_ENABLE + static int GetCommandByteCount(BYTE opcode); + + #ifdef USE_SEL_EVENT_ENABLE // SEL signal interrupt - int FASTCALL PollSelectEvent(); + int PollSelectEvent(); // SEL signal event polling - void FASTCALL ClearSelectEvent(); + void ClearSelectEvent(); // Clear SEL signal event #endif // USE_SEL_EVENT_ENABLE private: // SCSI I/O signal control - void FASTCALL MakeTable(); + void MakeTable(); // Create work data - void FASTCALL SetControl(int pin, BOOL ast); + void SetControl(int pin, BOOL ast); // Set Control Signal - void FASTCALL SetMode(int pin, int mode); + void SetMode(int pin, int mode); // Set SCSI I/O mode - BOOL FASTCALL GetSignal(int pin); + BOOL GetSignal(int pin); // Get SCSI input signal value - void FASTCALL SetSignal(int pin, BOOL ast); + void SetSignal(int pin, BOOL ast); // Set SCSI output signal value - BOOL FASTCALL WaitSignal(int pin, BOOL ast); + BOOL WaitSignal(int pin, BOOL ast); // Wait for a signal to change // Interrupt control - void FASTCALL DisableIRQ(); + void DisableIRQ(); // IRQ Disabled - void FASTCALL EnableIRQ(); + void EnableIRQ(); // IRQ Enabled // GPIO pin functionality settings - void FASTCALL PinConfig(int pin, int mode); + void PinConfig(int pin, int mode); // GPIO pin direction setting - void FASTCALL PullConfig(int pin, int mode); + void PullConfig(int pin, int mode); // GPIO pin pull up/down resistor setting - void FASTCALL PinSetSignal(int pin, BOOL ast); + void PinSetSignal(int pin, BOOL ast); // Set GPIO output signal - void FASTCALL DrvConfig(DWORD drive); + void DrvConfig(DWORD drive); // Set GPIO drive strength @@ -589,8 +621,7 @@ private: DWORD baseaddr; // Base address - int rpitype; - // Type of Raspberry Pi + int rpitype; // Type of Raspberry Pi volatile DWORD *gpio; // GPIO register @@ -600,7 +631,6 @@ private: volatile DWORD *irpctl; // Interrupt control register -#ifndef BAREMETAL volatile DWORD irptenb; // Interrupt enabled state volatile DWORD *qa7regs; // QA7 register @@ -610,7 +640,6 @@ private: volatile DWORD tintctl; // Interupt control volatile DWORD giccpmr; // GICC priority setting -#endif // BAREMETAL volatile DWORD *gicd; // GIC Interrupt distributor register @@ -620,11 +649,11 @@ private: DWORD signals; // All bus signals -#if defined(USE_SEL_EVENT_ENABLE) && !defined(BAREMETAL) - struct gpioevent_request selevreq; // SEL signal event request +#ifdef USE_SEL_EVENT_ENABLE + struct gpioevent_request selevreq = {}; // SEL signal event request int epfd; // epoll file descriptor -#endif // USE_SEL_EVENT_ENABLE && !BAREMETAL +#endif // USE_SEL_EVENT_ENABLE #if SIGNAL_CONTROL_MODE == 0 DWORD tblDatMsk[3][256]; // Data mask table @@ -647,15 +676,15 @@ private: class SysTimer { public: - static void FASTCALL Init(DWORD *syst, DWORD *armt); + static void Init(DWORD *syst, DWORD *armt); // Initialization - static DWORD FASTCALL GetTimerLow(); + static DWORD GetTimerLow(); // Get system timer low byte - static DWORD FASTCALL GetTimerHigh(); + static DWORD GetTimerHigh(); // Get system timer high byte - static void FASTCALL SleepNsec(DWORD nsec); + static void SleepNsec(DWORD nsec); // Sleep for N nanoseconds - static void FASTCALL SleepUsec(DWORD usec); + static void SleepUsec(DWORD usec); // Sleep for N microseconds private: diff --git a/src/raspberrypi/launch_sudo.sh b/src/raspberrypi/launch_sudo.sh new file mode 100755 index 00000000..6ce283c1 --- /dev/null +++ b/src/raspberrypi/launch_sudo.sh @@ -0,0 +1,3 @@ +# This is used for debugging. VisualStudio code will call this file when launching +# the debugger, instead of directly calling GDB. That way we can add the pkexec +sudo /usr/bin/gdb "$@" \ No newline at end of file diff --git a/src/raspberrypi/log.h b/src/raspberrypi/log.h index 79882b75..7347376d 100644 --- a/src/raspberrypi/log.h +++ b/src/raspberrypi/log.h @@ -16,40 +16,18 @@ #include "spdlog/spdlog.h" #include "spdlog/sinks/sink.h" -#define SPDLOGWRAPPER(loglevel, ...)\ - do{ char buf[256]; \ - snprintf(buf, sizeof(buf),__VA_ARGS__); \ - spdlog::log(loglevel,buf);}while(0); - -#ifndef DEBUG -// If we're doing a non-debug build, we want to skip the overhead of -// formatting the string, then calling the logger -#define LOGTRACE(...) ((void)0) -#define LOGDEBUG(...) ((void)0) -#else -#define LOGTRACE(...) SPDLOGWRAPPER(spdlog::level::trace, __VA_ARGS__) -#define LOGDEBUG(...) SPDLOGWRAPPER(spdlog::level::debug, __VA_ARGS__) -#endif -#define LOGINFO(...) SPDLOGWRAPPER(spdlog::level::info, __VA_ARGS__) -#define LOGWARN(...) SPDLOGWRAPPER(spdlog::level::warn, __VA_ARGS__) -#define LOGERROR(...) SPDLOGWRAPPER(spdlog::level::err, __VA_ARGS__) -#define LOGCRITICAL(...) SPDLOGWRAPPER(spdlog::level::critical, __VA_ARGS__) - - -//=========================================================================== -// -// ログ -// -//=========================================================================== -class Log -{ -public: - enum loglevel { - Detail, // 詳細レベル - Normal, // 通常レベル - Warning, // 警告レベル - Debug // デバッグレベル - }; +#define SPDLOGWRAPPER(loglevel, ...) \ +{ \ + char logbuf[_MAX_FNAME*2]; \ + snprintf(logbuf, sizeof(logbuf), __VA_ARGS__); \ + spdlog::log(loglevel, logbuf); \ }; -#endif // log_h +#define LOGTRACE(...) SPDLOGWRAPPER(spdlog::level::trace, __VA_ARGS__) +#define LOGDEBUG(...) SPDLOGWRAPPER(spdlog::level::debug, __VA_ARGS__) +#define LOGINFO(...) SPDLOGWRAPPER(spdlog::level::info, __VA_ARGS__) +#define LOGWARN(...) SPDLOGWRAPPER(spdlog::level::warn, __VA_ARGS__) +#define LOGERROR(...) SPDLOGWRAPPER(spdlog::level::err, __VA_ARGS__) +#define LOGCRITICAL(...) SPDLOGWRAPPER(spdlog::level::critical, __VA_ARGS__) + +#endif diff --git a/src/raspberrypi/os.h b/src/raspberrypi/os.h index e1da949e..4d6a1a34 100644 --- a/src/raspberrypi/os.h +++ b/src/raspberrypi/os.h @@ -5,10 +5,11 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS +// Copyright (C) 2020 akuker // // Imported NetBSD support and some optimisation patch by Rin Okuyama. // -// [ OS固有 ] +// [ OS related definitions ] // //--------------------------------------------------------------------------- @@ -34,19 +35,16 @@ // //--------------------------------------------------------------------------- -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include #include #include -#include -#include -#include #include #include #include @@ -55,24 +53,16 @@ #include #include -#ifndef BAREMETAL #include #include -#include +#include #include #include #include #include -#include -#else -#include -#define htonl(_x) __htonl(_x) -#define htons(_x) __htons(_x) -#define ntohl(_x) __ntohl(_x) -#define ntohs(_x) __ntohs(_x) -#endif // BAREMETAL #if defined(__linux__) +#include #include #include #elif defined(__NetBSD__) @@ -86,18 +76,9 @@ //--------------------------------------------------------------------------- // -// 基本マクロ +// Basic Macros // //--------------------------------------------------------------------------- -#undef FASTCALL -#define FASTCALL - -#undef CDECL -#define CDECL - -#undef INLINE -#define INLINE - #if !defined(ASSERT) #if !defined(NDEBUG) #define ASSERT(cond) assert(cond) @@ -118,18 +99,14 @@ //--------------------------------------------------------------------------- // -// 基本型定義 +// Basic Type Definitions // //--------------------------------------------------------------------------- typedef unsigned char BYTE; -typedef unsigned short WORD; -typedef unsigned long DWORD; -typedef unsigned long long QWORD; +typedef uint16_t WORD; +typedef uint32_t DWORD; typedef int BOOL; typedef char TCHAR; -typedef char *LPTSTR; -typedef const char *LPCTSTR; -typedef const char *LPCSTR; #if !defined(FALSE) #define FALSE 0 @@ -144,14 +121,8 @@ typedef const char *LPCSTR; #endif #define _MAX_PATH 260 -#define _MAX_DRIVE 3 #define _MAX_DIR 256 #define _MAX_FNAME 256 #define _MAX_EXT 256 -#define off64_t off_t - -#define xstrcasecmp strcasecmp -#define xstrncasecmp strncasecmp - #endif // os_h diff --git a/src/raspberrypi/os_integration/dhcpcd.conf.patch b/src/raspberrypi/os_integration/dhcpcd.conf.patch new file mode 100644 index 00000000..459f0ee0 --- /dev/null +++ b/src/raspberrypi/os_integration/dhcpcd.conf.patch @@ -0,0 +1,7 @@ +--- dhcpcd_orig.conf 2021-02-26 17:32:14.065284400 -0600 ++++ dhcpcd.conf 2021-02-26 17:32:30.925039567 -0600 +@@ -58,3 +58,4 @@ + #interface eth0 + #fallback static_eth0 + # ++denyinterfaces eth0 diff --git a/src/raspberrypi/os_integration/rascsi.service b/src/raspberrypi/os_integration/rascsi.service index d5072387..dcd84cb8 100644 --- a/src/raspberrypi/os_integration/rascsi.service +++ b/src/raspberrypi/os_integration/rascsi.service @@ -5,11 +5,18 @@ After=network.target [Service] Type=simple Restart=always -ExecStart=/usr/local/bin/rascsi -# Example: If you want to automatically attach a hard disk at startup, change -# the ExecStart line to: -# ExecStart=/usr/local/bin/rascsi -ID1 /home/pi/images/harddisk.hda -ExecStop=/usr/local/bin/rasctl -stop +ExecStart=/usr/local/bin/rascsi -r 7 +# Example 1: If you want to automatically attach a hard disk at startup, +# say an image called harddisk.hds on SCSI ID 1, change the ExecStart line to: +# +# ExecStart=/usr/local/bin/rascsi -ID1 /home/pi/images/harddisk.hds +# +# Example 2: If you want to reserve SCSI IDs to prevent usage, add '-r' followed by +# comma-separated SCSI ID numbers; for instance IDs 0 and 7: +# +# ExecStart=/usr/local/bin/rascsi -r 0,7 +# +ExecStop=/usr/local/bin/rasctl -X StandardOutput=syslog StandardError=syslog SyslogIdentifier=RASCSI diff --git a/src/raspberrypi/os_integration/rascsi_bridge b/src/raspberrypi/os_integration/rascsi_bridge new file mode 100644 index 00000000..923fb35e --- /dev/null +++ b/src/raspberrypi/os_integration/rascsi_bridge @@ -0,0 +1,13 @@ +# +# Defines the 'rascsi_bridge' bridge that connects the RaSCSI network +# interface (ex DaynaPort SCSI/Link) to the outside world. +# +# Depending upon your system configuration, you may need to update this +# file to change 'eth0' to your Ethernet interface +# +# This file should be place in /etc/network/interfaces.d + +auto rascsi_bridge +iface rascsi_bridge inet dhcp + bridge_ports eth0 + diff --git a/src/raspberrypi/protobuf_util.cpp b/src/raspberrypi/protobuf_util.cpp new file mode 100644 index 00000000..6c5554c6 --- /dev/null +++ b/src/raspberrypi/protobuf_util.cpp @@ -0,0 +1,155 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include +#include "os.h" +#include "log.h" +#include "rascsi_interface.pb.h" +#include "exceptions.h" +#include "protobuf_util.h" + +using namespace std; +using namespace rascsi_interface; + +#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ ) + +const string protobuf_util::GetParam(const PbCommand& command, const string& key) +{ + auto map = command.params(); + return map[key]; +} + +const string protobuf_util::GetParam(const PbDeviceDefinition& device, const string& key) +{ + auto map = device.params(); + return map[key]; +} + +void protobuf_util::AddParam(PbCommand& command, const string& key, const string& value) +{ + if (!key.empty() && !value.empty()) { + auto& map = *command.mutable_params(); + map[key] = value; + } +} + +void protobuf_util::AddParam(PbDevice& device, const string& key, const string& value) +{ + if (!key.empty() && !value.empty()) { + auto& map = *device.mutable_params(); + map[key] = value; + } +} + +void protobuf_util::AddParam(PbDeviceDefinition& device, const string& key, const string& value) +{ + if (!key.empty() && !value.empty()) { + auto& map = *device.mutable_params(); + map[key] = value; + } +} + +//--------------------------------------------------------------------------- +// +// Serialize/Deserialize protobuf message: Length followed by the actual data. +// Little endian is assumed. +// +//--------------------------------------------------------------------------- + +void protobuf_util::SerializeMessage(int fd, const google::protobuf::Message& message) +{ + string data; + message.SerializeToString(&data); + + // Write the size of the protobuf data as a header + int32_t size = data.length(); + if (write(fd, &size, sizeof(size)) != sizeof(size)) { + throw io_exception("Can't write protobuf message header"); + } + + // Write the actual protobuf data + if (write(fd, data.data(), size) != size) { + throw io_exception("Can't write protobuf message data"); + } +} + +void protobuf_util::DeserializeMessage(int fd, google::protobuf::Message& message) +{ + // Read the header with the size of the protobuf data + uint8_t header_buf[4]; + int bytes_read = ReadNBytes(fd, header_buf, sizeof(header_buf)); + if (bytes_read < (int)sizeof(header_buf)) { + return; + } + int32_t size = (header_buf[3] << 24) + (header_buf[2] << 16) + (header_buf[1] << 8) + header_buf[0]; + if (size <= 0) { + throw io_exception("Broken protobuf message header"); + } + + // Read the binary protobuf data + uint8_t data_buf[size]; + bytes_read = ReadNBytes(fd, data_buf, size); + if (bytes_read < size) { + throw io_exception("Missing protobuf message data"); + } + + // Create protobuf message + string data((const char *)data_buf, size); + message.ParseFromString(data); +} + +int protobuf_util::ReadNBytes(int fd, uint8_t *buf, int n) +{ + int offset = 0; + while (offset < n) { + ssize_t len = read(fd, buf + offset, n - offset); + if (!len) { + break; + } + + offset += len; + } + + return offset; +} + + +bool protobuf_util::ReturnStatus(int fd, bool status, const string msg) +{ + if (!status && !msg.empty()) { + LOGERROR("%s", msg.c_str()); + } + + if (fd == -1) { + if (!msg.empty()) { + if (status) { + FPRT(stderr, "Error: "); + FPRT(stderr, "%s", msg.c_str()); + FPRT(stderr, "\n"); + } + else { + FPRT(stdout, "%s", msg.c_str()); + FPRT(stderr, "\n"); + } + } + } + else { + PbResult result; + result.set_status(status); + result.set_msg(msg); + SerializeMessage(fd, result); + } + + return status; +} + +bool protobuf_util::ReturnStatus(int fd, bool status, const ostringstream& msg) +{ + return ReturnStatus(fd, status, msg.str()); +} diff --git a/src/raspberrypi/protobuf_util.h b/src/raspberrypi/protobuf_util.h new file mode 100644 index 00000000..8482fe16 --- /dev/null +++ b/src/raspberrypi/protobuf_util.h @@ -0,0 +1,34 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +// Helper methods for serializing/deserializing protobuf messages +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "google/protobuf/message.h" +#include "rascsi_interface.pb.h" +#include +#include + +using namespace std; +using namespace rascsi_interface; + +namespace protobuf_util +{ + const string GetParam(const PbCommand&, const string&); + const string GetParam(const PbDeviceDefinition&, const string&); + void AddParam(PbCommand&, const string&, const string&); + void AddParam(PbDevice&, const string&, const string&); + void AddParam(PbDeviceDefinition&, const string&, const string&); + void SerializeMessage(int, const google::protobuf::Message&); + void DeserializeMessage(int, google::protobuf::Message&); + int ReadNBytes(int, uint8_t *, int); + bool ReturnStatus(int, bool = true, const string = ""); + bool ReturnStatus(int, bool, const ostringstream&); +} diff --git a/src/raspberrypi/rascsi.1 b/src/raspberrypi/rascsi.1 deleted file mode 100644 index 9bd89f78..00000000 --- a/src/raspberrypi/rascsi.1 +++ /dev/null @@ -1,14 +0,0 @@ -.TH rascsi 1 -.SH NAME -rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins -.SH SYNOPSIS -.B rascsi -[\fB\-HDn\fR \fIfile\fR] ... -.SH DESCRIPTION -.B corrupt -modifies files by toggling a randomly chosen bit. -.SH OPTIONS -.TP -.BR \-n ", " \-\-bits =\fIBITS\fR -Set the number of bits to modify. -Default is one bit. \ No newline at end of file diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 8fc44ae1..e0cdd799 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -5,27 +5,49 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS +// Copyright (C) 2020-2021 Contributors to the RaSCSI project // [ RaSCSI main ] // //--------------------------------------------------------------------------- +#include "rascsi.h" #include "os.h" -#include "xm6.h" #include "filepath.h" #include "fileio.h" -#include "devices/disk.h" -#include "devices/sasihd.h" -#include "devices/scsihd.h" -#include "devices/scsihd_apple.h" -#include "devices/scsihd_nec.h" -#include "devices/scsicd.h" -#include "devices/scsimo.h" -#include "devices/scsi_host_bridge.h" #include "controllers/scsidev_ctrl.h" #include "controllers/sasidev_ctrl.h" +#include "devices/device_factory.h" +#include "devices/device.h" +#include "devices/disk.h" +#include "devices/scsi_host_bridge.h" +#include "devices/scsi_daynaport.h" +#include "devices/file_support.h" #include "gpiobus.h" +#include "exceptions.h" +#include "protobuf_util.h" #include "rascsi_version.h" +#include "rascsi_response.h" +#include "rasutil.h" +#include "rascsi_image.h" +#include "rascsi_interface.pb.h" #include "spdlog/spdlog.h" +#include "spdlog/sinks/stdout_color_sinks.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace spdlog; +using namespace rascsi_interface; +using namespace ras_util; +using namespace protobuf_util; //--------------------------------------------------------------------------- // @@ -33,32 +55,29 @@ // //--------------------------------------------------------------------------- #define CtrlMax 8 // Maximum number of SCSI controllers -#define UnitNum 2 // Number of units around controller -#ifdef BAREMETAL -#define FPRT(fp, ...) printf( __VA_ARGS__ ) -#else +#define UnitNum SASIDEV::UnitMax // Number of units around controller #define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ ) -#endif // BAREMETAL //--------------------------------------------------------------------------- // // Variable declarations // //--------------------------------------------------------------------------- -static volatile BOOL running; // Running flag -static volatile BOOL active; // Processing flag -SASIDEV *ctrl[CtrlMax]; // Controller -Disk *disk[CtrlMax * UnitNum]; // Disk +static volatile bool running; // Running flag +static volatile bool active; // Processing flag +vector controllers(CtrlMax); // Controllers +vector devices(CtrlMax * UnitNum); // Disks GPIOBUS *bus; // GPIO Bus -#ifdef BAREMETAL -FATFS fatfs; // FatFS -#else int monsocket; // Monitor Socket pthread_t monthread; // Monitor Thread +pthread_mutex_t ctrl_mutex; // Semaphore for the ctrl array static void *MonThread(void *param); -#endif // BAREMETAL +string current_log_level; // Some versions of spdlog do not support get_log_level() +set reserved_ids; +DeviceFactory& device_factory = DeviceFactory::instance(); +RascsiImage rascsi_image; +RascsiResponse rascsi_response(&device_factory, &rascsi_image); -#ifndef BAREMETAL //--------------------------------------------------------------------------- // // Signal Processing @@ -67,9 +86,8 @@ static void *MonThread(void *param); void KillHandler(int sig) { // Stop instruction - running = FALSE; + running = false; } -#endif // BAREMETAL //--------------------------------------------------------------------------- // @@ -85,6 +103,7 @@ void Banner(int argc, char* argv[]) __TIME__); FPRT(stdout,"Powered by XM6 TypeG Technology / "); FPRT(stdout,"Copyright (C) 2016-2020 GIMONS\n"); + FPRT(stdout,"Copyright (C) 2020-2021 Contributors to the RaSCSI project\n"); FPRT(stdout,"Connect type : %s\n", CONNECT_DESC); if ((argc > 1 && strcmp(argv[1], "-h") == 0) || @@ -95,20 +114,18 @@ void Banner(int argc, char* argv[]) FPRT(stdout," FILE is disk image file.\n\n"); FPRT(stdout,"Usage: %s [-HDn FILE] ...\n\n", argv[0]); FPRT(stdout," n is X68000 SASI HD number(0-15).\n"); - FPRT(stdout," FILE is disk image file.\n\n"); + FPRT(stdout," FILE is disk image file, \"daynaport\", or \"bridge\".\n\n"); FPRT(stdout," Image type is detected based on file extension.\n"); - FPRT(stdout," hdf : SASI HD image(XM6 SASI HD image)\n"); - FPRT(stdout," hds : SCSI HD image(XM6 SCSI HD image)\n"); - FPRT(stdout," hdn : SCSI HD image(NEC GENUINE)\n"); - FPRT(stdout," hdi : SCSI HD image(Anex86 HD image)\n"); - FPRT(stdout," nhd : SCSI HD image(T98Next HD image)\n"); - FPRT(stdout," hda : SCSI HD image(APPLE GENUINE)\n"); - FPRT(stdout," mos : SCSI MO image(XM6 SCSI MO image)\n"); - FPRT(stdout," iso : SCSI CD image(ISO 9660 image)\n"); + FPRT(stdout," hdf : SASI HD image (XM6 SASI HD image)\n"); + FPRT(stdout," hds : SCSI HD image (Non-removable generic SCSI HD image)\n"); + FPRT(stdout," hdr : SCSI HD image (Removable generic SCSI HD image)\n"); + FPRT(stdout," hdn : SCSI HD image (NEC GENUINE)\n"); + FPRT(stdout," hdi : SCSI HD image (Anex86 HD image)\n"); + FPRT(stdout," nhd : SCSI HD image (T98Next HD image)\n"); + FPRT(stdout," mos : SCSI MO image (MO image)\n"); + FPRT(stdout," iso : SCSI CD image (ISO 9660 image)\n"); -#ifndef BAREMETAL - exit(0); -#endif // BAREMETAL + exit(EXIT_SUCCESS); } } @@ -117,33 +134,36 @@ void Banner(int argc, char* argv[]) // Initialization // //--------------------------------------------------------------------------- -BOOL Init() -{ - int i; -#ifndef BAREMETAL - struct sockaddr_in server; - int yes; +bool InitService(int port) +{ + int result = pthread_mutex_init(&ctrl_mutex,NULL); + if (result != EXIT_SUCCESS){ + LOGERROR("Unable to create a mutex. Error code: %d", result); + return false; + } // Create socket for monitor + struct sockaddr_in server; monsocket = socket(PF_INET, SOCK_STREAM, 0); memset(&server, 0, sizeof(server)); server.sin_family = PF_INET; - server.sin_port = htons(6868); + server.sin_port = htons(port); server.sin_addr.s_addr = htonl(INADDR_ANY); // allow address reuse - yes = 1; - if (setsockopt( - monsocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0){ - return FALSE; + int yes = 1; + if (setsockopt(monsocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { + return false; } + signal(SIGPIPE, SIG_IGN); + // Bind if (bind(monsocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { - FPRT(stderr, "Error : Already running?\n"); - return FALSE; + FPRT(stderr, "Error: Already running?\n"); + return false; } // Create Monitor Thread @@ -151,42 +171,35 @@ BOOL Init() // Interrupt handler settings if (signal(SIGINT, KillHandler) == SIG_ERR) { - return FALSE; + return false; } if (signal(SIGHUP, KillHandler) == SIG_ERR) { - return FALSE; + return false; } if (signal(SIGTERM, KillHandler) == SIG_ERR) { - return FALSE; + return false; } -#endif // BAREMETAL + running = false; + active = false; + + return true; +} + +bool InitBus() +{ // GPIOBUS creation bus = new GPIOBUS(); // GPIO Initialization if (!bus->Init()) { - return FALSE; + return false; } // Bus Reset bus->Reset(); - // Controller initialization - for (i = 0; i < CtrlMax; i++) { - ctrl[i] = NULL; - } - - // Disk Initialization - for (i = 0; i < CtrlMax; i++) { - disk[i] = NULL; - } - - // Other - running = FALSE; - active = FALSE; - - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -196,36 +209,36 @@ BOOL Init() //--------------------------------------------------------------------------- void Cleanup() { - int i; - // Delete the disks - for (i = 0; i < CtrlMax * UnitNum; i++) { - if (disk[i]) { - delete disk[i]; - disk[i] = NULL; + for (auto it = devices.begin(); it != devices.end(); ++it) { + if (*it) { + delete *it; + *it = NULL; } } // Delete the Controllers - for (i = 0; i < CtrlMax; i++) { - if (ctrl[i]) { - delete ctrl[i]; - ctrl[i] = NULL; + for (auto it = controllers.begin(); it != controllers.end(); ++it) { + if (*it) { + delete *it; + *it = NULL; } } // Cleanup the Bus - bus->Cleanup(); + if (bus) { + bus->Cleanup(); - // Discard the GPIOBUS object - delete bus; + // Discard the GPIOBUS object + delete bus; + } -#ifndef BAREMETAL // Close the monitor socket if (monsocket >= 0) { close(monsocket); } -#endif // BAREMETAL + + pthread_mutex_destroy(&ctrl_mutex); } //--------------------------------------------------------------------------- @@ -235,12 +248,10 @@ void Cleanup() //--------------------------------------------------------------------------- void Reset() { - int i; - // Reset all of the controllers - for (i = 0; i < CtrlMax; i++) { - if (ctrl[i]) { - ctrl[i]->Reset(); + for (const auto& controller : controllers) { + if (controller) { + controller->Reset(); } } @@ -248,123 +259,53 @@ void Reset() bus->Reset(); } -//--------------------------------------------------------------------------- -// -// List Devices -// -//--------------------------------------------------------------------------- -void ListDevice(FILE *fp) -{ - int i; - int id; - int un; - Disk *pUnit; - Filepath filepath; - BOOL find; - char type[5]; - - find = FALSE; - type[4] = 0; - for (i = 0; i < CtrlMax * UnitNum; i++) { - // Initialize ID and unit number - id = i / UnitNum; - un = i % UnitNum; - pUnit = disk[i]; - - // skip if unit does not exist or null disk - if (pUnit == NULL || pUnit->IsNULL()) { - continue; - } - - // Output the header - if (!find) { - FPRT(fp, "\n"); - FPRT(fp, "+----+----+------+-------------------------------------\n"); - FPRT(fp, "| ID | UN | TYPE | DEVICE STATUS\n"); - FPRT(fp, "+----+----+------+-------------------------------------\n"); - find = TRUE; - } - - // ID,UNIT,Type,Device Status - type[0] = (char)(pUnit->GetID() >> 24); - type[1] = (char)(pUnit->GetID() >> 16); - type[2] = (char)(pUnit->GetID() >> 8); - type[3] = (char)(pUnit->GetID()); - FPRT(fp, "| %d | %d | %s | ", id, un, type); - - // mount status output - if (pUnit->GetID() == MAKEID('S', 'C', 'B', 'R')) { - FPRT(fp, "%s", "HOST BRIDGE"); - } else { - pUnit->GetPath(filepath); - FPRT(fp, "%s", - (pUnit->IsRemovable() && !pUnit->IsReady()) ? - "NO MEDIA" : filepath.GetPath()); - } - - // Write protection status - if (pUnit->IsRemovable() && pUnit->IsReady() && pUnit->IsWriteP()) { - FPRT(fp, "(WRITEPROTECT)"); - } - - // Goto the next line - FPRT(fp, "\n"); - } - - // If there is no controller, find will be null - if (!find) { - FPRT(fp, "No images currently attached.\n"); - return; - } - - FPRT(fp, "+----+----+------+-------------------------------------\n"); -} - //--------------------------------------------------------------------------- // // Controller Mapping // //--------------------------------------------------------------------------- -void MapControler(FILE *fp, Disk **map) +bool MapController(Device **map) { - int i; - int j; - int unitno; - int sasi_num; - int scsi_num; + assert(bus); + + bool status = true; + + // Take ownership of the ctrl data structure + pthread_mutex_lock(&ctrl_mutex); // Replace the changed unit - for (i = 0; i < CtrlMax; i++) { - for (j = 0; j < UnitNum; j++) { - unitno = i * UnitNum + j; - if (disk[unitno] != map[unitno]) { + for (size_t i = 0; i < controllers.size(); i++) { + for (int j = 0; j < UnitNum; j++) { + int unitno = i * UnitNum + j; + if (devices[unitno] != map[unitno]) { // Check if the original unit exists - if (disk[unitno]) { + if (devices[unitno]) { // Disconnect it from the controller - if (ctrl[i]) { - ctrl[i]->SetUnit(j, NULL); + if (controllers[i]) { + controllers[i]->SetUnit(j, NULL); } // Free the Unit - delete disk[unitno]; + delete devices[unitno]; } // Setup a new unit - disk[unitno] = map[unitno]; + devices[unitno] = map[unitno]; } } } // Reconfigure all of the controllers - for (i = 0; i < CtrlMax; i++) { + int i = 0; + for (auto it = controllers.begin(); it != controllers.end(); ++i, ++it) { // Examine the unit configuration - sasi_num = 0; - scsi_num = 0; - for (j = 0; j < UnitNum; j++) { - unitno = i * UnitNum + j; + int sasi_num = 0; + int scsi_num = 0; + for (int j = 0; j < UnitNum; j++) { + int unitno = i * UnitNum + j; // branch by unit type - if (disk[unitno]) { - if (disk[unitno]->IsSASI()) { + if (devices[unitno]) { + if (devices[unitno]->IsSASIHD()) { // Drive is SASI, so increment SASI count sasi_num++; } else { @@ -374,23 +315,23 @@ void MapControler(FILE *fp, Disk **map) } // Remove the unit - if (ctrl[i]) { - ctrl[i]->SetUnit(j, NULL); + if (*it) { + (*it)->SetUnit(j, NULL); } } // If there are no units connected - if (sasi_num == 0 && scsi_num == 0) { - if (ctrl[i]) { - delete ctrl[i]; - ctrl[i] = NULL; + if (!sasi_num && !scsi_num) { + if (*it) { + delete *it; + *it = NULL; continue; } } // Mixture of SCSI and SASI if (sasi_num > 0 && scsi_num > 0) { - FPRT(fp, "Error : SASI and SCSI can't be mixed\n"); + status = false; continue; } @@ -398,41 +339,459 @@ void MapControler(FILE *fp, Disk **map) // Only SASI Unit(s) // Release the controller if it is not SASI - if (ctrl[i] && !ctrl[i]->IsSASI()) { - delete ctrl[i]; - ctrl[i] = NULL; + if (*it && !(*it)->IsSASI()) { + delete *it; + *it = NULL; } // Create a new SASI controller - if (!ctrl[i]) { - ctrl[i] = new SASIDEV(); - ctrl[i]->Connect(i, bus); + if (!*it) { + *it = new SASIDEV(); + (*it)->Connect(i, bus); } } else { // Only SCSI Unit(s) // Release the controller if it is not SCSI - if (ctrl[i] && !ctrl[i]->IsSCSI()) { - delete ctrl[i]; - ctrl[i] = NULL; + if (*it && !(*it)->IsSCSI()) { + delete *it; + *it = NULL; } // Create a new SCSI controller - if (!ctrl[i]) { - ctrl[i] = new SCSIDEV(); - ctrl[i]->Connect(i, bus); + if (!*it) { + *it = new SCSIDEV(); + (*it)->Connect(i, bus); } } // connect all units - for (j = 0; j < UnitNum; j++) { - unitno = i * UnitNum + j; - if (disk[unitno]) { + for (int j = 0; j < UnitNum; j++) { + int unitno = i * UnitNum + j; + if (devices[unitno]) { // Add the unit connection - ctrl[i]->SetUnit(j, disk[unitno]); + (*it)->SetUnit(j, (static_cast(devices[unitno]))); } } } + pthread_mutex_unlock(&ctrl_mutex); + + return status; +} + +string ValidateLunSetup(const PbCommand& command, const vector& existing_devices) +{ + // Mapping of available LUNs (bit vector) to devices + map luns; + + // Collect LUN vectors of new devices + for (const auto& device : command.devices()) { + luns[device.id()] |= 1 << device.unit(); + } + + // Collect LUN vectors of existing devices + for (auto const& device : existing_devices) { + if (device) { + luns[device->GetId()] |= 1 << device->GetLun(); + } + } + + // LUNs must be consecutive + for (auto const& [id, lun]: luns) { + bool is_consecutive = false; + + uint32_t lun_vector = 0; + for (int i = 0; i < 32; i++) { + lun_vector |= 1 << i; + + if (lun == lun_vector) { + is_consecutive = true; + break; + } + } + + if (!is_consecutive) { + ostringstream error; + error << "LUNs for device ID " << id << " are not consecutive"; + + return error.str(); + } + } + + return ""; +} + +bool SetLogLevel(const string& log_level) +{ + if (log_level == "trace") { + set_level(level::trace); + } + else if (log_level == "debug") { + set_level(level::debug); + } + else if (log_level == "info") { + set_level(level::info); + } + else if (log_level == "warn") { + set_level(level::warn); + } + else if (log_level == "err") { + set_level(level::err); + } + else if (log_level == "critical") { + set_level(level::critical); + } + else if (log_level == "off") { + set_level(level::off); + } + else { + return false; + } + + current_log_level = log_level; + + return true; +} + +void LogDevices(const string& devices) +{ + stringstream ss(devices); + string line; + + while (getline(ss, line, '\n')) { + LOGINFO("%s", line.c_str()); + } +} + +string SetReservedIds(const string& ids) +{ + list ids_to_reserve; + stringstream ss(ids); + string id; + while (getline(ss, id, ',')) { + if (!id.empty()) { + ids_to_reserve.push_back(id); + } + } + + set reserved; + for (string id_to_reserve : ids_to_reserve) { + int id; + if (!GetAsInt(id_to_reserve, id) || id > 7) { + return "Invalid ID " + id_to_reserve; + } + + if (devices[id * UnitNum]) { + return "ID " + id_to_reserve + " is currently in use"; + } + + reserved.insert(id); + } + + reserved_ids = reserved; + + if (!reserved_ids.empty()) { + ostringstream s; + bool isFirst = true; + for (auto const& reserved_id : reserved_ids) { + if (!isFirst) { + s << ", "; + } + isFirst = false; + s << reserved_id; + } + + LOGINFO("Reserved ID(s) set to %s", s.str().c_str()); + } + else { + LOGINFO("Cleared reserved IDs"); + } + + return ""; +} + +void DetachAll() +{ + Device *map[devices.size()]; + for (size_t i = 0; i < devices.size(); i++) { + map[i] = NULL; + } + + if (MapController(map)) { + LOGINFO("Detached all devices"); + } + + FileSupport::UnreserveAll(); +} + +bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dryRun) +{ + const int id = pb_device.id(); + const int unit = pb_device.unit(); + const PbDeviceType type = pb_device.type(); + ostringstream error; + + if (map[id * UnitNum + unit]) { + error << "Duplicate ID " << id << ", unit " << unit; + return ReturnStatus(fd, false, error); + } + + string filename = GetParam(pb_device, "file"); + + // Create a new device, based on the provided type or filename + Device *device = device_factory.CreateDevice(type, filename); + if (!device) { + if (type == UNDEFINED) { + return ReturnStatus(fd, false, "Device type required for unknown extension of file '" + filename + "'"); + } + else { + return ReturnStatus(fd, false, "Unknown device type " + PbDeviceType_Name(type)); + } + } + + int supported_luns = device->GetSupportedLuns(); + if (unit >= supported_luns) { + delete device; + + error << "Invalid unit " << unit << " for device type " << PbDeviceType_Name(type); + if (supported_luns == 1) { + error << " (0)"; + } + else { + error << " (0-" << (supported_luns -1) << ")"; + } + return ReturnStatus(fd, false, error.str()); + } + + // If no filename was provided the medium is considered removed + FileSupport *file_support = dynamic_cast(device); + if (file_support) { + device->SetRemoved(filename.empty()); + } + else { + device->SetRemoved(false); + } + + device->SetId(id); + device->SetLun(unit); + + try { + if (!pb_device.vendor().empty()) { + device->SetVendor(pb_device.vendor()); + } + if (!pb_device.product().empty()) { + device->SetProduct(pb_device.product()); + } + if (!pb_device.revision().empty()) { + device->SetRevision(pb_device.revision()); + } + } + catch(const illegal_argument_exception& e) { + return ReturnStatus(fd, false, e.getmsg()); + } + + if (pb_device.block_size()) { + Disk *disk = dynamic_cast(device); + if (disk && disk->IsSectorSizeConfigurable()) { + if (!disk->SetConfiguredSectorSize(pb_device.block_size())) { + delete device; + + error << "Invalid block size " << pb_device.block_size() << " bytes"; + return ReturnStatus(fd, false, error); + } + } + else { + delete device; + + return ReturnStatus(fd, false, "Block size is not configurable for device type " + PbDeviceType_Name(type)); + } + } + + // File check (type is HD, for removable media drives, CD and MO the medium (=file) may be inserted later) + if (file_support && !device->IsRemovable() && filename.empty()) { + delete device; + + return ReturnStatus(fd, false, "Device type " + PbDeviceType_Name(type) + " requires a filename"); + } + + Filepath filepath; + if (file_support && !filename.empty()) { + filepath.SetPath(filename.c_str()); + string initial_filename = filepath.GetPath(); + + try { + try { + file_support->Open(filepath); + } + catch(const file_not_found_exception&) { + // If the file does not exist search for it in the default image folder + filepath.SetPath(string(rascsi_image.GetDefaultImageFolder() + "/" + filename).c_str()); + file_support->Open(filepath); + } + } + catch(const io_exception& e) { + delete device; + + return ReturnStatus(fd, false, "Tried to open an invalid or non-existing file '" + initial_filename + "': " + e.getmsg()); + } + + int id; + int unit; + if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) { + delete device; + + error << "Image file '" << filename << "' is already used by ID " << id << ", unit " << unit; + return ReturnStatus(fd, false, error); + } + + file_support->ReserveFile(filepath, device->GetId(), device->GetLun()); + } + + // Only non read-only devices support protect/unprotect + // This operation must not be executed before Open() because Open() overrides some settings. + if (device->IsProtectable() && !device->IsReadOnly()) { + device->SetProtected(pb_device.protected_()); + } + + // Stop the dry run here, before permanently modifying something + if (dryRun) { + delete device; + + return true; + } + + std::map params = { pb_device.params().begin(), pb_device.params().end() }; + if (!device->Init(params)) { + error << "Initialization of " << device->GetType() << " device, ID " << id << ", unit " << unit << " failed"; + + delete device; + + return ReturnStatus(fd, false, error); + } + + // Replace with the newly created unit + map[id * UnitNum + unit] = device; + + // Re-map the controller + if (MapController(map)) { + ostringstream msg; + msg << "Attached "; + if (device->IsReadOnly()) { + msg << "read-only "; + } + else if (device->IsProtectable() && device->IsProtected()) { + msg << "protected "; + } + msg << device->GetType() << " device, ID " << id << ", unit " << unit; + LOGINFO("%s", msg.str().c_str()); + + return true; + } + + return ReturnStatus(fd, false, "SASI and SCSI can't be mixed"); +} + +bool Detach(int fd, Device *device, Device *map[], bool dryRun) +{ + if (!dryRun) { + for (auto const& d : devices) { + // Detach all LUNs equal to or higher than the LUN specified + if (d && d->GetId() == device->GetId() && d->GetLun() >= device->GetLun()) { + map[d->GetId() * UnitNum + d->GetLun()] = NULL; + + FileSupport *file_support = dynamic_cast(d); + if (file_support) { + file_support->UnreserveFile(); + } + + LOGINFO("Detached %s device with ID %d, unit %d", d->GetType().c_str(), d->GetId(), d->GetLun()); + } + } + + // Re-map the controller + MapController(map); + } + + return true; +} + +bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dryRun) +{ + if (!device->IsRemoved()) { + return ReturnStatus(fd, false, "Existing medium must first be ejected"); + } + + if (!pb_device.vendor().empty() || !pb_device.product().empty() || !pb_device.revision().empty()) { + return ReturnStatus(fd, false, "Once set the device name cannot be changed anymore"); + } + + string filename = GetParam(pb_device, "file"); + if (filename.empty()) { + return ReturnStatus(fd, false, "Missing filename for " + PbOperation_Name(INSERT)); + } + + if (dryRun) { + return true; + } + + LOGINFO("Insert %sfile '%s' requested into %s ID %d, unit %d", pb_device.protected_() ? "protected " : "", + filename.c_str(), device->GetType().c_str(), pb_device.id(), pb_device.unit()); + + int id; + int unit; + Filepath filepath; + filepath.SetPath(filename.c_str()); + string initial_filename = filepath.GetPath(); + if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) { + ostringstream error; + error << "Image file '" << filename << "' is already used by ID " << id << ", unit " << unit; + return ReturnStatus(fd, false, error); + } + + if (pb_device.block_size()) { + Disk *disk = dynamic_cast(device); + if (disk && disk->IsSectorSizeConfigurable()) { + if (!disk->SetConfiguredSectorSize(pb_device.block_size())) { + ostringstream error; + error << "Invalid block size " << pb_device.block_size() << " bytes"; + return ReturnStatus(fd, false, error); + } + } + else { + return ReturnStatus(fd, false, "Block size is not configurable for device type " + device->GetType()); + } + } + + FileSupport *file_support = dynamic_cast(device); + try { + try { + file_support->Open(filepath); + } + catch(const file_not_found_exception&) { + // If the file does not exist search for it in the default image folder + filepath.SetPath((rascsi_image.GetDefaultImageFolder() + "/" + filename).c_str()); + file_support->Open(filepath); + } + } + catch(const io_exception& e) { + return ReturnStatus(fd, false, "Tried to open an invalid or non-existing file '" + initial_filename + "': " + e.getmsg()); + } + file_support->ReserveFile(filepath, device->GetId(), device->GetLun()); + + // Only non read-only devices support protect/unprotect. + // This operation must not be executed before Open() because Open() overrides some settings. + if (device->IsProtectable() && !device->IsReadOnly()) { + device->SetProtected(pb_device.protected_()); + } + + return true; +} + +void TerminationHandler(int signum) +{ + DetachAll(); + + exit(signum); } //--------------------------------------------------------------------------- @@ -440,186 +799,284 @@ void MapControler(FILE *fp, Disk **map) // Command Processing // //--------------------------------------------------------------------------- -BOOL ProcessCmd(FILE *fp, int id, int un, int cmd, int type, char *file) -{ - Disk *map[CtrlMax * UnitNum]; - int len; - char *ext; - Filepath filepath; - Disk *pUnit; - // Copy the Unit List - memcpy(map, disk, sizeof(disk)); +bool ProcessCmd(int fd, const PbDeviceDefinition& pb_device, const PbCommand& command, bool dryRun) +{ + ostringstream error; + + const int id = pb_device.id(); + const int unit = pb_device.unit(); + const PbDeviceType type = pb_device.type(); + const PbOperation operation = command.operation(); + const map params = { command.params().begin(), command.params().end() }; + + ostringstream s; + s << (dryRun ? "Validating: " : "Executing: "); + s << "operation=" << PbOperation_Name(operation); + + if (!params.empty()) { + s << ", command params="; + bool isFirst = true; + for (const auto& param: params) { + if (!isFirst) { + s << ", "; + } + isFirst = false; + s << "'" << param.first << "=" << param.second << "'"; + } + } + + s << ", device id=" << id << ", unit=" << unit << ", type=" << PbDeviceType_Name(type); + + if (pb_device.params_size()) { + s << ", device params="; + bool isFirst = true; + for (const auto& param: pb_device.params()) { + if (!isFirst) { + s << ", "; + } + isFirst = false; + s << "'" << param.first << "=" << param.second << "'"; + } + } + + s << ", vendor='" << pb_device.vendor() << "', product='" << pb_device.product() + << "', revision='" << pb_device.revision() + << "', block size=" << pb_device.block_size(); + LOGINFO("%s", s.str().c_str()); // Check the Controller Number if (id < 0 || id >= CtrlMax) { - FPRT(fp, "Error : Invalid ID\n"); - return FALSE; + error << "Invalid device ID " << id << " (0-" << CtrlMax - 1 << ")"; + return ReturnStatus(fd, false, error); + } + + if (operation == ATTACH && reserved_ids.find(id) != reserved_ids.end()) { + error << "Device ID " << id << " is reserved"; + return ReturnStatus(fd, false, error); } // Check the Unit Number - if (un < 0 || un >= UnitNum) { - FPRT(fp, "Error : Invalid unit number\n"); - return FALSE; + if (unit < 0 || unit >= UnitNum) { + error << "Invalid unit " << unit << " (0-" << UnitNum - 1 << ")"; + return ReturnStatus(fd, false, error); } - // Connect Command - if (cmd == 0) { // ATTACH - // Distinguish between SASI and SCSI - ext = NULL; - pUnit = NULL; - if (type == 0) { - // Passed the check - if (!file) { - return FALSE; - } - - // Check that command is at least 5 characters long - len = strlen(file); - if (len < 5) { - return FALSE; - } - - // Check the extension - if (file[len - 4] != '.') { - return FALSE; - } - - // If the extension is not SASI type, replace with SCSI - ext = &file[len - 3]; - if (xstrcasecmp(ext, "hdf") != 0) { - type = 1; - } - } - - // Create a new drive, based upon type - switch (type) { - case 0: // HDF - pUnit = new SASIHD(); - break; - case 1: // HDS/HDN/HDI/NHD/HDA - if (ext == NULL) { - break; - } - if (xstrcasecmp(ext, "hdn") == 0 || - xstrcasecmp(ext, "hdi") == 0 || xstrcasecmp(ext, "nhd") == 0) { - pUnit = new SCSIHD_NEC(); - } else if (xstrcasecmp(ext, "hda") == 0) { - pUnit = new SCSIHD_APPLE(); - } else { - pUnit = new SCSIHD(); - } - break; - case 2: // MO - pUnit = new SCSIMO(); - break; - case 3: // CD - pUnit = new SCSICD(); - break; - case 4: // BRIDGE - pUnit = new SCSIBR(); - break; - default: - FPRT(fp, "Error : Invalid device type\n"); - return FALSE; - } - - // drive checks files - if (type <= 1 || (type <= 3 && xstrcasecmp(file, "-") != 0)) { - // Set the Path - filepath.SetPath(file); - - // Open the file path - if (!pUnit->Open(filepath)) { - FPRT(fp, "Error : File open error [%s]\n", file); - delete pUnit; - return FALSE; - } - } - - // Set the cache to write-through - pUnit->SetCacheWB(FALSE); - - // Replace with the newly created unit - map[id * UnitNum + un] = pUnit; - - // Re-map the controller - MapControler(fp, map); - return TRUE; + // Copy the devices + Device *map[devices.size()]; + for (size_t i = 0; i < devices.size(); i++) { + map[i] = devices[i]; } - // Is this a valid command? - if (cmd > 4) { - FPRT(fp, "Error : Invalid command\n"); - return FALSE; + if (operation == ATTACH) { + return Attach(fd, pb_device, map, dryRun); } // Does the controller exist? - if (ctrl[id] == NULL) { - FPRT(fp, "Error : No such device\n"); - return FALSE; + if (!dryRun && !controllers[id]) { + error << "Received a command for non-existing ID " << id; + return ReturnStatus(fd, false, error); } // Does the unit exist? - pUnit = disk[id * UnitNum + un]; - if (pUnit == NULL) { - FPRT(fp, "Error : No such device\n"); - return FALSE; + Device *device = devices[id * UnitNum + unit]; + if (!device) { + error << "Received a command for a non-existing device or unit, ID " << id << ", unit " << unit; + return ReturnStatus(fd, false, error); } - // Disconnect Command - if (cmd == 1) { // DETACH - // Free the existing unit - map[id * UnitNum + un] = NULL; - - // Re-map the controller - MapControler(fp, map); - return TRUE; + if (operation == DETACH) { + return Detach(fd, device, map, dryRun); } - // Valid only for MO or CD - if (pUnit->GetID() != MAKEID('S', 'C', 'M', 'O') && - pUnit->GetID() != MAKEID('S', 'C', 'C', 'D')) { - FPRT(fp, "Error : Operation denied(Deveice isn't removable)\n"); - return FALSE; + if ((operation == START || operation == STOP) && !device->IsStoppable()) { + return ReturnStatus(fd, false, PbOperation_Name(operation) + " operation denied (" + device->GetType() + " isn't stoppable)"); } - switch (cmd) { - case 2: // INSERT - // Set the file path - filepath.SetPath(file); + if ((operation == INSERT || operation == EJECT) && !device->IsRemovable()) { + return ReturnStatus(fd, false, PbOperation_Name(operation) + " operation denied (" + device->GetType() + " isn't removable)"); + } - // Open the file - if (!pUnit->Open(filepath)) { - FPRT(fp, "Error : File open error [%s]\n", file); - return FALSE; + if ((operation == PROTECT || operation == UNPROTECT) && !device->IsProtectable()) { + return ReturnStatus(fd, false, PbOperation_Name(operation) + " operation denied (" + device->GetType() + " isn't protectable)"); + } + if ((operation == PROTECT || operation == UNPROTECT) && !device->IsReady()) { + return ReturnStatus(fd, false, PbOperation_Name(operation) + " operation denied (" + device->GetType() + " isn't ready)"); + } + + switch (operation) { + case START: + if (!dryRun) { + LOGINFO("Start requested for %s ID %d, unit %d", device->GetType().c_str(), id, unit); + + if (!device->Start()) { + LOGWARN("Starting %s ID %d, unit %d failed", device->GetType().c_str(), id, unit); + } } break; - case 3: // EJECT - pUnit->Eject(TRUE); + case STOP: + if (!dryRun) { + LOGINFO("Stop requested for %s ID %d, unit %d", device->GetType().c_str(), id, unit); + + // STOP is idempotent + device->Stop(); + } break; - case 4: // PROTECT - if (pUnit->GetID() != MAKEID('S', 'C', 'M', 'O')) { - FPRT(fp, "Error : Operation denied(Deveice isn't MO)\n"); - return FALSE; + case INSERT: + return Insert(fd, pb_device, device, dryRun); + + case EJECT: + if (!dryRun) { + LOGINFO("Eject requested for %s ID %d, unit %d", device->GetType().c_str(), id, unit); + + if (!device->Eject(true)) { + LOGWARN("Ejecting %s ID %d, unit %d failed", device->GetType().c_str(), id, unit); + } } - pUnit->WriteP(!pUnit->IsWriteP()); break; + + case PROTECT: + if (!dryRun) { + LOGINFO("Write protection requested for %s ID %d, unit %d", device->GetType().c_str(), id, unit); + + // PROTECT is idempotent + device->SetProtected(true); + } + break; + + case UNPROTECT: + if (!dryRun) { + LOGINFO("Write unprotection requested for %s ID %d, unit %d", device->GetType().c_str(), id, unit); + + // UNPROTECT is idempotent + device->SetProtected(false); + } + break; + + case ATTACH: + case DETACH: + // The non dry-run case has been handled before the switch + assert(dryRun); + break; + + case NONE: + // Do nothing, just log + LOGTRACE("Received %s command", PbOperation_Name(operation).c_str()); + break; + default: - ASSERT(FALSE); - return FALSE; + return ReturnStatus(fd, false, "Unknown operation"); } - return TRUE; + return true; } -bool has_suffix(const char* string, const char* suffix) { - int string_len = strlen(string); - int suffix_len = strlen(suffix); - return (string_len >= suffix_len) - && (xstrcasecmp(string + (string_len - suffix_len), suffix) == 0); +bool ProcessCmd(const int fd, const PbCommand& command) +{ + switch (command.operation()) { + case DETACH_ALL: + DetachAll(); + return ReturnStatus(fd); + + case RESERVE_IDS: { + const string ids = GetParam(command, "ids"); + string error = SetReservedIds(ids); + if (!error.empty()) { + return ReturnStatus(fd, false, error); + } + + return ReturnStatus(fd); + } + + case CREATE_IMAGE: + return rascsi_image.CreateImage(fd, command); + + case DELETE_IMAGE: + return rascsi_image.DeleteImage(fd, command); + + case RENAME_IMAGE: + return rascsi_image.RenameImage(fd, command); + + case COPY_IMAGE: + return rascsi_image.CopyImage(fd, command); + + case PROTECT_IMAGE: + case UNPROTECT_IMAGE: + return rascsi_image.SetImagePermissions(fd, command); + + default: + // This is a device-specific command handled below + break; + } + + // Remember the list of reserved files, than run the dry run + const auto reserved_files = FileSupport::GetReservedFiles(); + for (const auto& device : command.devices()) { + if (!ProcessCmd(fd, device, command, true)) { + // Dry run failed, restore the file list + FileSupport::SetReservedFiles(reserved_files); + return false; + } + } + + // Restore the list of reserved files before proceeding + FileSupport::SetReservedFiles(reserved_files); + + string result = ValidateLunSetup(command, devices); + if (!result.empty()) { + return ReturnStatus(fd, false, result); + } + + for (const auto& device : command.devices()) { + if (!ProcessCmd(fd, device, command, false)) { + return false; + } + } + + // ATTACH and DETACH return the device list + if (fd != -1 && (command.operation() == ATTACH || command.operation() == DETACH)) { + // A new command with an empty device list is required here in order to return data for all devices + PbCommand command; + PbResult result; + rascsi_response.GetDevicesInfo(result, command, devices, UnitNum); + SerializeMessage(fd, result); + return true; + } + + return ReturnStatus(fd); +} + +bool ProcessId(const string id_spec, PbDeviceType type, int& id, int& unit) +{ + size_t separator_pos = id_spec.find(':'); + if (separator_pos == string::npos) { + int max_id = type == SAHD ? 16 : 8; + + if (!GetAsInt(id_spec, id) || id < 0 || id >= max_id) { + cerr << optarg << ": Invalid device ID (0-" << (max_id - 1) << ")" << endl; + return false; + } + + // Required for SASI ID/LUN handling backwards compatibility + unit = 0; + if (type == SAHD) { + unit = id % 2; + id /= 2; + } + } + else { + int max_unit = type == SAHD ? 2 : UnitNum; + + if (!GetAsInt(id_spec.substr(0, separator_pos), id) || id < 0 || id > 7 || + !GetAsInt(id_spec.substr(separator_pos + 1), unit) || unit < 0 || unit >= max_unit) { + cerr << optarg << ": Invalid unit (0-" << (max_unit - 1) << ")" << endl; + return false; + } + } + + return true; } //--------------------------------------------------------------------------- @@ -627,278 +1084,158 @@ bool has_suffix(const char* string, const char* suffix) { // Argument Parsing // //--------------------------------------------------------------------------- -#ifdef BAREMETAL -BOOL ParseConfig(int argc, char* argv[]) -{ - FRESULT fr; - FIL fp; - char line[512]; - int id; - int un; - int type; - char *argID; - char *argPath; - int len; - char *ext; - - // Mount the SD card - fr = f_mount(&fatfs, "", 1); - if (fr != FR_OK) { - FPRT(stderr, "Error : SD card mount failed.\n"); - return FALSE; - } - - // If there is no setting file, the processing is interrupted - fr = f_open(&fp, "rascsi.ini", FA_READ); - if (fr != FR_OK) { - return FALSE; - } - - // Start Decoding - - while (TRUE) { - // Get one Line - memset(line, 0x00, sizeof(line)); - if (f_gets(line, sizeof(line) -1, &fp) == NULL) { - break; - } - - // Delete the CR/LF - len = strlen(line); - while (len > 0) { - if (line[len - 1] != '\r' && line[len - 1] != '\n') { - break; - } - line[len - 1] = '\0'; - len--; - } - - // Get the ID and Path - argID = &line[0]; - argPath = &line[4]; - line[3] = '\0'; - - // Check if the line is an empty string - if (argID[0] == '\0' || argPath[0] == '\0') { - continue; - } - - if (strlen(argID) == 3 && xstrncasecmp(argID, "id", 2) == 0) { - // ID or ID Format - - // Check that the ID number is valid (0-7) - if (argID[2] < '0' || argID[2] > '7') { - FPRT(stderr, - "Error : Invalid argument(IDn n=0-7) [%c]\n", argID[2]); - goto parse_error; - } - - // The ID unit is good - id = argID[2] - '0'; - un = 0; - } else if (xstrncasecmp(argID, "hd", 2) == 0) { - // HD or HD format - - if (strlen(argID) == 3) { - // Check that the HD number is valid (0-9) - if (argID[2] < '0' || argID[2] > '9') { - FPRT(stderr, - "Error : Invalid argument(HDn n=0-15) [%c]\n", argID[2]); - goto parse_error; - } - - // ID was confirmed - id = (argID[2] - '0') / UnitNum; - un = (argID[2] - '0') % UnitNum; - } else if (strlen(argID) == 4) { - // Check that the HD number is valid (10-15) - if (argID[2] != '1' || argID[3] < '0' || argID[3] > '5') { - FPRT(stderr, - "Error : Invalid argument(HDn n=0-15) [%c]\n", argID[2]); - goto parse_error; - } - - // The ID unit is good - create the id and unit number - id = ((argID[3] - '0') + 10) / UnitNum; - un = ((argID[3] - '0') + 10) % UnitNum; - argPath++; - } else { - FPRT(stderr, - "Error : Invalid argument(IDn or HDn) [%s]\n", argID); - goto parse_error; - } - } else { - FPRT(stderr, - "Error : Invalid argument(IDn or HDn) [%s]\n", argID); - goto parse_error; - } - - // Skip if there is already an active device - if (disk[id * UnitNum + un] && - !disk[id * UnitNum + un]->IsNULL()) { - continue; - } - - // Initialize device type - type = -1; - - // Check ethernet and host bridge - if (xstrcasecmp(argPath, "bridge") == 0) { - type = 4; - } else { - // Check the path length - len = strlen(argPath); - if (len < 5) { - FPRT(stderr, - "Error : Invalid argument(File path is short) [%s]\n", - argPath); - goto parse_error; - } - - // Does the file have an extension? - if (argPath[len - 4] != '.') { - FPRT(stderr, - "Error : Invalid argument(No extension) [%s]\n", argPath); - goto parse_error; - } - - // Figure out what the type is - ext = &argPath[len - 3]; - if (xstrcasecmp(ext, "hdf") == 0 || - xstrcasecmp(ext, "hds") == 0 || - xstrcasecmp(ext, "hdn") == 0 || - xstrcasecmp(ext, "hdi") == 0 || xstrcasecmp(ext, "nhd") == 0 || - xstrcasecmp(ext, "hda") == 0) { - // HD(SASI/SCSI) - type = 0; - } else if (strcasecmp(ext, "mos") == 0) { - // MO - type = 2; - } else if (strcasecmp(ext, "iso") == 0) { - // CD - type = 3; - } else { - // Cannot determine the file type - FPRT(stderr, - "Error : Invalid argument(file type) [%s]\n", ext); - goto parse_error; - } - } - - // Execute the command - if (!ProcessCmd(stderr, id, un, 0, type, argPath)) { - goto parse_error; - } - } - - // Close the configuration file - f_close(&fp); - - // Display the device list - ListDevice(stdout); - - return TRUE; - -parse_error: - - // Close the configuration file - f_close(&fp); - - return FALSE; -} -#else -bool ParseArgument(int argc, char* argv[]) +bool ParseArgument(int argc, char* argv[], int& port) { + PbCommand command; int id = -1; - bool is_sasi = false; - int max_id = 7; + int unit = -1; + PbDeviceType type = UNDEFINED; + int block_size = 0; + string name; + string log_level; + opterr = 1; int opt; - while ((opt = getopt(argc, argv, "-IiHhD:d:")) != -1) { + while ((opt = getopt(argc, argv, "-IiHhb:d:n:p:r:t:D:F:L:")) != -1) { switch (opt) { - case 'I': + // The three options below are kind of a compound option with two letters case 'i': - is_sasi = false; - max_id = 7; + case 'I': id = -1; + unit = -1; continue; - case 'H': case 'h': - is_sasi = true; - max_id = 15; + case 'H': id = -1; + unit = -1; + type = SAHD; continue; - case 'D': - case 'd': { - char* end; - id = strtol(optarg, &end, 10); - if (*end || (id < 0) || (max_id < id)) { - fprintf(stderr, "%s: invalid %s (0-%d)\n", - optarg, is_sasi ? "HD" : "ID", max_id); + case 'd': + case 'D': { + if (!ProcessId(optarg, type, id, unit)) { return false; } continue; } + case 'b': { + if (!GetAsInt(optarg, block_size)) { + cerr << "Invalid block size " << optarg << endl; + return false; + } + continue; + } + + case 'F': { + string result = rascsi_image.SetDefaultImageFolder(optarg); + if (!result.empty()) { + cerr << result << endl; + return false; + } + continue; + } + + case 'L': + log_level = optarg; + continue; + + case 'n': + name = optarg; + continue; + + case 'p': + if (!GetAsInt(optarg, port) || port <= 0 || port > 65535) { + cerr << "Invalid port " << optarg << ", port must be between 1 and 65535" << endl; + return false; + } + continue; + + case 'r': { + string error = SetReservedIds(optarg); + if (!error.empty()) { + cerr << error << endl; + return false; + } + } + continue; + + case 't': { + string t = optarg; + transform(t.begin(), t.end(), t.begin(), ::toupper); + if (!PbDeviceType_Parse(t, &type)) { + cerr << "Illegal device type '" << optarg << "'" << endl; + return false; + } + } + continue; + default: return false; case 1: + // Encountered filename break; } - if (id < 0) { - fprintf(stderr, "%s: ID not specified\n", optarg); - return false; - } else if (disk[id] && !disk[id]->IsNULL()) { - fprintf(stderr, "%d: duplicate ID\n", id); + if (optopt) { return false; } - char* path = optarg; - int type = -1; - if (has_suffix(path, ".hdf") - || has_suffix(path, ".hds") - || has_suffix(path, ".hdn") - || has_suffix(path, ".hdi") - || has_suffix(path, ".hda") - || has_suffix(path, ".nhd")) { - type = 0; - } else if (has_suffix(path, ".mos")) { - type = 2; - } else if (has_suffix(path, ".iso")) { - type = 3; - } else if (xstrcasecmp(path, "bridge") == 0) { - type = 4; - } else { - // Cannot determine the file type - fprintf(stderr, - "%s: unknown file extension\n", path); - return false; + // Set up the device data + PbDeviceDefinition *device = command.add_devices(); + device->set_id(id); + device->set_unit(unit); + device->set_type(type); + device->set_block_size(block_size); + AddParam(*device, "file", optarg); + + size_t separator_pos = name.find(':'); + if (separator_pos != string::npos) { + device->set_vendor(name.substr(0, separator_pos)); + name = name.substr(separator_pos + 1); + separator_pos = name.find(':'); + if (separator_pos != string::npos) { + device->set_product(name.substr(0, separator_pos)); + device->set_revision(name.substr(separator_pos + 1)); + } + else { + device->set_product(name); + } + } + else { + device->set_vendor(name); } - int un = 0; - if (is_sasi) { - un = id % UnitNum; - id /= UnitNum; - } - - // Execute the command - if (!ProcessCmd(stderr, id, un, 0, type, path)) { - return false; - } id = -1; + type = UNDEFINED; + block_size = 0; + name = ""; } - // Display the device list - ListDevice(stdout); + if (!log_level.empty() && !SetLogLevel(log_level)) { + LOGWARN("Invalid log level '%s'", log_level.c_str()); + } + + // Attach all specified devices + command.set_operation(ATTACH); + + if (!ProcessCmd(-1, command)) { + return false; + } + + // Display and log the device list + PbServerInfo server_info; + rascsi_response.GetDevices(server_info, devices); + const list& devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() }; + const string device_list = ListDevices(devices); + LogDevices(device_list); + cout << device_list << endl; + return true; } -#endif // BAREMETAL -#ifndef BAREMETAL //--------------------------------------------------------------------------- // // Pin the thread to a specific CPU @@ -906,13 +1243,11 @@ bool ParseArgument(int argc, char* argv[]) //--------------------------------------------------------------------------- void FixCpu(int cpu) { - cpu_set_t cpuset; - int cpus; - // Get the number of CPUs + cpu_set_t cpuset; CPU_ZERO(&cpuset); sched_getaffinity(0, sizeof(cpu_set_t), &cpuset); - cpus = CPU_COUNT(&cpuset); + int cpus = CPU_COUNT(&cpuset); // Set the thread affinity if (cpu < cpus) { @@ -929,22 +1264,8 @@ void FixCpu(int cpu) //--------------------------------------------------------------------------- static void *MonThread(void *param) { + // Scheduler Settings struct sched_param schedparam; - struct sockaddr_in client; - socklen_t len; - int fd; - FILE *fp; - char buf[BUFSIZ]; - char *p; - int i; - char *argv[5]; - int id; - int un; - int cmd; - int type; - char *file; - - // Scheduler Settings schedparam.sched_priority = 0; sched_setscheduler(0, SCHED_IDLE, &schedparam); @@ -956,151 +1277,269 @@ static void *MonThread(void *param) usleep(1); } - // Setup the monitor socket to receive commands + // Set up the monitor socket to receive commands listen(monsocket, 1); - while (1) { - // Wait for connection - memset(&client, 0, sizeof(client)); - len = sizeof(client); - fd = accept(monsocket, (struct sockaddr*)&client, &len); - if (fd < 0) { - break; - } + while (true) { + int fd = -1; - // Fetch the command - fp = fdopen(fd, "r+"); - p = fgets(buf, BUFSIZ, fp); - - // Failed to get the command - if (!p) { - goto next; - } - - // Remove the newline character - p[strlen(p) - 1] = 0; - - // List all of the devices - if (xstrncasecmp(p, "list", 4) == 0) { - ListDevice(fp); - goto next; - } - - // Parameter separation - argv[0] = p; - for (i = 1; i < 5; i++) { - // Skip parameter values - while (*p && (*p != ' ')) { - p++; + try { + // Wait for connection + struct sockaddr_in client; + socklen_t socklen = sizeof(client); + memset(&client, 0, socklen); + fd = accept(monsocket, (struct sockaddr*)&client, &socklen); + if (fd < 0) { + throw io_exception("accept() failed"); } - // Replace spaces with null characters - while (*p && (*p == ' ')) { - *p++ = 0; + // Read magic string + char magic[6]; + int bytes_read = ReadNBytes(fd, (uint8_t *)magic, sizeof(magic)); + if (!bytes_read) { + continue; + } + if (bytes_read != sizeof(magic) || strncmp(magic, "RASCSI", sizeof(magic))) { + throw io_exception("Invalid magic"); } - // The parameters were lost - if (!*p) { - break; + // Fetch the command + PbCommand command; + DeserializeMessage(fd, command); + + switch(command.operation()) { + case LOG_LEVEL: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + string log_level = GetParam(command, "level"); + bool status = SetLogLevel(log_level); + if (!status) { + ReturnStatus(fd, false, "Invalid log level: " + log_level); + } + else { + ReturnStatus(fd); + } + break; + } + + case DEFAULT_FOLDER: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + string folder = GetParam(command, "folder"); + if (folder.empty()) { + ReturnStatus(fd, false, "Can't set default image folder: Missing folder name"); + } + + string result = rascsi_image.SetDefaultImageFolder(folder); + if (!result.empty()) { + ReturnStatus(fd, false, result); + } + else { + ReturnStatus(fd); + } + break; + } + + case DEVICES_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + rascsi_response.GetDevicesInfo(result, command, devices, UnitNum); + SerializeMessage(fd, result); + + break; + } + + case DEVICE_TYPES_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_device_types_info(rascsi_response.GetDeviceTypesInfo(result, command)); + SerializeMessage(fd, result); + break; + } + + case SERVER_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_server_info(rascsi_response.GetServerInfo( + result, devices, reserved_ids, current_log_level)); + SerializeMessage(fd, result); + break; + } + + case VERSION_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_version_info(rascsi_response.GetVersionInfo(result)); + SerializeMessage(fd, result); + break; + } + + case LOG_LEVEL_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_log_level_info(rascsi_response.GetLogLevelInfo(result, current_log_level)); + SerializeMessage(fd, result); + break; + } + + case DEFAULT_IMAGE_FILES_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_image_files_info(rascsi_response.GetAvailableImages(result)); + SerializeMessage(fd, result); + break; + } + + case IMAGE_FILE_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + string filename = GetParam(command, "file"); + if (filename.empty()) { + ReturnStatus(fd, false, "Can't get image file info: Missing filename"); + } + else { + PbResult result; + PbImageFile* image_file = new PbImageFile(); + bool status = rascsi_response.GetImageFile(image_file, filename); + if (status) { + result.set_status(true); + result.set_allocated_image_file_info(image_file); + SerializeMessage(fd, result); + } + else { + ReturnStatus(fd, false, "Can't get image file info for '" + filename + "'"); + } + } + break; + } + + case NETWORK_INTERFACES_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_network_interfaces_info(rascsi_response.GetNetworkInterfacesInfo(result)); + SerializeMessage(fd, result); + break; + } + + case MAPPING_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_mapping_info(rascsi_response.GetMappingInfo(result)); + SerializeMessage(fd, result); + break; + } + + case RESERVED_IDS_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_reserved_ids_info(rascsi_response.GetReservedIds(result, reserved_ids)); + SerializeMessage(fd, result); + break; + } + + case SHUT_DOWN: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_status(true); + SerializeMessage(fd, result); + + TerminationHandler(0); + break; + } + + default: { + // Wait until we become idle + while (active) { + usleep(500 * 1000); + } + + ProcessCmd(fd, command); + break; + } } + } + catch(const io_exception& e) { + LOGWARN("%s", e.getmsg().c_str()); - // Recognized as a parameter - argv[i] = p; + // Fall through } - // Failed to get all parameters - if (i < 5) { - goto next; + if (fd >= 0) { + close(fd); } - - // ID, unit, command, type, file - id = atoi(argv[0]); - un = atoi(argv[1]); - cmd = atoi(argv[2]); - type = atoi(argv[3]); - file = argv[4]; - - // Wait until we becom idle - while (active) { - usleep(500 * 1000); - } - - // Execute the command - ProcessCmd(fp, id, un, cmd, type, file); - -next: - // Release the connection - fclose(fp); - close(fd); } return NULL; } -#endif // BAREMETAL //--------------------------------------------------------------------------- // // Main processing // //--------------------------------------------------------------------------- -#ifdef BAREMETAL -extern "C" -int startrascsi(void) -{ - int argc = 0; - char** argv = NULL; -#else int main(int argc, char* argv[]) { -#endif // BAREMETAL - int i; - int ret; - int actid; - DWORD now; - BUS::phase_t phase; - BYTE data; -#ifndef BAREMETAL - struct sched_param schparam; -#endif // BAREMETAL + GOOGLE_PROTOBUF_VERIFY_VERSION; + + int actid; + BUS::phase_t phase; + // added setvbuf to override stdout buffering, so logs are written immediately and not when the process exits. + setvbuf(stdout, NULL, _IONBF, 0); + struct sched_param schparam; - spdlog::set_level(spdlog::level::trace); - LOGTRACE("Entering the function %s with %d arguments", __PRETTY_FUNCTION__, argc); // Output the Banner Banner(argc, argv); - // Initialize - ret = 0; - if (!Init()) { - ret = EPERM; - goto init_exit; + // ParseArgument() requires the bus to have been initialized first, which requires the root user. + // The -v option should be available for any user, which requires special handling. + for (int i = 1 ; i < argc; i++) { + if (!strcasecmp(argv[i], "-v")) { + cout << rascsi_get_version_string() << endl; + return 0; + } } + SetLogLevel("info"); + + // Create a thread-safe stdout logger to process the log messages + auto logger = stdout_color_mt("rascsi stdout logger"); + + int port = 6868; + + if (!InitBus()) { + return EPERM; + } + + if (!ParseArgument(argc, argv, port)) { + Cleanup(); + return -1; + } + + if (!InitService(port)) { + return EPERM; + } + + // Signal handler to detach all devices on a KILL or TERM signal + struct sigaction termination_handler; + termination_handler.sa_handler = TerminationHandler; + sigemptyset(&termination_handler.sa_mask); + termination_handler.sa_flags = 0; + sigaction(SIGINT, &termination_handler, NULL); + sigaction(SIGTERM, &termination_handler, NULL); + // Reset Reset(); -#ifdef BAREMETAL - // BUSY assert (to hold the host side) - bus->SetBSY(TRUE); - - // Argument parsing - if (!ParseConfig(argc, argv)) { - ret = EINVAL; - goto err_exit; - } -#else - // Argument parsing - if (!ParseArgument(argc, argv)) { - ret = EINVAL; - goto err_exit; - } -#endif - -#ifdef BAREMETAL - // Release the busy signal - bus->SetBSY(FALSE); -#endif - -#ifndef BAREMETAL // Set the affinity to a specific processor core FixCpu(3); @@ -1109,10 +1548,9 @@ int main(int argc, char* argv[]) schparam.sched_priority = sched_get_priority_max(SCHED_FIFO); sched_setscheduler(0, SCHED_FIFO, &schparam); #endif // USE_SEL_EVENT_ENABLE -#endif // BAREMETAL // Start execution - running = TRUE; + running = true; // Main Loop while (running) { @@ -1135,9 +1573,7 @@ int main(int argc, char* argv[]) #else bus->Aquire(); if (!bus->GetSEL()) { -#if !defined(BAREMETAL) usleep(0); -#endif // !BAREMETAL continue; } #endif // USE_SEL_EVENT_ENABLE @@ -1145,7 +1581,7 @@ int main(int argc, char* argv[]) // Wait until BSY is released as there is a possibility for the // initiator to assert it while setting the ID (for up to 3 seconds) if (bus->GetBSY()) { - now = SysTimer::GetTimerLow(); + int now = SysTimer::GetTimerLow(); while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) { bus->Aquire(); if (!bus->GetBSY()) { @@ -1154,20 +1590,23 @@ int main(int argc, char* argv[]) } } - // Stop because it the bus is busy or another device responded + // Stop because the bus is busy or another device responded if (bus->GetBSY() || !bus->GetSEL()) { continue; } + pthread_mutex_lock(&ctrl_mutex); + // Notify all controllers - data = bus->GetDAT(); - for (i = 0; i < CtrlMax; i++) { - if (!ctrl[i] || (data & (1 << i)) == 0) { + BYTE data = bus->GetDAT(); + int i = 0; + for (auto it = controllers.begin(); it != controllers.end(); ++i, ++it) { + if (!*it || (data & (1 << i)) == 0) { continue; } // Find the target that has moved to the selection phase - if (ctrl[i]->Process() == BUS::selection) { + if ((*it)->Process() == BUS::selection) { // Get the target ID actid = i; @@ -1179,47 +1618,41 @@ int main(int argc, char* argv[]) // Return to bus monitoring if the selection phase has not started if (phase != BUS::selection) { + pthread_mutex_unlock(&ctrl_mutex); continue; } // Start target device - active = TRUE; + active = true; -#if !defined(USE_SEL_EVENT_ENABLE) && !defined(BAREMETAL) +#ifndef USE_SEL_EVENT_ENABLE // Scheduling policy setting (highest priority) schparam.sched_priority = sched_get_priority_max(SCHED_FIFO); sched_setscheduler(0, SCHED_FIFO, &schparam); -#endif // !USE_SEL_EVENT_ENABLE && !BAREMETAL +#endif // USE_SEL_EVENT_ENABLE // Loop until the bus is free while (running) { // Target drive - phase = ctrl[actid]->Process(); + phase = controllers[actid]->Process(); // End when the bus is free if (phase == BUS::busfree) { break; } } + pthread_mutex_unlock(&ctrl_mutex); -#if !defined(USE_SEL_EVENT_ENABLE) && !defined(BAREMETAL) + +#ifndef USE_SEL_EVENT_ENABLE // Set the scheduling priority back to normal schparam.sched_priority = 0; sched_setscheduler(0, SCHED_OTHER, &schparam); -#endif // !USE_SEL_EVENT_ENABLE && !BAREMETAL +#endif // USE_SEL_EVENT_ENABLE // End the target travel - active = FALSE; + active = false; } -err_exit: - // Cleanup - Cleanup(); - -init_exit: -#if !defined(BAREMETAL) - exit(ret); -#else - return ret; -#endif // BAREMETAL + return 0; } diff --git a/src/raspberrypi/xm6.h b/src/raspberrypi/rascsi.h similarity index 58% rename from src/raspberrypi/xm6.h rename to src/raspberrypi/rascsi.h index 1d0fc943..42f9fc4a 100644 --- a/src/raspberrypi/xm6.h +++ b/src/raspberrypi/rascsi.h @@ -6,26 +6,11 @@ // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS // -// [ Common Definition ] +// [ Common Definitions ] // //--------------------------------------------------------------------------- -#if !defined(xm6_h) -#define xm6_h - -//--------------------------------------------------------------------------- -// -// RaSCSI -// -//--------------------------------------------------------------------------- -#define RASCSI 1 - -//--------------------------------------------------------------------------- -// -// ID Macro -// -//--------------------------------------------------------------------------- -#define MAKEID(a, b, c, d) ((DWORD)((a<<24) | (b<<16) | (c<<8) | d)) +#pragma once //--------------------------------------------------------------------------- // @@ -34,11 +19,15 @@ //--------------------------------------------------------------------------- #define USE_SEL_EVENT_ENABLE // Check SEL signal by event #define REMOVE_FIXED_SASIHD_SIZE // remove the size limitation of SASIHD -#define USE_MZ1F23_1024_SUPPORT // MZ-1F23 (SASI 20M/sector size 1024) +// This avoids an indefinite loop with warnings if there is no RaSCSI hardware +// and thus helps with running certain tests on X86 hardware. +#if defined(__x86_64__) || defined(__X86__) +#undef USE_SEL_EVENT_ENABLE +#endif //--------------------------------------------------------------------------- // -// Class Declaration +// Class Declarations // //--------------------------------------------------------------------------- class Fileio; @@ -46,6 +35,3 @@ class Fileio; class Disk; // SASI/SCSI Disk class Filepath; - // File Path - -#endif // xm6_h diff --git a/src/raspberrypi/rascsi_image.cpp b/src/raspberrypi/rascsi_image.cpp new file mode 100644 index 00000000..95c5edbf --- /dev/null +++ b/src/raspberrypi/rascsi_image.cpp @@ -0,0 +1,347 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include +#include +#include "os.h" +#include "log.h" +#include "filepath.h" +#include "spdlog/spdlog.h" +#include "devices/file_support.h" +#include "protobuf_util.h" +#include "rascsi_image.h" +#include +#include +#include + +using namespace std; +using namespace spdlog; +using namespace rascsi_interface; +using namespace protobuf_util; + +#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ ) + +RascsiImage::RascsiImage() +{ + // ~/images is the default folder for device image files, for the root user it is /home/pi/images + int uid = getuid(); + const char *sudo_user = getenv("SUDO_UID"); + if (sudo_user) { + uid = stoi(sudo_user); + } + + const passwd *passwd = getpwuid(uid); + if (uid && passwd) { + default_image_folder = passwd->pw_dir; + default_image_folder += "/images"; + } + else { + default_image_folder = "/home/pi/images"; + } +} + +string RascsiImage::SetDefaultImageFolder(const string& f) +{ + string folder = f; + + // If a relative path is specified the path is assumed to be relative to the user's home directory + if (folder[0] != '/') { + int uid = getuid(); + const char *sudo_user = getenv("SUDO_UID"); + if (sudo_user) { + uid = stoi(sudo_user); + } + + const passwd *passwd = getpwuid(uid); + if (passwd) { + folder = passwd->pw_dir; + folder += "/"; + folder += f; + } + } + else { + if (folder.find("/home/") != 0) { + return "Default image folder must be located in '/home/'"; + } + } + + struct stat info; + stat(folder.c_str(), &info); + if (!S_ISDIR(info.st_mode) || access(folder.c_str(), F_OK) == -1) { + return "Folder '" + f + "' does not exist or is not accessible"; + } + + default_image_folder = folder; + + LOGINFO("Default image folder set to '%s'", default_image_folder.c_str()); + + return ""; +} + +bool RascsiImage::IsValidSrcFilename(const string& filename) +{ + // Source file must exist and must be a regular file or a symlink + struct stat st; + return !stat(filename.c_str(), &st) && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)); +} + +bool RascsiImage::IsValidDstFilename(const string& filename) +{ + // Destination file must not yet exist + struct stat st; + return stat(filename.c_str(), &st); +} + +bool RascsiImage::CreateImage(int fd, const PbCommand& command) +{ + string filename = GetParam(command, "file"); + if (filename.empty()) { + return ReturnStatus(fd, false, "Can't create image file: Missing image filename"); + } + if (filename.find('/') != string::npos) { + return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Filename must not contain a path"); + } + filename = default_image_folder + "/" + filename; + if (!IsValidDstFilename(filename)) { + return ReturnStatus(fd, false, "Can't create image file: '" + filename + "': File already exists"); + } + + const string size = GetParam(command, "size"); + if (size.empty()) { + return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Missing image size"); + } + + off_t len; + try { + len = stoull(size); + } + catch(const invalid_argument& e) { + return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Invalid file size " + size); + } + catch(const out_of_range& e) { + return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Invalid file size " + size); + } + if (len < 512 || (len & 0x1ff)) { + ostringstream error; + error << "Invalid image file size " << len; + return ReturnStatus(fd, false, error.str()); + } + + struct stat st; + if (!stat(filename.c_str(), &st)) { + return ReturnStatus(fd, false, "Can't create image file '" + filename + "': File already exists"); + } + + string permission = GetParam(command, "read_only"); + // Since rascsi is running as root ensure that others can access the file + int permissions = !strcasecmp(permission.c_str(), "true") ? + S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + + int image_fd = open(filename.c_str(), O_CREAT|O_WRONLY, permissions); + if (image_fd == -1) { + return ReturnStatus(fd, false, "Can't create image file '" + filename + "': " + string(strerror(errno))); + } + + if (fallocate(image_fd, 0, 0, len) == -1) { + close(image_fd); + + unlink(filename.c_str()); + + return ReturnStatus(fd, false, "Can't allocate space for image file '" + filename + "': " + string(strerror(errno))); + } + + close(image_fd); + + ostringstream msg; + msg << "Created " << (permissions & S_IWUSR ? "": "read-only ") << "image file '" << filename + "' with a size of " << len << " bytes"; + LOGINFO("%s", msg.str().c_str()); + + return ReturnStatus(fd); +} + +bool RascsiImage::DeleteImage(int fd, const PbCommand& command) +{ + string filename = GetParam(command, "file"); + if (filename.empty()) { + return ReturnStatus(fd, false, "Missing image filename"); + } + + if (!IsValidDstFilename(filename)) { + return ReturnStatus(fd, false, "Can't delete image file '" + filename + "': File already exists"); + } + + if (filename.find('/') != string::npos) { + return ReturnStatus(fd, false, "The image filename '" + filename + "' must not contain a path"); + } + + filename = default_image_folder + "/" + filename; + + int id; + int unit; + Filepath filepath; + filepath.SetPath(filename.c_str()); + if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) { + ostringstream msg; + msg << "Can't delete image file '" << filename << "', it is used by device ID " << id << ", unit " << unit; + return ReturnStatus(fd, false, msg.str()); + } + + if (unlink(filename.c_str())) { + return ReturnStatus(fd, false, "Can't delete image file '" + filename + "': " + string(strerror(errno))); + } + + LOGINFO("Deleted image file '%s'", filename.c_str()); + + return ReturnStatus(fd); +} + +bool RascsiImage::RenameImage(int fd, const PbCommand& command) +{ + string from = GetParam(command, "from"); + if (from.empty()) { + return ReturnStatus(fd, false, "Can't rename image file: Missing source filename"); + } + if (from.find('/') != string::npos) { + return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path"); + } + from = default_image_folder + "/" + from; + if (!IsValidSrcFilename(from)) { + return ReturnStatus(fd, false, "Can't rename image file: '" + from + "': Invalid name or type"); + } + + string to = GetParam(command, "to"); + if (to.empty()) { + return ReturnStatus(fd, false, "Can't rename image file '" + from + "': Missing destination filename"); + } + if (to.find('/') != string::npos) { + return ReturnStatus(fd, false, "The destination filename '" + to + "' must not contain a path"); + } + to = default_image_folder + "/" + to; + if (!IsValidDstFilename(to)) { + return ReturnStatus(fd, false, "Can't rename image file '" + from + "' to '" + to + "': File already exists"); + } + + if (rename(from.c_str(), to.c_str())) { + return ReturnStatus(fd, false, "Can't rename image file '" + from + "' to '" + to + "': " + string(strerror(errno))); + } + + LOGINFO("Renamed image file '%s' to '%s'", from.c_str(), to.c_str()); + + return ReturnStatus(fd); +} + +bool RascsiImage::CopyImage(int fd, const PbCommand& command) +{ + string from = GetParam(command, "from"); + if (from.empty()) { + return ReturnStatus(fd, false, "Can't copy image file: Missing source filename"); + } + if (from.find('/') != string::npos) { + return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path"); + } + from = default_image_folder + "/" + from; + if (!IsValidSrcFilename(from)) { + return ReturnStatus(fd, false, "Can't copy image file: '" + from + "': Invalid name or type"); + } + + string to = GetParam(command, "to"); + if (to.empty()) { + return ReturnStatus(fd, false, "Can't copy image file '" + from + "': Missing destination filename"); + } + if (to.find('/') != string::npos) { + return ReturnStatus(fd, false, "The destination filename '" + to + "' must not contain a path"); + } + to = default_image_folder + "/" + to; + if (!IsValidDstFilename(to)) { + return ReturnStatus(fd, false, "Can't copy image file '" + from + "' to '" + to + "': File already exists"); + } + + struct stat st; + if (lstat(from.c_str(), &st)) { + return ReturnStatus(fd, false, "Can't access source image file '" + from + "': " + string(strerror(errno))); + } + + // Symbolic links need a special handling + if ((st.st_mode & S_IFMT) == S_IFLNK) { + if (symlink(filesystem::read_symlink(from).c_str(), to.c_str())) { + return ReturnStatus(fd, false, "Can't copy symlink '" + from + "': " + string(strerror(errno))); + } + + LOGINFO("Copied symlink '%s' to '%s'", from.c_str(), to.c_str()); + + return ReturnStatus(fd); + } + + int fd_src = open(from.c_str(), O_RDONLY, 0); + if (fd_src == -1) { + return ReturnStatus(fd, false, "Can't open source image file '" + from + "': " + string(strerror(errno))); + } + + string permission = GetParam(command, "read_only"); + // Since rascsi is running as root ensure that others can access the file + int permissions = !strcasecmp(permission.c_str(), "true") ? + S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + + int fd_dst = open(to.c_str(), O_WRONLY | O_CREAT, permissions); + if (fd_dst == -1) { + close(fd_src); + + return ReturnStatus(fd, false, "Can't open destination image file '" + to + "': " + string(strerror(errno))); + } + + if (sendfile(fd_dst, fd_src, 0, st.st_size) == -1) { + close(fd_dst); + close(fd_src); + + unlink(to.c_str()); + + return ReturnStatus(fd, false, "Can't copy image file '" + from + "' to '" + to + "': " + string(strerror(errno))); + } + + close(fd_dst); + close(fd_src); + + LOGINFO("Copied image file '%s' to '%s'", from.c_str(), to.c_str()); + + return ReturnStatus(fd); +} + +bool RascsiImage::SetImagePermissions(int fd, const PbCommand& command) +{ + string filename = GetParam(command, "file"); + if (filename.empty()) { + return ReturnStatus(fd, false, "Missing image filename"); + } + if (filename.find('/') != string::npos) { + return ReturnStatus(fd, false, "The image filename '" + filename + "' must not contain a path"); + } + filename = default_image_folder + "/" + filename; + if (!IsValidSrcFilename(filename)) { + return ReturnStatus(fd, false, "Can't modify image file '" + filename + "': Invalid name or type"); + } + + bool protect = command.operation() == PROTECT_IMAGE; + + int permissions = protect ? S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + + if (chmod(filename.c_str(), permissions) == -1) { + ostringstream error; + error << "Can't " << (protect ? "protect" : "unprotect") << " image file '" << filename << "': " << strerror(errno); + return ReturnStatus(fd, false, error.str()); + } + + if (protect) { + LOGINFO("Protected image file '%s'", filename.c_str()); + } + else { + LOGINFO("Unprotected image file '%s'", filename.c_str()); + } + + return ReturnStatus(fd); +} diff --git a/src/raspberrypi/rascsi_image.h b/src/raspberrypi/rascsi_image.h new file mode 100644 index 00000000..21b28e2c --- /dev/null +++ b/src/raspberrypi/rascsi_image.h @@ -0,0 +1,38 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "rascsi_interface.pb.h" +#include + +using namespace std; +using namespace rascsi_interface; + +class RascsiImage +{ +public: + + RascsiImage(); + ~RascsiImage() {}; + + string GetDefaultImageFolder() const { return default_image_folder; } + string SetDefaultImageFolder(const string&); + bool IsValidSrcFilename(const string&); + bool IsValidDstFilename(const string&); + bool CreateImage(int, const PbCommand&); + bool DeleteImage(int, const PbCommand&); + bool RenameImage(int, const PbCommand&); + bool CopyImage(int, const PbCommand&); + bool SetImagePermissions(int, const PbCommand&); + +private: + + string default_image_folder; +}; diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto new file mode 100644 index 00000000..4fc6f53a --- /dev/null +++ b/src/raspberrypi/rascsi_interface.proto @@ -0,0 +1,349 @@ +// +// Each rascsi message sent to the rascsi server is preceded by the magic string "RASCSI". +// A message starts with a little endian 32 bit header which contains the protobuf message size. +// Unless explicitly specified the order of repeated data returned is undefined. +// + +syntax = "proto3"; + +package rascsi_interface; + +// The available device types +enum PbDeviceType { + UNDEFINED = 0; + // Non-removable SASI drive + SAHD = 1; + // Non-removable SCSI drive + SCHD = 2; + // Removable SCSI drive + SCRM = 3; + // Magnoto-Optical drive + SCMO = 4; + // CD-ROM drive + SCCD = 5; + // Network bridge + SCBR = 6; + // DaynaPort network adapter + SCDP = 7; +} + +// rascsi remote operations, returning PbResult +enum PbOperation { + NONE = 0; + + // Attach devices and return the new device list (PbDevicesInfo) + // Parameters (mutually exclusive): + // "file": The filename relative to the default image folder. It must not contain a slash. + // "interfaces": A prioritized comma-separated list of interfaces to create a network bridge for. + ATTACH = 1; + + // Detach a device and return the new device list (PbDevicesInfo) + // Detaches all LUNs of that device which are equal to or higher than the LUN specified. + DETACH = 2; + + // Detach all devices, does not require a device list + DETACH_ALL = 3; + + // Start device + START = 4; + + // Stop device, e.g. park drive + STOP = 5; + + // Insert medium + // Parameters: + // "file": The filename, relative to the default image folder. It must not contain a slash. + INSERT = 6; + + // Eject medium + EJECT = 7; + + // Write-protect medium (not possible for read-only media) + PROTECT = 8; + + // Make medium writable (not possible for read-only media) + UNPROTECT = 9; + + // Gets the server information (PbServerInfo) + SERVER_INFO = 10; + + // Get rascsi version information (PbVersionInfo) + VERSION_INFO = 11; + + // Get information on attached devices (PbDevicesInfo) + // Returns data for all attached devices if the device list is empty. + DEVICES_INFO = 12; + + // Get device properties by device type (PbDeviceTypesInfo) + DEVICE_TYPES_INFO = 13; + + // Get information on available image files in the default image folder (PbImageFilesInfo) + DEFAULT_IMAGE_FILES_INFO = 14; + + // Get information on an image file (not necessarily in the default image folder) based on an absolute path. + // Parameters: + // "file": The filename. Either an absolute path or a path relative to the default image folder. + IMAGE_FILE_INFO = 15; + + // Get information on the available log levels and the current log level (PbLogLevelInfo) + LOG_LEVEL_INFO = 16; + + // Get the names of the available network interfaces (PbNetworkInterfacesInfo) + // Only lists interfaces that are up. + NETWORK_INTERFACES_INFO = 17; + + // Get the mapping of extensions to device types (PbMappingInfo) + MAPPING_INFO = 18; + + // Get the list of reserved device IDs (PbReservedIdsInfo) + RESERVED_IDS_INFO = 19; + + // Set the default folder for image files. This folder must be located in /home. + // Parameters: + // "folder": The path of the default folder, relative to the user's home folder if relative. + DEFAULT_FOLDER = 20; + + // Set a new log level. + // Parameters: + // "level": The new log level + LOG_LEVEL = 21; + + // Block IDs from being used, usually the IDs of the initiators (computers) in the SCSI chain. + // Parameters: + // "ids": A comma-separated list of IDs to reserve, or an empty string in order not to reserve any ID. + RESERVE_IDS = 22; + + // Shut down the rascsi process + SHUT_DOWN = 23; + + // Create an image file. The image file must not yet exist. + // Parameters: + // "file": The filename, relative to the default image folder. It must not contain a slash. + // "size": The file size in bytes, must be a multiple of 512 + // "read_only": Optional, "true" (case-insensitive) in order to create a read-only file + CREATE_IMAGE = 24; + + // Delete an image file. + // Parameters: + // "file": The filename, relative to the default image folder. It must not contain a slash. + DELETE_IMAGE = 25; + + // Rename an image file. + // Parameters: + // "from": The old filename, relative to the default image folder. It must not contain a slash. + // "to": The new filename, relative to the default image folder. It must not contain a slash. + // The new filename must not yet exist. + RENAME_IMAGE = 26; + + // Copy an image file. + // Parameters: + // "from": The source filename, relative to the default image folder. It must not contain a slash. + // "to": The destination filename, relative to the default image folder. It must not contain a slash. + // "read_only": Optional, "true" (case-insensitive) in order to create a read-only file + // The destination filename must not yet exist. + COPY_IMAGE = 27; + + // Write-protect an image file. + // Parameters: + // "file": The filename, relative to the default image folder. It must not contain a slash. + PROTECT_IMAGE = 28; + + // Make an image file writable. + // Parameters: + // "file": The filename, relative to the default image folder. It must not contain a slash. + UNPROTECT_IMAGE = 29; +} + +// The supported file extensions mapped to their respective device types +message PbMappingInfo { + map mapping = 1; +} + +// The properties supported by a device +message PbDeviceProperties { + // Read-only media (e.g. CD-ROMs) are not protectable but permanently read-only + bool read_only = 1; + // Medium can be write-protected + bool protectable = 2; + // Device can be stopped, e.g. parked + bool stoppable = 3; + // Medium can be removed + bool removable = 4; + // Medium can be locked + bool lockable = 5; + // Device supports image file as a parameter + bool supports_file = 6; + // Device supports parameters other than a filename + bool supports_params = 7; + // List of default parameters, if any (requires supports_params to be true) + map default_params = 8; + // LUN numbers this device can represent. At least 1 (for LUN 0 only). Maxium is 32, for LUNs 0-31. + int32 luns = 9; + // Unordered list of permitted block sizes in bytes, empty if the block size is not configurable + repeated uint32 block_sizes = 10; +} + +// The status of a device, only meaningful if the respective feature is supported +message PbDeviceStatus { + // Medium is write-protected + bool protected = 1; + // Device is stopped, e.g. parked + bool stopped = 2; + // Medium is removed + bool removed = 3; + // Medium is locked + bool locked = 4; +} + +// Device properties by device type. (Note that a protobuf map cannot be used because enums cannot be map keys.) +message PbDeviceTypeProperties { + PbDeviceType type = 1; + PbDeviceProperties properties = 2; +} + +message PbDeviceTypesInfo { + repeated PbDeviceTypeProperties properties = 1; +} + +// The image file data +message PbImageFile { + string name = 1; + // The assumed device type, based on the filename extension + PbDeviceType type = 2; + // The file size in bytes, 0 for block devices + uint64 size = 3; + bool read_only = 4; +} + +// The default image folder and the image files it contains +message PbImageFilesInfo { + string default_image_folder = 1; + repeated PbImageFile image_files = 2; +} + +// Log level information +message PbLogLevelInfo { + // List of available log levels, ordered by increasing by severity + repeated string log_levels = 2; + string current_log_level = 3; +} + +// The network interfaces information +message PbNetworkInterfacesInfo { + repeated string name = 1; +} + +// The device definition, sent from the client to the server +message PbDeviceDefinition { + int32 id = 1; + int32 unit = 2; + PbDeviceType type = 3; + // Device specific named parameters, e.g. the name of an image file + map params = 4; + // The optional block size in bytes per sector, must be one of the supported block sizes for SASI/SCSI + int32 block_size = 5; + // The device name components + string vendor = 6; + string product = 7; + string revision = 8; + // Create a write-protected device + bool protected = 9; +} + +// The device data, sent from the server to the client +message PbDevice { + int32 id = 1; + int32 unit = 2; + PbDeviceType type = 3; + PbDeviceProperties properties = 4; + PbDeviceStatus status = 5; + // Image file information, if the device supports image files + PbImageFile file = 6; + // Effective parameters the device was created with + map params = 7; + string vendor = 8; + string product = 9; + string revision = 10; + // Block size in bytes + int32 block_size = 11; + // Number of blocks + uint64 block_count = 12; +} + +message PbDevicesInfo { + repeated PbDevice devices = 1; +} + +// The reserved device IDs +message PbReservedIdsInfo { + repeated int32 ids = 1; +} + +// Rascsi server version information +message PbVersionInfo { + // The rascsi version + int32 major_version = 1; + int32 minor_version = 2; + // < 0 for a development version, = 0 if there is no patch yet + int32 patch_version = 3; +} + +// Commands rascsi can execute and their parameters +message PbCommand { + PbOperation operation = 1; + // The non-empty list of devices for this command + repeated PbDeviceDefinition devices = 2; + // The named parameters for the operation, e.g. a filename, or a network interface list + map params = 3; +} + +// The result of a command +message PbResult { + // false means that an error occurred + bool status = 1; + // An optional error or information message, depending on the status. A string without trailing CR/LF. + string msg = 2; + // Optional additional result data + oneof result { + // The result of a SERVER_INFO command + PbServerInfo server_info = 3; + // The result of a VERSION_INFO command + PbVersionInfo version_info = 4; + // The result of a LOG_LEVEL_INFO command + PbLogLevelInfo log_level_info = 5; + // The result of a DEVICES_INFO, ATTACH or DETACH command + PbDevicesInfo devices_info = 6; + // The result of a DEVICE_TYPES_INFO command + PbDeviceTypesInfo device_types_info = 7; + // The result of a DEFAULT_IMAGE_FILES_INFO command + PbImageFilesInfo image_files_info = 8; + // The result of an IMAGE_FILE_INFO command + PbImageFile image_file_info = 9; + // The result of a NETWORK_INTERFACES_INFO command + PbNetworkInterfacesInfo network_interfaces_info = 10; + // The result of an MAPPING_INFO command + PbMappingInfo mapping_info = 11; + // The result of a RESERVED_IDS_INFO command + PbReservedIdsInfo reserved_ids_info = 12; + } +} + +// The rascsi server information. All data can also be requested with individual commands. +message PbServerInfo { + // The rascsi server version data + PbVersionInfo version_info = 1; + // The available log levels and the current log level + PbLogLevelInfo log_level_info = 2; + // Supported device types and their properties + PbDeviceTypesInfo device_types_info = 3; + // The image files in the default folder + PbImageFilesInfo image_files_info = 4; + // The available (up) network interfaces + PbNetworkInterfacesInfo network_interfaces_info = 5; + // The extensions to device types mapping + PbMappingInfo mapping_info = 6; + // The reserved device IDs + PbReservedIdsInfo reserved_ids_info = 7; + // The attached devices + PbDevicesInfo devices_info = 8; +} diff --git a/src/raspberrypi/rascsi_response.cpp b/src/raspberrypi/rascsi_response.cpp new file mode 100644 index 00000000..e16e7520 --- /dev/null +++ b/src/raspberrypi/rascsi_response.cpp @@ -0,0 +1,344 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "devices/file_support.h" +#include "devices/disk.h" +#include "devices/device_factory.h" +#include "devices/device.h" +#include "protobuf_util.h" +#include "rascsi_version.h" +#include "rascsi_interface.pb.h" +#include "rascsi_image.h" +#include "rascsi_response.h" +#include + +using namespace rascsi_interface; +using namespace protobuf_util; + +RascsiResponse::RascsiResponse(DeviceFactory *device_factory, const RascsiImage *rascsi_image) +{ + this->device_factory = device_factory; + this->rascsi_image = rascsi_image; + + log_levels.push_back("trace"); + log_levels.push_back("debug"); + log_levels.push_back("info"); + log_levels.push_back("warn"); + log_levels.push_back("err"); + log_levels.push_back("critical"); + log_levels.push_back("off"); +} + +PbDeviceProperties *RascsiResponse::GetDeviceProperties(const Device *device) +{ + PbDeviceProperties *properties = new PbDeviceProperties(); + + properties->set_luns(device->GetSupportedLuns()); + properties->set_read_only(device->IsReadOnly()); + properties->set_protectable(device->IsProtectable()); + properties->set_stoppable(device->IsStoppable()); + properties->set_removable(device->IsRemovable()); + properties->set_lockable(device->IsLockable()); + properties->set_supports_file(dynamic_cast(device)); + properties->set_supports_params(device->SupportsParams()); + + PbDeviceType t = UNDEFINED; + PbDeviceType_Parse(device->GetType(), &t); + + if (device->SupportsParams()) { + for (const auto& param : device_factory->GetDefaultParams(t)) { + auto& map = *properties->mutable_default_params(); + map[param.first] = param.second; + } + } + + for (const auto& block_size : device_factory->GetSectorSizes(t)) { + properties->add_block_sizes(block_size); + } + + return properties; +} + +void RascsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_info, PbDeviceType type) +{ + PbDeviceTypeProperties *type_properties = device_types_info.add_properties(); + type_properties->set_type(type); + Device *device = device_factory->CreateDevice(type, ""); + type_properties->set_allocated_properties(GetDeviceProperties(device)); + delete device; +} + +void RascsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info) +{ + GetDeviceTypeProperties(device_types_info, SAHD); + GetDeviceTypeProperties(device_types_info, SCHD); + GetDeviceTypeProperties(device_types_info, SCRM); + GetDeviceTypeProperties(device_types_info, SCMO); + GetDeviceTypeProperties(device_types_info, SCCD); + GetDeviceTypeProperties(device_types_info, SCBR); + GetDeviceTypeProperties(device_types_info, SCDP); +} + +void RascsiResponse::GetDevice(const Device *device, PbDevice *pb_device) +{ + pb_device->set_id(device->GetId()); + pb_device->set_unit(device->GetLun()); + pb_device->set_vendor(device->GetVendor()); + pb_device->set_product(device->GetProduct()); + pb_device->set_revision(device->GetRevision()); + + PbDeviceType type = UNDEFINED; + PbDeviceType_Parse(device->GetType(), &type); + pb_device->set_type(type); + + pb_device->set_allocated_properties(GetDeviceProperties(device)); + + PbDeviceStatus *status = new PbDeviceStatus(); + pb_device->set_allocated_status(status); + status->set_protected_(device->IsProtected()); + status->set_stopped(device->IsStopped()); + status->set_removed(device->IsRemoved()); + status->set_locked(device->IsLocked()); + + if (device->SupportsParams()) { + for (const auto& param : device->GetParams()) { + AddParam(*pb_device, param.first, param.second); + } + } + + const Disk *disk = dynamic_cast(device); + if (disk) { + pb_device->set_block_size(device->IsRemoved()? 0 : disk->GetSectorSizeInBytes()); + pb_device->set_block_count(device->IsRemoved() ? 0: disk->GetBlockCount()); + } + + const FileSupport *file_support = dynamic_cast(device); + if (file_support) { + Filepath filepath; + file_support->GetPath(filepath); + PbImageFile *image_file = new PbImageFile(); + GetImageFile(image_file, device->IsRemovable() && !device->IsReady() ? "" : filepath.GetPath()); + pb_device->set_allocated_file(image_file); + } +} + +bool RascsiResponse::GetImageFile(PbImageFile *image_file, const string& filename) +{ + if (!filename.empty()) { + image_file->set_name(filename); + image_file->set_type(device_factory->GetTypeForFile(filename)); + + string f = filename[0] == '/' ? filename : rascsi_image->GetDefaultImageFolder() + "/" + filename; + + image_file->set_read_only(access(f.c_str(), W_OK)); + + struct stat st; + if (!stat(f.c_str(), &st) && !S_ISDIR(st.st_mode)) { + image_file->set_size(st.st_size); + return true; + } + } + + return false; +} + +PbImageFilesInfo *RascsiResponse::GetAvailableImages(PbResult& result) +{ + PbImageFilesInfo *image_files_info = new PbImageFilesInfo(); + + string default_image_folder = rascsi_image->GetDefaultImageFolder(); + image_files_info->set_default_image_folder(default_image_folder); + + // filesystem::directory_iterator cannot be used because libstdc++ 8.3.0 does not support big files + DIR *d = opendir(default_image_folder.c_str()); + if (d) { + struct dirent *dir; + while ((dir = readdir(d))) { + if (dir->d_type == DT_REG || dir->d_type == DT_LNK || dir->d_type == DT_BLK) { + string filename = default_image_folder + "/" + dir->d_name; + + struct stat st; + if (dir->d_type == DT_REG && !stat(filename.c_str(), &st)) { + if (!st.st_size) { + LOGTRACE("File '%s' in image folder '%s' has a size of 0 bytes", dir->d_name, default_image_folder.c_str()); + continue; + } + } else if (dir->d_type == DT_LNK && stat(filename.c_str(), &st)) { + LOGTRACE("Symlink '%s' in image folder '%s' is broken", dir->d_name, default_image_folder.c_str()); + continue; + } + + PbImageFile *image_file = new PbImageFile(); + if (GetImageFile(image_file, dir->d_name)) { + GetImageFile(image_files_info->add_image_files(), dir->d_name); + } + delete image_file; + } + } + + closedir(d); + } + + result.set_status(true); + + return image_files_info; +} + +void RascsiResponse::GetAvailableImages(PbResult& result, PbServerInfo& server_info) +{ + PbImageFilesInfo *image_files_info = GetAvailableImages(result); + image_files_info->set_default_image_folder(rascsi_image->GetDefaultImageFolder()); + server_info.set_allocated_image_files_info(image_files_info); + + result.set_status(true); +} + +PbReservedIdsInfo *RascsiResponse::GetReservedIds(PbResult& result, const set& ids) +{ + PbReservedIdsInfo *reserved_ids_info = new PbReservedIdsInfo(); + for (int id : ids) { + reserved_ids_info->add_ids(id); + } + + result.set_status(true); + + return reserved_ids_info; +} + +void RascsiResponse::GetDevices(PbServerInfo& server_info, const vector& devices) +{ + for (const Device *device : devices) { + // Skip if unit does not exist or is not assigned + if (device) { + PbDevice *pb_device = server_info.mutable_devices_info()->add_devices(); + GetDevice(device, pb_device); + } + } +} + +void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command, const vector& devices, + int unit_count) +{ + set id_sets; + if (!command.devices_size()) { + for (const Device *device : devices) { + if (device) { + id_sets.insert(make_pair(device->GetId(), device->GetLun())); + } + } + } + else { + for (const auto& device : command.devices()) { + if (devices[device.id() * unit_count + device.unit()]) { + id_sets.insert(make_pair(device.id(), device.unit())); + } + else { + ostringstream error; + error << "No device for ID " << device.id() << ", unit " << device.unit(); + result.set_status(false); + result.set_msg(error.str()); + return; + } + } + } + + PbDevicesInfo *devices_info = new PbDevicesInfo(); + result.set_allocated_devices_info(devices_info); + + for (const auto& id_set : id_sets) { + const Device *device = devices[id_set.first * unit_count + id_set.second]; + GetDevice(device, devices_info->add_devices()); + } + + result.set_status(true); +} + +PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result, const PbCommand& command) +{ + PbDeviceTypesInfo *device_types_info = new PbDeviceTypesInfo(); + + GetAllDeviceTypeProperties(*device_types_info); + + result.set_status(true); + + return device_types_info; +} + +PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const vector& devices, const set& reserved_ids, + const string& current_log_level) +{ + PbServerInfo *server_info = new PbServerInfo(); + + server_info->set_allocated_version_info(GetVersionInfo(result)); + server_info->set_allocated_log_level_info(GetLogLevelInfo(result, current_log_level)); + GetAllDeviceTypeProperties(*server_info->mutable_device_types_info()); + GetAvailableImages(result, *server_info); + server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result)); + server_info->set_allocated_mapping_info(GetMappingInfo(result)); + GetDevices(*server_info, devices); + server_info->set_allocated_reserved_ids_info(GetReservedIds(result, reserved_ids)); + + result.set_status(true); + + return server_info; +} + +PbVersionInfo *RascsiResponse::GetVersionInfo(PbResult& result) +{ + PbVersionInfo *version_info = new PbVersionInfo(); + + version_info->set_major_version(rascsi_major_version); + version_info->set_minor_version(rascsi_minor_version); + version_info->set_patch_version(rascsi_patch_version); + + result.set_status(true); + + return version_info; +} + +PbLogLevelInfo *RascsiResponse::GetLogLevelInfo(PbResult& result, const string& current_log_level) +{ + PbLogLevelInfo *log_level_info = new PbLogLevelInfo(); + + for (const auto& log_level : log_levels) { + log_level_info->add_log_levels(log_level); + } + + log_level_info->set_current_log_level(current_log_level); + + result.set_status(true); + + return log_level_info; +} + +PbNetworkInterfacesInfo *RascsiResponse::GetNetworkInterfacesInfo(PbResult& result) +{ + PbNetworkInterfacesInfo *network_interfaces_info = new PbNetworkInterfacesInfo(); + + for (const auto& network_interface : device_factory->GetNetworkInterfaces()) { + network_interfaces_info->add_name(network_interface); + } + + result.set_status(true); + + return network_interfaces_info; +} + +PbMappingInfo *RascsiResponse::GetMappingInfo(PbResult& result) +{ + PbMappingInfo *mapping_info = new PbMappingInfo(); + + for (const auto& mapping : device_factory->GetExtensionMapping()) { + (*mapping_info->mutable_mapping())[mapping.first] = mapping.second; + } + + result.set_status(true); + + return mapping_info; +} diff --git a/src/raspberrypi/rascsi_response.h b/src/raspberrypi/rascsi_response.h new file mode 100644 index 00000000..12f4ea44 --- /dev/null +++ b/src/raspberrypi/rascsi_response.h @@ -0,0 +1,55 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "devices/device_factory.h" +#include "rascsi_interface.pb.h" +#include +#include + +using namespace std; +using namespace rascsi_interface; + +class DeviceFactory; +class RascsiImage; +class Device; + +class RascsiResponse +{ +public: + + RascsiResponse(DeviceFactory *, const RascsiImage *); + ~RascsiResponse() {}; + + bool GetImageFile(PbImageFile *, const string&); + PbImageFilesInfo *GetAvailableImages(PbResult&); + PbReservedIdsInfo *GetReservedIds(PbResult&, const set&); + void GetDevices(PbServerInfo&, const vector&); + void GetDevicesInfo(PbResult&, const PbCommand&, const vector&, int); + PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&); + PbVersionInfo *GetVersionInfo(PbResult&); + PbServerInfo *GetServerInfo(PbResult&, const vector&, const set&, const string&); + PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&); + PbMappingInfo *GetMappingInfo(PbResult&); + PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&); + +private: + + DeviceFactory *device_factory; + const RascsiImage *rascsi_image; + + vector log_levels; + + PbDeviceProperties *GetDeviceProperties(const Device *); + void GetDevice(const Device *, PbDevice *); + void GetAllDeviceTypeProperties(PbDeviceTypesInfo&); + void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType); + void GetAvailableImages(PbResult& result, PbServerInfo&); +}; diff --git a/src/raspberrypi/rascsi_version.cpp b/src/raspberrypi/rascsi_version.cpp index 959f73c5..09f5ab97 100644 --- a/src/raspberrypi/rascsi_version.cpp +++ b/src/raspberrypi/rascsi_version.cpp @@ -4,7 +4,7 @@ // for Raspberry Pi // // Copyright (C) 2020 akuker -// [ Define the version string] +// [ Define the version string ] // //--------------------------------------------------------------------------- @@ -14,8 +14,8 @@ // The following should be updated for each release const int rascsi_major_version = 21; // Last two digits of year -const int rascsi_minor_version = 05; // Month -const int rascsi_patch_version = -1; // Patch number - increment for each update +const int rascsi_minor_version = 10; // Month +const int rascsi_patch_version = 1; // Patch number - increment for each update static char rascsi_version_string[30]; // Allow for string up to "XX.XX.XXX" + null character + "development build" @@ -24,7 +24,7 @@ static char rascsi_version_string[30]; // Allow for string up to "XX.XX.XXX" + n // Get the RaSCSI version string // //--------------------------------------------------------------------------- -char* rascsi_get_version_string() +const char* rascsi_get_version_string() { if(rascsi_patch_version < 0) { @@ -42,3 +42,5 @@ char* rascsi_get_version_string() } return rascsi_version_string; } + + diff --git a/src/raspberrypi/rascsi_version.h b/src/raspberrypi/rascsi_version.h index 17a2d345..a39ced4e 100644 --- a/src/raspberrypi/rascsi_version.h +++ b/src/raspberrypi/rascsi_version.h @@ -4,7 +4,7 @@ // for Raspberry Pi // // Copyright (C) 2020 akuker -// [ Define the version string] +// [ Define the version string ] // //--------------------------------------------------------------------------- #pragma once @@ -18,4 +18,4 @@ extern const int rascsi_patch_version; // Patch number // Fetch the version string // //--------------------------------------------------------------------------- -extern char* rascsi_get_version_string(); +const char* rascsi_get_version_string(); diff --git a/src/raspberrypi/rasctl.1 b/src/raspberrypi/rasctl.1 deleted file mode 100644 index 9bd89f78..00000000 --- a/src/raspberrypi/rasctl.1 +++ /dev/null @@ -1,14 +0,0 @@ -.TH rascsi 1 -.SH NAME -rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins -.SH SYNOPSIS -.B rascsi -[\fB\-HDn\fR \fIfile\fR] ... -.SH DESCRIPTION -.B corrupt -modifies files by toggling a randomly chosen bit. -.SH OPTIONS -.TP -.BR \-n ", " \-\-bits =\fIBITS\fR -Set the number of bits to modify. -Default is one bit. \ No newline at end of file diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index c99e5d01..f56c42b9 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -5,261 +5,415 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS +// Copyright (C) 2020-2021 Contributors to the RaSCSI project // [ Send Control Command ] // //--------------------------------------------------------------------------- #include "os.h" #include "rascsi_version.h" +#include "protobuf_util.h" +#include "rasutil.h" +#include "rasctl_commands.h" +#include "rascsi_interface.pb.h" +#include +#include -//--------------------------------------------------------------------------- -// -// Send Command -// -//--------------------------------------------------------------------------- -BOOL SendCommand(char *buf) +// Separator for the INQUIRY name components +#define COMPONENT_SEPARATOR ':' + +using namespace std; +using namespace rascsi_interface; +using namespace ras_util; +using namespace protobuf_util; + +PbOperation ParseOperation(const char *optarg) { - int fd; - struct sockaddr_in server; - FILE *fp; + switch (tolower(optarg[0])) { + case 'a': + return ATTACH; - // Create a socket to send the command - fd = socket(PF_INET, SOCK_STREAM, 0); - memset(&server, 0, sizeof(server)); - server.sin_family = PF_INET; - server.sin_port = htons(6868); - server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + case 'd': + return DETACH; - // Connect - if (connect(fd, (struct sockaddr *)&server, - sizeof(struct sockaddr_in)) < 0) { - fprintf(stderr, "Error : Can't connect to rascsi process\n"); - return FALSE; + case 'i': + return INSERT; + + case 'e': + return EJECT; + + case 'p': + return PROTECT; + + case 'u': + return UNPROTECT; + + case 's': + return DEVICES_INFO; + + default: + return NONE; } - - // Send the command - fp = fdopen(fd, "r+"); - setvbuf(fp, NULL, _IONBF, 0); - fputs(buf, fp); - - // Receive the message - while (1) { - if (fgets((char *)buf, BUFSIZ, fp) == NULL) { - break; - } - printf("%s", buf); - } - - // Close the socket when we're done - fclose(fp); - close(fd); - - return TRUE; } -//--------------------------------------------------------------------------- -// -// Main processing -// -//--------------------------------------------------------------------------- +PbDeviceType ParseType(const char *optarg) +{ + string t = optarg; + transform(t.begin(), t.end(), t.begin(), ::toupper); + + PbDeviceType type; + if (PbDeviceType_Parse(t, &type)) { + return type; + } + else { + // Parse convenience device types (shortcuts) + switch (tolower(optarg[0])) { + case 'c': + return SCCD; + + case 'b': + return SCBR; + + case 'd': + return SCDP; + + case 'h': + return SCHD; + + case 'm': + return SCMO; + + case 'r': + return SCRM; + } + } + + return UNDEFINED; +} + int main(int argc, char* argv[]) { - int opt; - int id; - int un; - int cmd; - int type; - char *file; - BOOL list; - int len; - char *ext; - char buf[BUFSIZ]; - - id = -1; - un = 0; - cmd = -1; - type = -1; - file = NULL; - list = FALSE; + GOOGLE_PROTOBUF_VERIFY_VERSION; // Display help if (argc < 2) { - fprintf(stderr, "SCSI Target Emulator RaSCSI Controller\n"); - fprintf(stderr, "version %s (%s, %s)\n", - rascsi_get_version_string(), - __DATE__, - __TIME__); - fprintf(stderr, - "Usage: %s -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE]\n", - argv[0]); - fprintf(stderr, " where ID := {0|1|2|3|4|5|6|7}\n"); - fprintf(stderr, " UNIT := {0|1} default setting is 0.\n"); - fprintf(stderr, " CMD := {attach|detach|insert|eject|protect}\n"); - fprintf(stderr, " TYPE := {hd|mo|cd|bridge}\n"); - fprintf(stderr, " FILE := image file path\n"); - fprintf(stderr, " CMD is 'attach' or 'insert' and FILE parameter is required.\n"); - fprintf(stderr, "Usage: %s -l\n", argv[0]); - fprintf(stderr, " Print device list.\n"); - exit(0); + cerr << "SCSI Target Emulator RaSCSI Controller" << endl; + cerr << "version " << rascsi_get_version_string() << " (" << __DATE__ << ", " << __TIME__ << ")" << endl; + cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-C FILE] [-t TYPE] [-b BLOCK_SIZE] [-n NAME] [-f FILE|PARAM] "; + cerr << "[-F IMAGE_FOLDER] [-L LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] "; + cerr << "[-C FILENAME:FILESIZE] [-d FILENAME] [-w FILENAME] [-R CURRENT_NAME:NEW_NAME] [-x CURRENT_NAME:NEW_NAME] "; + cerr << "[-e] [-E FILENAME] [-D] [-I] [-l] [-L] [-m] [-O] [-s] [-v] [-V] [-y] [-X]" << endl; + cerr << " where ID := {0-7}" << endl; + cerr << " UNIT := {0-31}, default is 0" << endl; + cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl; + cerr << " TYPE := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}" << endl; + cerr << " BLOCK_SIZE := {256|512|1024|2048|4096) bytes per hard disk drive block" << endl; + cerr << " NAME := name of device to attach (VENDOR:PRODUCT:REVISION)" << endl; + cerr << " FILE|PARAM := image file path or device-specific parameter" << endl; + cerr << " IMAGE_FOLDER := default location for image files, default is '~/images'" << endl; + cerr << " HOST := rascsi host to connect to, default is 'localhost'" << endl; + cerr << " PORT := rascsi port to connect to, default is 6868" << endl; + cerr << " RESERVED_IDS := comma-separated list of IDs to reserve" << endl; + cerr << " LOG_LEVEL := log level {trace|debug|info|warn|err|critical|off}, default is 'info'" << endl; + cerr << " If CMD is 'attach' or 'insert' the FILE parameter is required." << endl; + cerr << "Usage: " << argv[0] << " -l" << endl; + cerr << " Print device list." << endl; + + exit(EXIT_SUCCESS); } - // Parse the arguments - opterr = 0; - while ((opt = getopt(argc, argv, "i:u:c:t:f:l")) != -1) { + PbCommand command; + list devices; + PbDeviceDefinition* device = command.add_devices(); + device->set_id(-1); + const char *hostname = "localhost"; + int port = 6868; + string param; + string log_level; + string default_folder; + string reserved_ids; + string image_params; + string filename; + bool list = false; + + opterr = 1; + int opt; + while ((opt = getopt(argc, argv, "elmsvDINOTVXa:b:c:d:f:h:i:n:p:r:t:u:x:C:E:F:L:R:")) != -1) { switch (opt) { - case 'i': - id = optarg[0] - '0'; + case 'i': { + int id; + if (!GetAsInt(optarg, id)) { + cerr << "Error: Invalid device ID " << optarg << endl; + exit(EXIT_FAILURE); + } + device->set_id(id); + break; + } + + case 'u': { + int unit; + if (!GetAsInt(optarg, unit)) { + cerr << "Error: Invalid unit " << optarg << endl; + exit(EXIT_FAILURE); + } + device->set_unit(unit); + break; + } + + case 'C': + command.set_operation(CREATE_IMAGE); + image_params = optarg; break; - case 'u': - un = optarg[0] - '0'; + case 'b': + int block_size; + if (!GetAsInt(optarg, block_size)) { + cerr << "Error: Invalid block size " << optarg << endl; + exit(EXIT_FAILURE); + } + device->set_block_size(block_size); break; case 'c': - switch (optarg[0]) { - case 'a': // ATTACH - case 'A': - cmd = 0; - break; - case 'd': // DETACH - case 'D': - cmd = 1; - break; - case 'i': // INSERT - case 'I': - cmd = 2; - break; - case 'e': // EJECT - case 'E': - cmd = 3; - break; - case 'p': // PROTECT - case 'P': - cmd = 4; - break; + command.set_operation(ParseOperation(optarg)); + if (command.operation() == NONE) { + cerr << "Error: Unknown operation '" << optarg << "'" << endl; + exit(EXIT_FAILURE); } break; - case 't': - switch (optarg[0]) { - case 's': // HD(SASI) - case 'S': - case 'h': // HD(SCSI) - case 'H': - type = 0; - break; - case 'm': // MO - case 'M': - type = 2; - break; - case 'c': // CD - case 'C': - type = 3; - break; - case 'b': // BRIDGE - case 'B': - type = 4; - break; - } + case 'D': + command.set_operation(DETACH_ALL); + break; + + case 'd': + command.set_operation(DELETE_IMAGE); + image_params = optarg; + break; + + case 'E': + command.set_operation(IMAGE_FILE_INFO); + filename = optarg; + break; + + case 'e': + command.set_operation(DEFAULT_IMAGE_FILES_INFO); + break; + + case 'F': + command.set_operation(DEFAULT_FOLDER); + default_folder = optarg; break; case 'f': - file = optarg; + param = optarg; + break; + + case 'h': + hostname = optarg; + break; + + case 'I': + command.set_operation(RESERVED_IDS_INFO); + break; + + case 'L': + command.set_operation(LOG_LEVEL); + log_level = optarg; break; case 'l': - list = TRUE; + list = true; + break; + + case 'm': + command.set_operation(MAPPING_INFO); + break; + + case 'N': + command.set_operation(NETWORK_INTERFACES_INFO); + break; + + case 'O': + command.set_operation(LOG_LEVEL_INFO); + break; + + case 't': + device->set_type(ParseType(optarg)); + if (device->type() == UNDEFINED) { + cerr << "Error: Unknown device type '" << optarg << "'" << endl; + exit(EXIT_FAILURE); + } + break; + + case 'r': + command.set_operation(RESERVE_IDS); + reserved_ids = optarg; + break; + + case 'R': + command.set_operation(RENAME_IMAGE); + image_params = optarg; + break; + + case 'n': { + string vendor; + string product; + string revision; + + string s = optarg; + size_t separator_pos = s.find(COMPONENT_SEPARATOR); + if (separator_pos != string::npos) { + vendor = s.substr(0, separator_pos); + s = s.substr(separator_pos + 1); + separator_pos = s.find(COMPONENT_SEPARATOR); + if (separator_pos != string::npos) { + product = s.substr(0, separator_pos); + revision = s.substr(separator_pos + 1); + } + else { + product = s; + } + } + else { + vendor = s; + } + + device->set_vendor(vendor); + device->set_product(product); + device->set_revision(revision); + } + break; + + case 'p': + if (!GetAsInt(optarg, port) || port <= 0 || port > 65535) { + cerr << "Error: Invalid port " << optarg << ", port must be between 1 and 65535" << endl; + exit(EXIT_FAILURE); + } + break; + + case 's': + command.set_operation(SERVER_INFO); + break; + + case 'v': + cout << "rasctl version: " << rascsi_get_version_string() << endl; + exit(EXIT_SUCCESS); + break; + + case 'V': + command.set_operation(VERSION_INFO); + break; + + case 'x': + command.set_operation(COPY_IMAGE); + image_params = optarg; + break; + + case 'T': + command.set_operation(DEVICE_TYPES_INFO); + break; + + case 'X': + command.set_operation(SHUT_DOWN); break; } } - // List display only - if (id < 0 && cmd < 0 && type < 0 && file == NULL && list) { - sprintf(buf, "list\n"); - SendCommand(buf); - exit(0); + if (optopt) { + exit(EXIT_FAILURE); } - // Check the ID number - if (id < 0 || id > 7) { - fprintf(stderr, "Error : Invalid ID\n"); - exit(EINVAL); - } - - // Check the unit number - if (un < 0 || un > 1) { - fprintf(stderr, "Error : Invalid UNIT\n"); - exit(EINVAL); - } - - // Command check - if (cmd < 0) { - cmd = 0; // Default command is ATTATCH - } - - // Type Check - if (cmd == 0 && type < 0) { - - // Try to determine the file type from the extension - len = file ? strlen(file) : 0; - if (len > 4 && file[len - 4] == '.') { - ext = &file[len - 3]; - if (xstrcasecmp(ext, "hdf") == 0 || - xstrcasecmp(ext, "hds") == 0 || - xstrcasecmp(ext, "hdn") == 0 || - xstrcasecmp(ext, "hdi") == 0 || - xstrcasecmp(ext, "nhd") == 0 || - xstrcasecmp(ext, "hda") == 0) { - // HD(SASI/SCSI) - type = 0; - } else if (xstrcasecmp(ext, "mos") == 0) { - // MO - type = 2; - } else if (xstrcasecmp(ext, "iso") == 0) { - // CD - type = 3; - } - } - - if (type < 0) { - fprintf(stderr, "Error : Invalid type\n"); - exit(EINVAL); - } - } - - // File check (command is ATTACH and type is HD) - if (cmd == 0 && type >= 0 && type <= 1) { - if (!file) { - fprintf(stderr, "Error : Invalid file path\n"); - exit(EINVAL); - } - } - - // File check (command is INSERT) - if (cmd == 2) { - if (!file) { - fprintf(stderr, "Error : Invalid file path\n"); - exit(EINVAL); - } - } - - // Set unnecessary type to 0 - if (type < 0) { - type = 0; - } - - // Generate the command and send it - sprintf(buf, "%d %d %d %d %s\n", id, un, cmd, type, file ? file : "-"); - if (!SendCommand(buf)) { - exit(ENOTCONN); - } - - // Display the list + // Listing devices is a special case (rasctl backwards compatibility) if (list) { - sprintf(buf, "list\n"); - SendCommand(buf); + PbCommand command_list; + command_list.set_operation(DEVICES_INFO); + RasctlCommands rasctl_commands(command_list, hostname, port); + rasctl_commands.CommandDevicesInfo(); + exit(EXIT_SUCCESS); } - // All done! - exit(0); + if (!param.empty()) { + // Only one of these parameters will be used, depending on the device type + AddParam(*device, "interfaces", param); + AddParam(*device, "file", param); + } + + RasctlCommands rasctl_commands(command, hostname, port); + + switch(command.operation()) { + case LOG_LEVEL: + rasctl_commands.CommandLogLevel(log_level); + break; + + case DEFAULT_FOLDER: + rasctl_commands.CommandDefaultImageFolder(default_folder); + break; + + case RESERVE_IDS: + rasctl_commands.CommandReserveIds(reserved_ids); + break; + + case CREATE_IMAGE: + rasctl_commands.CommandCreateImage(image_params); + break; + + case DELETE_IMAGE: + rasctl_commands.CommandDeleteImage(image_params); + break; + + case RENAME_IMAGE: + rasctl_commands.CommandRenameImage(image_params); + break; + + case COPY_IMAGE: + rasctl_commands.CommandCopyImage(image_params); + break; + + case DEVICES_INFO: + rasctl_commands.CommandDeviceInfo(); + break; + + case DEVICE_TYPES_INFO: + rasctl_commands.CommandDeviceTypesInfo(); + break; + + case VERSION_INFO: + rasctl_commands.CommandVersionInfo(); + break; + + case SERVER_INFO: + rasctl_commands.CommandServerInfo(); + break; + + case DEFAULT_IMAGE_FILES_INFO: + rasctl_commands.CommandDefaultImageFilesInfo(); + break; + + case IMAGE_FILE_INFO: + rasctl_commands.CommandImageFileInfo(hostname); + break; + + case NETWORK_INTERFACES_INFO: + rasctl_commands.CommandNetworkInterfacesInfo(); + break; + + case LOG_LEVEL_INFO: + rasctl_commands.CommandLogLevelInfo(); + break; + + case RESERVED_IDS_INFO: + rasctl_commands.CommandReservedIdsInfo(); + break; + + case MAPPING_INFO: + rasctl_commands.CommandMappingInfo(); + break; + + default: + rasctl_commands.SendCommand(); + break; + } + + exit(EXIT_SUCCESS); } diff --git a/src/raspberrypi/rasctl_commands.cpp b/src/raspberrypi/rasctl_commands.cpp new file mode 100644 index 00000000..0302520b --- /dev/null +++ b/src/raspberrypi/rasctl_commands.cpp @@ -0,0 +1,275 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include +#include "os.h" +#include "exceptions.h" +#include "protobuf_util.h" +#include "rasutil.h" +#include "rasctl_commands.h" +#include "rascsi_interface.pb.h" +#include +#include +#include + +// Separator for the INQUIRY name components +#define COMPONENT_SEPARATOR ':' + +using namespace std; +using namespace rascsi_interface; +using namespace protobuf_util; + +RasctlCommands::RasctlCommands(PbCommand& command, const string& hostname, int port) +{ + this->command = command; + this->hostname = hostname; + this->port = port; +} + +void RasctlCommands::SendCommand() +{ + // Send command + int fd = -1; + try { + struct hostent *host = gethostbyname(hostname.c_str()); + if (!host) { + throw io_exception("Can't resolve hostname '" + hostname + "'"); + } + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + throw io_exception("Can't create socket"); + } + + struct sockaddr_in server; + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_port = htons(port); + server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + memcpy(&server.sin_addr.s_addr, host->h_addr, host->h_length); + + if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { + ostringstream error; + error << "Can't connect to rascsi process on host '" << hostname << "', port " << port; + throw io_exception(error.str()); + } + + if (write(fd, "RASCSI", 6) != 6) { + throw io_exception("Can't write magic"); + } + + SerializeMessage(fd, command); + } + catch(const io_exception& e) { + cerr << "Error: " << e.getmsg() << endl; + + if (fd >= 0) { + close(fd); + } + + exit(fd < 0 ? ENOTCONN : EXIT_FAILURE); + } + + // Receive result + try { + DeserializeMessage(fd, result); + + if (!result.status()) { + throw io_exception(result.msg()); + } + } + catch(const io_exception& e) { + close(fd); + + cerr << "Error: " << e.getmsg() << endl; + + exit(EXIT_FAILURE); + } + + close(fd); + + if (!result.msg().empty()) { + cout << result.msg() << endl; + } +} + +void RasctlCommands::CommandDevicesInfo() +{ + SendCommand(); + + rasctl_display.DisplayDevices(result.devices_info()); +} + +void RasctlCommands::CommandLogLevel(const string& log_level) +{ + AddParam(command, "level", log_level); + + SendCommand(); +} + +void RasctlCommands::CommandReserveIds(const string& reserved_ids) +{ + AddParam(command, "ids", reserved_ids); + + SendCommand(); +} + +void RasctlCommands::CommandCreateImage(const string& image_params) +{ + size_t separatorPos = image_params.find(COMPONENT_SEPARATOR); + if (separatorPos != string::npos) { + AddParam(command, "file", image_params.substr(0, separatorPos)); + AddParam(command, "size", image_params.substr(separatorPos + 1)); + } + else { + cerr << "Error: Invalid file descriptor '" << image_params << "', format is NAME:SIZE" << endl; + exit(EXIT_FAILURE); + } + + AddParam(command, "read_only", "false"); + + SendCommand(); +} + +void RasctlCommands::CommandDeleteImage(const string& filename) +{ + AddParam(command, "file", filename); + + SendCommand(); +} + +void RasctlCommands::CommandRenameImage(const string& image_params) +{ + size_t separatorPos = image_params.find(COMPONENT_SEPARATOR); + if (separatorPos != string::npos) { + AddParam(command, "from", image_params.substr(0, separatorPos)); + AddParam(command, "to", image_params.substr(separatorPos + 1)); + } + else { + cerr << "Error: Invalid file descriptor '" << image_params << "', format is CURRENT_NAME:NEW_NAME" << endl; + exit(EXIT_FAILURE); + } + + SendCommand(); +} + +void RasctlCommands::CommandCopyImage(const string& image_params) +{ + size_t separatorPos = image_params.find(COMPONENT_SEPARATOR); + if (separatorPos != string::npos) { + AddParam(command, "from", image_params.substr(0, separatorPos)); + AddParam(command, "to", image_params.substr(separatorPos + 1)); + } + else { + cerr << "Error: Invalid file descriptor '" << image_params << "', format is CURRENT_NAME:NEW_NAME" << endl; + exit(EXIT_FAILURE); + } + + SendCommand(); +} + +void RasctlCommands::CommandDefaultImageFolder(const string& folder) +{ + AddParam(command, "folder", folder); + + SendCommand(); +} + +void RasctlCommands::CommandDeviceInfo() +{ + SendCommand(); + + for (const auto& device : result.devices_info().devices()) { + rasctl_display.DisplayDeviceInfo(device); + } +} + +void RasctlCommands::CommandDeviceTypesInfo() +{ + SendCommand(); + + rasctl_display.DisplayDeviceTypesInfo(result.device_types_info()); +} + +void RasctlCommands::CommandVersionInfo() +{ + SendCommand(); + + rasctl_display.DisplayVersionInfo(result.version_info()); +} + +void RasctlCommands::CommandServerInfo() +{ + SendCommand(); + + PbServerInfo server_info = result.server_info(); + + rasctl_display.DisplayVersionInfo(server_info.version_info()); + rasctl_display.DisplayLogLevelInfo(server_info.log_level_info()); + rasctl_display.DisplayImageFiles(server_info.image_files_info()); + rasctl_display.DisplayMappingInfo(server_info.mapping_info()); + rasctl_display.DisplayNetworkInterfaces(server_info.network_interfaces_info()); + rasctl_display.DisplayDeviceTypesInfo(server_info.device_types_info()); + rasctl_display.DisplayReservedIdsInfo(server_info.reserved_ids_info()); + + if (server_info.devices_info().devices_size()) { + list sorted_devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() }; + sorted_devices.sort([](const auto& a, const auto& b) { return a.id() < b.id(); }); + + cout << "Attached devices:" << endl; + + for (const auto& device : sorted_devices) { + rasctl_display.DisplayDeviceInfo(device); + } + } +} + +void RasctlCommands::CommandDefaultImageFilesInfo() +{ + SendCommand(); + + rasctl_display.DisplayImageFiles(result.image_files_info()); +} + +void RasctlCommands::CommandImageFileInfo(const string& filename) +{ + AddParam(command, "file", filename); + + SendCommand(); + + rasctl_display.DisplayImageFile(result.image_file_info()); +} + +void RasctlCommands::CommandNetworkInterfacesInfo() +{ + SendCommand(); + + rasctl_display.DisplayNetworkInterfaces(result.network_interfaces_info()); +} + +void RasctlCommands::CommandLogLevelInfo() +{ + SendCommand(); + + rasctl_display.DisplayLogLevelInfo(result.log_level_info()); +} + +void RasctlCommands::CommandReservedIdsInfo() +{ + SendCommand(); + + rasctl_display.DisplayReservedIdsInfo(result.reserved_ids_info()); +} + +void RasctlCommands::CommandMappingInfo() +{ + SendCommand(); + + rasctl_display.DisplayMappingInfo(result.mapping_info()); +} diff --git a/src/raspberrypi/rasctl_commands.h b/src/raspberrypi/rasctl_commands.h new file mode 100644 index 00000000..0704191c --- /dev/null +++ b/src/raspberrypi/rasctl_commands.h @@ -0,0 +1,55 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "rascsi_interface.pb.h" +#include "rasctl_display.h" +#include + +using namespace std; +using namespace rascsi_interface; + +class RasctlCommands +{ +public: + + RasctlCommands(PbCommand&, const string&, int); + ~RasctlCommands() {}; + + void SendCommand(); + void CommandDevicesInfo(); + void CommandLogLevel(const string&); + void CommandReserveIds(const string&); + void CommandCreateImage(const string&); + void CommandDeleteImage(const string&); + void CommandRenameImage(const string&); + void CommandCopyImage(const string&); + void CommandDefaultImageFolder(const string&); + void CommandDeviceInfo(); + void CommandDeviceTypesInfo(); + void CommandVersionInfo(); + void CommandServerInfo(); + void CommandDefaultImageFilesInfo(); + void CommandImageFileInfo(const string&); + void CommandNetworkInterfacesInfo(); + void CommandLogLevelInfo(); + void CommandReservedIdsInfo(); + void CommandMappingInfo(); + +private: + + PbCommand command; + string hostname; + int port; + + PbResult result; + + RasctlDisplay rasctl_display; +}; diff --git a/src/raspberrypi/rasctl_display.cpp b/src/raspberrypi/rasctl_display.cpp new file mode 100644 index 00000000..d9e6036c --- /dev/null +++ b/src/raspberrypi/rasctl_display.cpp @@ -0,0 +1,273 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "rascsi_interface.pb.h" +#include "rasutil.h" +#include "rasctl_display.h" +#include +#include + +using namespace std; +using namespace rascsi_interface; +using namespace ras_util; + +void RasctlDisplay::DisplayDevices(const PbDevicesInfo& devices_info) +{ + const list& devices = { devices_info.devices().begin(), devices_info.devices().end() }; + cout << ListDevices(devices) << endl; +} + +void RasctlDisplay::DisplayDeviceInfo(const PbDevice& pb_device) +{ + cout << " " << pb_device.id() << ":" << pb_device.unit() << " " << PbDeviceType_Name(pb_device.type()) + << " " << pb_device.vendor() << ":" << pb_device.product() << ":" << pb_device.revision(); + + if (pb_device.block_size()) { + cout << " " << pb_device.block_size() << " bytes per sector"; + if (pb_device.block_count()) { + cout << " " << pb_device.block_size() * pb_device.block_count() << " bytes capacity"; + } + } + + if (pb_device.properties().supports_file() && !pb_device.file().name().empty()) { + cout << " " << pb_device.file().name(); + } + + cout << " "; + bool hasProperty = false; + if (pb_device.properties().read_only()) { + cout << "read-only"; + hasProperty = true; + } + if (pb_device.properties().protectable() && pb_device.status().protected_()) { + if (hasProperty) { + cout << ", "; + } + cout << "protected"; + hasProperty = true; + } + if (pb_device.properties().stoppable() && pb_device.status().stopped()) { + if (hasProperty) { + cout << ", "; + } + cout << "stopped"; + hasProperty = true; + } + if (pb_device.properties().removable() && pb_device.status().removed()) { + if (hasProperty) { + cout << ", "; + } + cout << "removed"; + hasProperty = true; + } + if (pb_device.properties().lockable() && pb_device.status().locked()) { + if (hasProperty) { + cout << ", "; + } + cout << "locked"; + } + if (hasProperty) { + cout << " "; + } + + bool isFirst = true; + for (const auto& param : pb_device.params()) { + if (!isFirst) { + cout << " "; + } + isFirst = false; + cout << param.first << "=" << param.second; + } + + cout << endl; +} + +void RasctlDisplay::DisplayVersionInfo(const PbVersionInfo& version_info) +{ + cout << "rascsi server version: " << version_info.major_version() << "." << version_info.minor_version(); + if (version_info.patch_version() > 0) { + cout << "." << version_info.patch_version(); + } + else if (version_info.patch_version() < 0) { + cout << " (development version)"; + } + cout << endl; +} + +void RasctlDisplay::DisplayLogLevelInfo(const PbLogLevelInfo& log_level_info) +{ + if (!log_level_info.log_levels_size()) { + cout << " No log level settings available" << endl; + } + else { + cout << "rascsi log levels, sorted by severity:" << endl; + for (const auto& log_level : log_level_info.log_levels()) { + cout << " " << log_level << endl; + } + } + + cout << "Current rascsi log level: " << log_level_info.current_log_level() << endl; +} + +void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types_info) +{ + cout << "Supported device types and their properties:" << endl; + for (auto it = device_types_info.properties().begin(); it != device_types_info.properties().end(); ++it) { + cout << " " << PbDeviceType_Name(it->type()); + + const PbDeviceProperties& properties = it->properties(); + + cout << " Supported LUN numbers: 0"; + if (properties.luns() > 1) { + cout << "-" << (properties.luns() - 1); + } + cout << endl; + + if (properties.read_only() || properties.protectable() || properties.stoppable() || properties.read_only() + || properties.lockable()) { + cout << " Properties: "; + bool has_property = false; + if (properties.read_only()) { + cout << "read-only"; + has_property = true; + } + if (properties.protectable()) { + cout << (has_property ? ", " : "") << "protectable"; + has_property = true; + } + if (properties.stoppable()) { + cout << (has_property ? ", " : "") << "stoppable"; + has_property = true; + } + if (properties.removable()) { + cout << (has_property ? ", " : "") << "removable"; + has_property = true; + } + if (properties.lockable()) { + cout << (has_property ? ", " : "") << "lockable"; + } + cout << endl; + } + + if (properties.supports_file()) { + cout << " Image file support" << endl; + } + else if (properties.supports_params()) { + cout << " Parameter support" << endl; + } + + if (properties.supports_params() && properties.default_params_size()) { + map params = { properties.default_params().begin(), properties.default_params().end() }; + + cout << " Default parameters: "; + + bool isFirst = true; + for (const auto& param : params) { + if (!isFirst) { + cout << ", "; + } + cout << param.first << "=" << param.second; + + isFirst = false; + } + cout << endl; + } + + if (properties.block_sizes_size()) { + set block_sizes = { properties.block_sizes().begin(), properties.block_sizes().end() }; + + cout << " Configurable block sizes in bytes: "; + + bool isFirst = true; + for (const auto& block_size : block_sizes) { + if (!isFirst) { + cout << ", "; + } + cout << block_size; + + isFirst = false; + } + cout << endl; + } + } +} + +void RasctlDisplay::DisplayReservedIdsInfo(const PbReservedIdsInfo& reserved_ids_info) +{ + if (reserved_ids_info.ids_size()) { + cout << "Reserved device IDs: "; + for (int i = 0; i < reserved_ids_info.ids_size(); i++) { + if(i) { + cout << ", "; + } + cout << reserved_ids_info.ids(i); + } + cout < image_files = { image_files_info.image_files().begin(), image_files_info.image_files().end() }; + + cout << "Default image file folder: " << image_files_info.default_image_folder() << endl; + + if (image_files.empty()) { + cout << " No image files available" << endl; + } + else { + list files = { image_files.begin(), image_files.end() }; + files.sort([](const auto& a, const auto& b) { return a.name() < b.name(); }); + + cout << "Available image files:" << endl; + for (const auto& file : files) { + cout << " "; + DisplayImageFile(file); + } + } +} + +void RasctlDisplay::DisplayNetworkInterfaces(const PbNetworkInterfacesInfo& network_interfaces_info) +{ + const list interfaces = { network_interfaces_info.name().begin(), network_interfaces_info.name().end() }; + + cout << "Available (up) network interfaces:" << endl; + bool isFirst = true; + for (const auto& interface : interfaces) { + if (!isFirst) { + cout << ", "; + } + isFirst = false; + cout << interface; + } + cout << endl; +} + +void RasctlDisplay::DisplayMappingInfo(const PbMappingInfo& mapping_info) +{ + const map mappings = { mapping_info.mapping().begin(), mapping_info.mapping().end() }; + + cout << "Supported image file extension to device type mappings:" << endl; + for (const auto& mapping : mappings) { + cout << " " << mapping.first << "->" << PbDeviceType_Name(mapping.second) << endl; + } +} diff --git a/src/raspberrypi/rasctl_display.h b/src/raspberrypi/rasctl_display.h new file mode 100644 index 00000000..9957e5fa --- /dev/null +++ b/src/raspberrypi/rasctl_display.h @@ -0,0 +1,33 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "rascsi_interface.pb.h" + +using namespace rascsi_interface; + +class RasctlDisplay +{ +public: + + RasctlDisplay() {}; + ~RasctlDisplay() {}; + + void DisplayDevices(const PbDevicesInfo&); + void DisplayDeviceInfo(const PbDevice&); + void DisplayVersionInfo(const PbVersionInfo&); + void DisplayLogLevelInfo(const PbLogLevelInfo&); + void DisplayDeviceTypesInfo(const PbDeviceTypesInfo&); + void DisplayReservedIdsInfo(const PbReservedIdsInfo&); + void DisplayImageFile(const PbImageFile&); + void DisplayImageFiles(const PbImageFilesInfo&); + void DisplayNetworkInterfaces(const PbNetworkInterfacesInfo&); + void DisplayMappingInfo(const PbMappingInfo&); +}; diff --git a/src/raspberrypi/rasdump.cpp b/src/raspberrypi/rasdump.cpp index 776103dc..f5dcb0e7 100644 --- a/src/raspberrypi/rasdump.cpp +++ b/src/raspberrypi/rasdump.cpp @@ -5,62 +5,63 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS -// [ HDDダンプユーティリティ(イニシーエタモード) ] +// [ HDD dump utility (initiator mode) ] // //--------------------------------------------------------------------------- +#include #include "os.h" -#include "xm6.h" #include "fileio.h" #include "filepath.h" #include "gpiobus.h" +#include "rascsi.h" #include "rascsi_version.h" //--------------------------------------------------------------------------- // -// 定数宣言 +// Constant Declaration // //--------------------------------------------------------------------------- -#define BUFSIZE 1024 * 64 // 64KBぐらいかなぁ +#define BUFSIZE 1024 * 64 // Buffer size of about 64KB //--------------------------------------------------------------------------- // -// 変数宣言 +// Variable Declaration // //--------------------------------------------------------------------------- -GPIOBUS bus; // バス -int targetid; // ターゲットデバイスID -int boardid; // ボードID(自身のID) -Filepath hdsfile; // HDSファイル -BOOL restore; // リストアフラグ -BYTE buffer[BUFSIZE]; // ワークバッファ -int result; // 結果コード +GPIOBUS bus; // Bus +int targetid; // Target ID +int boardid; // Board ID (own ID) +Filepath hdsfile; // HDS file +bool restore; // Restore flag +BYTE buffer[BUFSIZE]; // Work Buffer +int result; // Result Code //--------------------------------------------------------------------------- // -// 関数宣言 +// Cleanup() Function declaration // //--------------------------------------------------------------------------- void Cleanup(); //--------------------------------------------------------------------------- // -// シグナル処理 +// Signal processing // //--------------------------------------------------------------------------- void KillHandler(int sig) { - // 停止指示 + // Stop running Cleanup(); exit(0); } //--------------------------------------------------------------------------- // -// バナー出力 +// Banner Output // //--------------------------------------------------------------------------- -BOOL Banner(int argc, char* argv[]) +bool Banner(int argc, char* argv[]) { printf("RaSCSI hard disk dump utility "); printf("version %s (%s, %s)\n", @@ -74,79 +75,79 @@ BOOL Banner(int argc, char* argv[]) printf(" BID is rascsi board SCSI ID {0|1|2|3|4|5|6|7}. Default is 7.\n"); printf(" FILE is HDS file path.\n"); printf(" -r is restore operation.\n"); - return FALSE; + return false; } - return TRUE; + return true; } //--------------------------------------------------------------------------- // -// 初期化 +// Initialization // //--------------------------------------------------------------------------- -BOOL Init() +bool Init() { - // 割り込みハンドラ設定 + // Interrupt handler setting if (signal(SIGINT, KillHandler) == SIG_ERR) { - return FALSE; + return false; } if (signal(SIGHUP, KillHandler) == SIG_ERR) { - return FALSE; + return false; } if (signal(SIGTERM, KillHandler) == SIG_ERR) { - return FALSE; + return false; } - // GPIO初期化 + // GPIO Initialization if (!bus.Init(BUS::INITIATOR)) { - return FALSE; + return false; } - // ワーク初期化 + // Work Intitialization targetid = -1; boardid = 7; - restore = FALSE; + restore = false; - return TRUE; + return true; } //--------------------------------------------------------------------------- // -// クリーンアップ +// Cleanup // //--------------------------------------------------------------------------- void Cleanup() { - // バスをクリーンアップ + // Cleanup the bus bus.Cleanup(); } //--------------------------------------------------------------------------- // -// リセット +// Reset // //--------------------------------------------------------------------------- void Reset() { - // バス信号線をリセット + // Reset the bus signal line bus.Reset(); } //--------------------------------------------------------------------------- // -// 引数処理 +// Argument processing // //--------------------------------------------------------------------------- -BOOL ParseArgument(int argc, char* argv[]) +bool ParseArgument(int argc, char* argv[]) { int opt; char *file; - // 初期化 + // Initialization file = NULL; - // 引数解析 + // Argument Parsing opterr = 0; while ((opt = getopt(argc, argv, "i:b:f:r")) != -1) { switch (opt) { @@ -163,94 +164,94 @@ BOOL ParseArgument(int argc, char* argv[]) break; case 'r': - restore = TRUE; + restore = true; break; } } - // TARGET IDチェック + // TARGET ID check if (targetid < 0 || targetid > 7) { fprintf(stderr, "Error : Invalid target id range\n"); - return FALSE; + return false; } - // BOARD IDチェック + // BOARD ID check if (boardid < 0 || boardid > 7) { fprintf(stderr, "Error : Invalid board id range\n"); - return FALSE; + return false; } - // TARGETとBOARDのID重複チェック + // Target and Board ID duplication check if (targetid == boardid) { fprintf(stderr, "Error : Invalid target or board id\n"); - return FALSE; + return false; } - // ファイルチェック + // File Check if (!file) { fprintf(stderr, "Error : Invalid file path\n"); - return FALSE; + return false; } hdsfile.SetPath(file); - return TRUE; + return true; } //--------------------------------------------------------------------------- // -// フェーズ待ち +// Wait Phase // //--------------------------------------------------------------------------- -BOOL WaitPhase(BUS::phase_t phase) +bool WaitPhase(BUS::phase_t phase) { DWORD now; - // タイムアウト(3000ms) + // Timeout (3000ms) now = SysTimer::GetTimerLow(); while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) { bus.Aquire(); if (bus.GetREQ() && bus.GetPhase() == phase) { - return TRUE; + return true; } } - return FALSE; + return false; } //--------------------------------------------------------------------------- // -// バスフリーフェーズ実行 +// Bus Free Phase // //--------------------------------------------------------------------------- void BusFree() { - // バスリセット + // Bus Reset bus.Reset(); } //--------------------------------------------------------------------------- // -// セレクションフェーズ実行 +// Selection Phase // //--------------------------------------------------------------------------- -BOOL Selection(int id) +bool Selection(int id) { BYTE data; int count; - // ID設定とSELアサート + // ID setting and SEL assert data = 0; data |= (1 << boardid); data |= (1 << id); bus.SetDAT(data); bus.SetSEL(TRUE); - // BSYを待つ + // wait for busy count = 10000; do { usleep(20); @@ -260,127 +261,128 @@ BOOL Selection(int id) } } while (count--); - // SELネゲート + // SEL negate bus.SetSEL(FALSE); - // ターゲットがビジー状態なら成功 + // Success if the target is busy return bus.GetBSY(); } //--------------------------------------------------------------------------- // -// コマンドフェーズ実行 +// Command Phase // //--------------------------------------------------------------------------- -BOOL Command(BYTE *buf, int length) +bool Command(BYTE *buf, int length) { int count; - // フェーズ待ち + // Waiting for Phase if (!WaitPhase(BUS::command)) { - return FALSE; + return false; } - // コマンド送信 - count = bus.SendHandShake(buf, length); + // Send Command + count = bus.SendHandShake(buf, length, BUS::SEND_NO_DELAY); - // 送信結果が依頼数と同じなら成功 + // Success if the transmission result is the same as the number + // of requests if (count == length) { - return TRUE; + return true; } - // 送信エラー - return FALSE; + // Return error + return false; } //--------------------------------------------------------------------------- // -// データインフェーズ実行 +// Data in phase // //--------------------------------------------------------------------------- int DataIn(BYTE *buf, int length) { - // フェーズ待ち + // Wait for phase if (!WaitPhase(BUS::datain)) { return -1; } - // データ受信 + // Data reception return bus.ReceiveHandShake(buf, length); } //--------------------------------------------------------------------------- // -// データアウトフェーズ実行 +// Data out phase // //--------------------------------------------------------------------------- int DataOut(BYTE *buf, int length) { - // フェーズ待ち + // Wait for phase if (!WaitPhase(BUS::dataout)) { return -1; } - // データ受信 - return bus.SendHandShake(buf, length); + // Data transmission + return bus.SendHandShake(buf, length, BUS::SEND_NO_DELAY); } //--------------------------------------------------------------------------- // -// ステータスフェーズ実行 +// Status Phase // //--------------------------------------------------------------------------- int Status() { BYTE buf[256]; - // フェーズ待ち + // Wait for phase if (!WaitPhase(BUS::status)) { return -2; } - // データ受信 + // Data reception if (bus.ReceiveHandShake(buf, 1) == 1) { return (int)buf[0]; } - // 受信エラー + // Return error return -1; } //--------------------------------------------------------------------------- // -// メッセージインフェーズ実行 +// Message in phase // //--------------------------------------------------------------------------- int MessageIn() { BYTE buf[256]; - // フェーズ待ち + // Wait for phase if (!WaitPhase(BUS::msgin)) { return -2; } - // データ受信 + // Data reception if (bus.ReceiveHandShake(buf, 1) == 1) { return (int)buf[0]; } - // 受信エラー + // Return error return -1; } //--------------------------------------------------------------------------- // -// TEST UNIT READY実行 +// TEST UNIT READY // //--------------------------------------------------------------------------- int TestUnitReady(int id) { BYTE cmd[256]; - // 結果コード初期化 + // Result code initialization result = 0; // SELECTION @@ -410,7 +412,7 @@ int TestUnitReady(int id) } exit: - // バスフリー + // Bus free BusFree(); return result; @@ -418,7 +420,7 @@ exit: //--------------------------------------------------------------------------- // -// REQUEST SENSE実行 +// REQUEST SENSE // //--------------------------------------------------------------------------- int RequestSense(int id, BYTE *buf) @@ -426,7 +428,7 @@ int RequestSense(int id, BYTE *buf) BYTE cmd[256]; int count; - // 結果コード初期化 + // Result code initialization result = 0; count = 0; @@ -466,10 +468,10 @@ int RequestSense(int id, BYTE *buf) } exit: - // バスフリー + // Bus Free BusFree(); - // 成功であれば転送数を返す + // Returns the number of transfers if successful if (result == 0) { return count; } @@ -479,7 +481,7 @@ exit: //--------------------------------------------------------------------------- // -// MODE SENSE実行 +// MODE SENSE // //--------------------------------------------------------------------------- int ModeSense(int id, BYTE *buf) @@ -487,7 +489,7 @@ int ModeSense(int id, BYTE *buf) BYTE cmd[256]; int count; - // 結果コード初期化 + // Result code initialization result = 0; count = 0; @@ -528,10 +530,10 @@ int ModeSense(int id, BYTE *buf) } exit: - // バスフリー + // Bus free BusFree(); - // 成功であれば転送数を返す + // Returns the number of transfers if successful if (result == 0) { return count; } @@ -541,7 +543,7 @@ exit: //--------------------------------------------------------------------------- // -// INQUIRY実行 +// INQUIRY // //--------------------------------------------------------------------------- int Inquiry(int id, BYTE *buf) @@ -549,7 +551,7 @@ int Inquiry(int id, BYTE *buf) BYTE cmd[256]; int count; - // 結果コード初期化 + // Result code initialization result = 0; count = 0; @@ -589,10 +591,10 @@ int Inquiry(int id, BYTE *buf) } exit: - // バスフリー + // Bus free BusFree(); - // 成功であれば転送数を返す + // Returns the number of transfers if successful if (result == 0) { return count; } @@ -602,7 +604,7 @@ exit: //--------------------------------------------------------------------------- // -// READ CAPACITY実行 +// READ CAPACITY // //--------------------------------------------------------------------------- int ReadCapacity(int id, BYTE *buf) @@ -610,7 +612,7 @@ int ReadCapacity(int id, BYTE *buf) BYTE cmd[256]; int count; - // 結果コード初期化 + // Result code initialization result = 0; count = 0; @@ -649,10 +651,10 @@ int ReadCapacity(int id, BYTE *buf) } exit: - // バスフリー + // Bus free BusFree(); - // 成功であれば転送数を返す + // Returns the number of transfers if successful if (result == 0) { return count; } @@ -662,7 +664,7 @@ exit: //--------------------------------------------------------------------------- // -// READ10実行 +// READ10 // //--------------------------------------------------------------------------- int Read10(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) @@ -670,7 +672,7 @@ int Read10(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) BYTE cmd[256]; int count; - // 結果コード初期化 + // Result code initialization result = 0; count = 0; @@ -714,10 +716,10 @@ int Read10(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) } exit: - // バスフリー + // Bus free BusFree(); - // 成功であれば転送数を返す + // Returns the number of transfers if successful if (result == 0) { return count; } @@ -727,7 +729,7 @@ exit: //--------------------------------------------------------------------------- // -// WRITE10実行 +// WRITE10 // //--------------------------------------------------------------------------- int Write10(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) @@ -735,7 +737,7 @@ int Write10(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) BYTE cmd[256]; int count; - // 結果コード初期化 + // Result code initialization result = 0; count = 0; @@ -779,10 +781,10 @@ int Write10(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) } exit: - // バスフリー + // Bus free BusFree(); - // 成功であれば転送数を返す + // Returns the number of transfers if successful if (result == 0) { return count; } @@ -792,7 +794,7 @@ exit: //--------------------------------------------------------------------------- // -// 主処理 +// Main process // //--------------------------------------------------------------------------- int main(int argc, char* argv[]) @@ -807,34 +809,34 @@ int main(int argc, char* argv[]) DWORD dnum; Fileio fio; Fileio::OpenMode omode; - off64_t size; + off_t size; - // バナー出力 + // Banner output if (!Banner(argc, argv)) { exit(0); } - // 初期化 + // Initialization if (!Init()) { - fprintf(stderr, "Error : Initializing\n"); + fprintf(stderr, "Error : Initializing. Are you root?\n"); - // 恐らくrootでは無い? + // Probably not root exit(EPERM); } - // 構築 + // Prase Argument if (!ParseArgument(argc, argv)) { - // クリーンアップ + // Cleanup Cleanup(); - // 引数エラーで終了 + // Exit with invalid argument error exit(EINVAL); } - // リセット + // Reset the SCSI bus Reset(); - // ファイルオープン + // File Open if (restore) { omode = Fileio::ReadOnly; } else { @@ -843,22 +845,22 @@ int main(int argc, char* argv[]) if (!fio.Open(hdsfile.GetPath(), omode)) { fprintf(stderr, "Error : Can't open hds file\n"); - // クリーンアップ + // Cleanup Cleanup(); exit(EPERM); } - // バスフリー + // Bus free BusFree(); - // RESETシグナル発行 + // Assert reset signal bus.SetRST(TRUE); usleep(1000); bus.SetRST(FALSE); - // ダンプ開始 + // Start dump printf("TARGET ID : %d\n", targetid); - printf("BORAD ID : %d\n", boardid); + printf("BOARD ID : %d\n", boardid); // TEST UNIT READY count = TestUnitReady(targetid); @@ -881,7 +883,7 @@ int main(int argc, char* argv[]) goto cleanup_exit; } - // INQUIRYの情報を表示 + // Display INQUIRY information memset(str, 0x00, sizeof(str)); memcpy(str, &buffer[8], 8); printf("Vendor : %s\n", str); @@ -892,14 +894,14 @@ int main(int argc, char* argv[]) memcpy(str, &buffer[32], 4); printf("Revison : %s\n", str); - // 容量取得 + // Get drive capacity count = ReadCapacity(targetid, buffer); if (count < 0) { fprintf(stderr, "READ CAPACITY ERROR %d\n", count); goto cleanup_exit; } - // ブロックサイズとブロック数の表示 + // Display block size and number of blocks bsiz = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; @@ -913,20 +915,20 @@ int main(int argc, char* argv[]) (int)(bsiz * bnum / 1024 / 1024), (int)(bsiz * bnum)); - // リストアファイルサイズの取得 + // Get the restore file size if (restore) { size = fio.GetFileSize(); printf("Restore file size : %d bytes", (int)size); - if (size > (off64_t)(bsiz * bnum)) { + if (size > (off_t)(bsiz * bnum)) { printf("(WARNING : File size is larger than disk size)"); - } else if (size < (off64_t)(bsiz * bnum)) { + } else if (size < (off_t)(bsiz * bnum)) { printf("(ERROR : File size is smaller than disk size)\n"); goto cleanup_exit; } printf("\n"); } - // バッファサイズ毎にダンプする + // Dump by buffer size duni = BUFSIZE; duni /= bsiz; dsiz = BUFSIZE; @@ -974,7 +976,7 @@ int main(int argc, char* argv[]) printf("\033[0K"); } - // 容量上の端数処理 + // Rounding on capacity dnum = bnum % duni; dsiz = dnum * bsiz; if (dnum > 0) { @@ -989,16 +991,16 @@ int main(int argc, char* argv[]) } } - // 完了メッセージ + // Completion Message printf("%3d%%(%7d/%7d)\n", 100, (int)bnum, (int)bnum); cleanup_exit: - // ファイルクローズ + // File close fio.Close(); - // クリーンアップ + // Cleanup Cleanup(); - // 終了 + // end exit(0); } diff --git a/src/raspberrypi/rasutil.cpp b/src/raspberrypi/rasutil.cpp new file mode 100644 index 00000000..4b37e6cc --- /dev/null +++ b/src/raspberrypi/rasutil.cpp @@ -0,0 +1,76 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include +#include +#include "rascsi_interface.pb.h" +#include "rasutil.h" + +using namespace std; +using namespace rascsi_interface; + +bool ras_util::GetAsInt(const string& value, int& result) +{ + if (value.find_first_not_of("0123456789") != string::npos) { + return false; + } + + try { + result = std::stoul(value); + } + catch(const invalid_argument& e) { + return false; + } + catch(const out_of_range& e) { + return false; + } + + return true; +} + +string ras_util::ListDevices(const list& pb_devices) +{ + if (pb_devices.empty()) { + return "No images currently attached."; + } + + ostringstream s; + s << "+----+-----+------+-------------------------------------" << endl + << "| ID | LUN | TYPE | IMAGE FILE" << endl + << "+----+-----+------+-------------------------------------" << endl; + + list devices = pb_devices; + devices.sort([](const auto& a, const auto& b) { return a.id() < b.id() && a.unit() < b.unit(); }); + + for (const auto& device : devices) { + string filename; + switch (device.type()) { + case SCBR: + filename = "X68000 HOST BRIDGE"; + break; + + case SCDP: + filename = "DaynaPort SCSI/Link"; + break; + + default: + filename = device.file().name(); + break; + } + + s << "| " << device.id() << " | " << device.unit() << " | " << PbDeviceType_Name(device.type()) << " | " + << (filename.empty() ? "NO MEDIA" : filename) + << (!device.status().removed() && (device.properties().read_only() || device.status().protected_()) ? " (READ-ONLY)" : "") + << endl; + } + + s << "+----+-----+------+-------------------------------------"; + + return s.str(); +} diff --git a/src/raspberrypi/rasutil.h b/src/raspberrypi/rasutil.h new file mode 100644 index 00000000..dd5d52a1 --- /dev/null +++ b/src/raspberrypi/rasutil.h @@ -0,0 +1,22 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +// Helper methods used by rascsi and rasctl +// +//--------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include "rascsi_interface.pb.h" + +namespace ras_util +{ + bool GetAsInt(const std::string&, int&); + std::string ListDevices(const std::list&); +} diff --git a/src/raspberrypi/sasidump.cpp b/src/raspberrypi/sasidump.cpp index 3e9dc960..847945a4 100644 --- a/src/raspberrypi/sasidump.cpp +++ b/src/raspberrypi/sasidump.cpp @@ -5,7 +5,7 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS -// [ HDDダンプユーティリティ(イニシーエタモード/SASI Version) ] +// [ HDD dump utility (Initiator mode/SASI Version) ] // // SASI IMAGE EXAMPLE // X68000 @@ -18,62 +18,48 @@ // //--------------------------------------------------------------------------- +#include #include "os.h" -#include "xm6.h" #include "fileio.h" #include "filepath.h" #include "gpiobus.h" +#include "rascsi.h" #include "rascsi_version.h" -//--------------------------------------------------------------------------- -// -// 定数宣言 -// -//--------------------------------------------------------------------------- -#define BUFSIZE 1024 * 64 // 64KBぐらいかなぁ +#define BUFSIZE 1024 * 64 // Maybe around 64KB? -//--------------------------------------------------------------------------- -// -// 変数宣言 -// -//--------------------------------------------------------------------------- -GPIOBUS bus; // バス -int targetid; // ターゲットデバイスID -int unitid; // ターゲットユニットID -int bsiz; // ブロックサイズ -int bnum; // ブロック数 -Filepath hdffile; // HDFファイル -BOOL restore; // リストアフラグ -BYTE buffer[BUFSIZE]; // ワークバッファ -int result; // 結果コード +GPIOBUS bus; +int targetid; +int unitid; +int bsiz; // Block size +int bnum; // Number of blocks +Filepath hdffile; // HDF file +bool restore; // Restore flag +BYTE buffer[BUFSIZE]; // Work buffer +int result; // Result code -//--------------------------------------------------------------------------- -// -// 関数宣言 -// -//--------------------------------------------------------------------------- void Cleanup(); //--------------------------------------------------------------------------- // -// シグナル処理 +// Signal processing // //--------------------------------------------------------------------------- void KillHandler(int sig) { - // 停止指示 + // Stop instruction Cleanup(); exit(0); } //--------------------------------------------------------------------------- // -// バナー出力 +// Banner output // //--------------------------------------------------------------------------- -BOOL Banner(int argc, char* argv[]) +bool Banner(int argc, char* argv[]) { - printf("RaSCSI hard disk dump utility(SASI HDD) "); + printf("RaSCSI hard disk dump utility (SASI HDD) "); printf("version %s (%s, %s)\n", rascsi_get_version_string(), __DATE__, @@ -87,81 +73,60 @@ BOOL Banner(int argc, char* argv[]) printf(" COUNT is block count.\n"); printf(" FILE is HDF file path.\n"); printf(" -r is restore operation.\n"); - return FALSE; + return false; } - return TRUE; + return true; } -//--------------------------------------------------------------------------- -// -// 初期化 -// -//--------------------------------------------------------------------------- -BOOL Init() +bool Init() { - // 割り込みハンドラ設定 + // Interrupt handler settings if (signal(SIGINT, KillHandler) == SIG_ERR) { - return FALSE; + return false; } if (signal(SIGHUP, KillHandler) == SIG_ERR) { - return FALSE; + return false; } if (signal(SIGTERM, KillHandler) == SIG_ERR) { - return FALSE; + return false; } - // GPIO初期化 + // GPIO initialization if (!bus.Init(BUS::INITIATOR)) { - return FALSE; + return false; } - // ワーク初期化 + // Work initialization targetid = -1; unitid = 0; bsiz = 256; bnum = -1; - restore = FALSE; + restore = false; - return TRUE; + return true; } -//--------------------------------------------------------------------------- -// -// クリーンアップ -// -//--------------------------------------------------------------------------- void Cleanup() { - // バスをクリーンアップ bus.Cleanup(); } -//--------------------------------------------------------------------------- -// -// リセット -// -//--------------------------------------------------------------------------- void Reset() { - // バス信号線をリセット + // Reset bus signal line bus.Reset(); } -//--------------------------------------------------------------------------- -// -// 引数処理 -// -//--------------------------------------------------------------------------- -BOOL ParseArgument(int argc, char* argv[]) +bool ParseArgument(int argc, char* argv[]) { int opt; char *file; - // 初期化 + // Initialization file = NULL; - // 引数解析 + // Argument parsing opterr = 0; while ((opt = getopt(argc, argv, "i:u:b:c:f:r")) != -1) { switch (opt) { @@ -186,100 +151,94 @@ BOOL ParseArgument(int argc, char* argv[]) break; case 'r': - restore = TRUE; + restore = true; break; } } - // TARGET IDチェック + // TARGET ID check if (targetid < 0 || targetid > 7) { fprintf(stderr, "Error : Invalid target id range\n"); - return FALSE; + return false; } - // UNIT IDチェック + // UNIT ID check if (unitid < 0 || unitid > 1) { fprintf(stderr, "Error : Invalid unit id range\n"); - return FALSE; + return false; } - // BSIZチェック + // BSIZ check if (bsiz != 256 && bsiz != 512 && bsiz != 1024) { fprintf(stderr, "Error : Invalid block size\n"); - return FALSE; + return false; } - // BNUMチェック + // BNUM check if (bnum < 0) { fprintf(stderr, "Error : Invalid block count\n"); - return FALSE; + return false; } - // ファイルチェック + // File check if (!file) { fprintf(stderr, "Error : Invalid file path\n"); - return FALSE; + return false; } hdffile.SetPath(file); - return TRUE; + return true; } -//--------------------------------------------------------------------------- -// -// フェーズ待ち -// -//--------------------------------------------------------------------------- -BOOL WaitPhase(BUS::phase_t phase) +bool WaitPhase(BUS::phase_t phase) { DWORD now; - // タイムアウト(3000ms) + // Timeout (3000ms) now = SysTimer::GetTimerLow(); while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) { bus.Aquire(); if (bus.GetREQ() && bus.GetPhase() == phase) { - return TRUE; + return true; } } - return FALSE; + return false; } //--------------------------------------------------------------------------- // -// バスフリーフェーズ実行 +// Bus free phase execution // //--------------------------------------------------------------------------- void BusFree() { - // バスリセット bus.Reset(); } //--------------------------------------------------------------------------- // -// セレクションフェーズ実行 +// Selection phase execution // //--------------------------------------------------------------------------- -BOOL Selection(int id) +bool Selection(int id) { BYTE data; int count; - // ID設定とSELアサート + // ID setting and SEL assertion data = 0; data |= (1 << id); bus.SetDAT(data); bus.SetSEL(TRUE); - // BSYを待つ + // Wait for BSY count = 10000; do { usleep(20); @@ -289,136 +248,134 @@ BOOL Selection(int id) } } while (count--); - // SELネゲート + // SEL negate bus.SetSEL(FALSE); - // ターゲットがビジー状態なら成功 + // Return true if target is busy return bus.GetBSY(); } //--------------------------------------------------------------------------- // -// コマンドフェーズ実行 +// Command phase execution // //--------------------------------------------------------------------------- -BOOL Command(BYTE *buf, int length) +bool Command(BYTE *buf, int length) { int count; - // フェーズ待ち + // Wait for phase if (!WaitPhase(BUS::command)) { - return FALSE; + return false; } - // コマンド送信 - count = bus.SendHandShake(buf, length); + // Send command + count = bus.SendHandShake(buf, length, BUS::SEND_NO_DELAY); - // 送信結果が依頼数と同じなら成功 + // Return true is send results match number of requests if (count == length) { - return TRUE; + return true; } - // 送信エラー - return FALSE; + // Send error + return false; } //--------------------------------------------------------------------------- // -// データインフェーズ実行 +// Data in phase execution // //--------------------------------------------------------------------------- int DataIn(BYTE *buf, int length) { - // フェーズ待ち + // Wait for phase if (!WaitPhase(BUS::datain)) { return -1; } - // データ受信 + // Receive data return bus.ReceiveHandShake(buf, length); } //--------------------------------------------------------------------------- // -// データアウトフェーズ実行 +// Data out phase execution // //--------------------------------------------------------------------------- int DataOut(BYTE *buf, int length) { - // フェーズ待ち + // Wait for phase if (!WaitPhase(BUS::dataout)) { return -1; } - // データ受信 - return bus.SendHandShake(buf, length); + // Receive data + return bus.SendHandShake(buf, length, BUS::SEND_NO_DELAY); } //--------------------------------------------------------------------------- // -// ステータスフェーズ実行 +// Status phase execution // //--------------------------------------------------------------------------- int Status() { BYTE buf[256]; - // フェーズ待ち + // Wait for phase if (!WaitPhase(BUS::status)) { return -2; } - // データ受信 + // Receive data if (bus.ReceiveHandShake(buf, 1) == 1) { return (int)buf[0]; } - // 受信エラー + // Receive error return -1; } //--------------------------------------------------------------------------- // -// メッセージインフェーズ実行 +// Message in phase execution // //--------------------------------------------------------------------------- int MessageIn() { BYTE buf[256]; - // フェーズ待ち + // Wait for phase if (!WaitPhase(BUS::msgin)) { return -2; } - // データ受信 + // Receive data if (bus.ReceiveHandShake(buf, 1) == 1) { return (int)buf[0]; } - // 受信エラー + // Receive error return -1; } //--------------------------------------------------------------------------- // -// TEST UNIT READY実行 +// TEST UNIT READY execution // //--------------------------------------------------------------------------- int TestUnitReady(int id) { BYTE cmd[256]; - // 結果コード初期化 + // Initialize result code result = 0; - // SELECTION if (!Selection(id)) { result = -1; goto exit; } - // COMMAND memset(cmd, 0x00, 6); cmd[0] = 0x00; cmd[1] = unitid << 5; @@ -427,20 +384,17 @@ int TestUnitReady(int id) goto exit; } - // STATUS if (Status() < 0) { result = -4; goto exit; } - // MESSAGE IN if (MessageIn() < 0) { result = -5; goto exit; } exit: - // バスフリー BusFree(); return result; @@ -448,7 +402,7 @@ exit: //--------------------------------------------------------------------------- // -// REQUEST SENSE実行 +// REQUEST SENSE execution // //--------------------------------------------------------------------------- int RequestSense(int id, BYTE *buf) @@ -456,17 +410,15 @@ int RequestSense(int id, BYTE *buf) BYTE cmd[256]; int count; - // 結果コード初期化 + // Initialize result codes result = 0; count = 0; - // SELECTION if (!Selection(id)) { result = -1; goto exit; } - // COMMAND memset(cmd, 0x00, 6); cmd[0] = 0x03; cmd[1] = unitid << 5; @@ -476,7 +428,6 @@ int RequestSense(int id, BYTE *buf) goto exit; } - // DATAIN memset(buf, 0x00, 256); count = DataIn(buf, 256); if (count <= 0) { @@ -484,23 +435,20 @@ int RequestSense(int id, BYTE *buf) goto exit; } - // STATUS if (Status() < 0) { result = -4; goto exit; } - // MESSAGE IN if (MessageIn() < 0) { result = -5; goto exit; } exit: - // バスフリー BusFree(); - // 成功であれば転送数を返す + // If successful, return number of transfers if (result == 0) { return count; } @@ -510,7 +458,7 @@ exit: //--------------------------------------------------------------------------- // -// READ6実行 +// READ6 execution // //--------------------------------------------------------------------------- int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) @@ -518,17 +466,15 @@ int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) BYTE cmd[256]; int count; - // 結果コード初期化 + // Initialize result codes result = 0; count = 0; - // SELECTION if (!Selection(id)) { result = -1; goto exit; } - // COMMAND memset(cmd, 0x00, 10); cmd[0] = 0x8; cmd[1] = (BYTE)(bstart >> 16); @@ -542,30 +488,26 @@ int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) goto exit; } - // DATAIN count = DataIn(buf, length); if (count <= 0) { result = -3; goto exit; } - // STATUS if (Status() < 0) { result = -4; goto exit; } - // MESSAGE IN if (MessageIn() < 0) { result = -5; goto exit; } exit: - // バスフリー BusFree(); - // 成功であれば転送数を返す + // If successful, return number of transfers if (result == 0) { return count; } @@ -575,7 +517,7 @@ exit: //--------------------------------------------------------------------------- // -// WRITE6実行 +// WRITE6 execution // //--------------------------------------------------------------------------- int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) @@ -583,17 +525,15 @@ int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) BYTE cmd[256]; int count; - // 結果コード初期化 + // Initialize result codes result = 0; count = 0; - // SELECTION if (!Selection(id)) { result = -1; goto exit; } - // COMMAND memset(cmd, 0x00, 10); cmd[0] = 0xa; cmd[1] = (BYTE)(bstart >> 16); @@ -607,30 +547,26 @@ int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) goto exit; } - // DATAOUT count = DataOut(buf, length); if (count <= 0) { result = -3; goto exit; } - // STATUS if (Status() < 0) { result = -4; goto exit; } - // MESSAGE IN if (MessageIn() < 0) { result = -5; goto exit; } exit: - // バスフリー BusFree(); - // 成功であれば転送数を返す + // If successful, return number of transfers if (result == 0) { return count; } @@ -640,7 +576,7 @@ exit: //--------------------------------------------------------------------------- // -// 主処理 +// Main processing // //--------------------------------------------------------------------------- int main(int argc, char* argv[]) @@ -652,34 +588,30 @@ int main(int argc, char* argv[]) DWORD dnum; Fileio fio; Fileio::OpenMode omode; - off64_t size; + off_t size; - // バナー出力 + // Output banner if (!Banner(argc, argv)) { exit(0); } - // 初期化 if (!Init()) { fprintf(stderr, "Error : Initializing\n"); - // 恐らくrootでは無い? + // Probably not executing as root? exit(EPERM); } - // 構築 if (!ParseArgument(argc, argv)) { - // クリーンアップ Cleanup(); - // 引数エラーで終了 + // Exit with argument error exit(EINVAL); } - // リセット Reset(); - // ファイルオープン + // Open file if (restore) { omode = Fileio::ReadOnly; } else { @@ -688,20 +620,18 @@ int main(int argc, char* argv[]) if (!fio.Open(hdffile.GetPath(), omode)) { fprintf(stderr, "Error : Can't open hdf file\n"); - // クリーンアップ Cleanup(); exit(EPERM); } - // バスフリー BusFree(); - // RESETシグナル発行 + // Execute RESET signal bus.SetRST(TRUE); usleep(1000); bus.SetRST(FALSE); - // ダンプ開始 + // Start dump printf("TARGET ID : %d\n", targetid); printf("UNIT ID : %d\n", unitid); @@ -712,36 +642,35 @@ int main(int argc, char* argv[]) goto cleanup_exit; } - // REQUEST SENSE(for CHECK CONDITION) + // REQUEST SENSE (for CHECK CONDITION) count = RequestSense(targetid, buffer); if (count < 0) { fprintf(stderr, "REQUEST SENSE ERROR %d\n", count); goto cleanup_exit; } - // ブロックサイズとブロック数の表示 printf("Number of blocks : %d Blocks\n", bnum); printf("Block length : %d Bytes\n", bsiz); - // データサイズの表示 + // Display data size printf("Total length : %d MBytes %d Bytes\n", (bsiz * bnum / 1024 / 1024), (bsiz * bnum)); - // リストアファイルサイズの取得 + // Get restore file size if (restore) { size = fio.GetFileSize(); printf("Restore file size : %d bytes", (int)size); - if (size > (off64_t)(bsiz * bnum)) { + if (size > (off_t)(bsiz * bnum)) { printf("(WARNING : File size is larger than disk size)"); - } else if (size < (off64_t)(bsiz * bnum)) { + } else if (size < (off_t)(bsiz * bnum)) { printf("(ERROR : File size is smaller than disk size)\n"); goto cleanup_exit; } printf("\n"); } - // バッファサイズ毎にダンプする + // Dump by buffer size duni = BUFSIZE; duni /= bsiz; dsiz = BUFSIZE; @@ -789,7 +718,7 @@ int main(int argc, char* argv[]) printf("\033[0K"); } - // 容量上の端数処理 + // Rounding of capacity dnum = bnum % duni; dsiz = dnum * bsiz; @@ -805,16 +734,13 @@ int main(int argc, char* argv[]) } } - // 完了メッセージ + // Completion message printf("%3d%%(%7d/%7d)\n", 100, bnum, bnum); cleanup_exit: - // ファイルクローズ fio.Close(); - // クリーンアップ Cleanup(); - // 終了 exit(0); } diff --git a/src/raspberrypi/scsi.cpp b/src/raspberrypi/scsi.cpp index 9eb14315..757ee7d3 100644 --- a/src/raspberrypi/scsi.cpp +++ b/src/raspberrypi/scsi.cpp @@ -10,20 +10,16 @@ //--------------------------------------------------------------------------- #include "os.h" -#include "xm6.h" #include "scsi.h" +#include "rascsi.h" //--------------------------------------------------------------------------- // // Phase Acquisition // //--------------------------------------------------------------------------- -BUS::phase_t FASTCALL BUS::GetPhase() +BUS::phase_t BUS::GetPhase() { - DWORD mci; - - ASSERT(this); - // Selection Phase if (GetSEL()) { return selection; @@ -35,7 +31,7 @@ BUS::phase_t FASTCALL BUS::GetPhase() } // Get target phase from bus signal line - mci = GetMSG() ? 0x04 : 0x00; + DWORD mci = GetMSG() ? 0x04 : 0x00; mci |= GetCD() ? 0x02 : 0x00; mci |= GetIO() ? 0x01 : 0x00; return GetPhase(mci); @@ -46,7 +42,7 @@ BUS::phase_t FASTCALL BUS::GetPhase() // Determine Phase String phase enum // //--------------------------------------------------------------------------- -const char* FASTCALL BUS::GetPhaseStrRaw(phase_t current_phase){ +const char* BUS::GetPhaseStrRaw(phase_t current_phase){ if(current_phase <= phase_t::reserved){ return phase_str_table[current_phase]; } diff --git a/src/raspberrypi/scsi.h b/src/raspberrypi/scsi.h index a1ee4259..7a6558bd 100644 --- a/src/raspberrypi/scsi.h +++ b/src/raspberrypi/scsi.h @@ -9,8 +9,45 @@ // //--------------------------------------------------------------------------- -#if !defined(scsi_h) -#define scsi_h +#pragma once + +//=========================================================================== +// +// Sense Keys and Additional Sense Codes +// (See https://www.t10.org/lists/1spc-lst.htm) +// +//=========================================================================== +class ERROR_CODES +{ +public: + enum sense_key : int { + NO_SENSE = 0x00, + RECOVERED_ERROR = 0x01, + NOT_READY = 0x02, + MEDIUM_ERROR = 0x03, + HARDWARE_ERROR = 0x04, + ILLEGAL_REQUEST = 0x05, + UNIT_ATTENTION = 0x06, + DATA_PROTECT = 0x07, + BLANK_CHECK = 0x08, + VENDOR_SPECIFIC = 0x09, + COPY_ABORTED = 0x0a, + ABORTED_COMMAND = 0x0b, + VOLUME_OVERFLOW = 0x0d, + MISCOMPARE = 0x0e, + COMPLETED = 0x0f + }; + + enum asc : int { + NO_ADDITIONAL_SENSE_INFORMATION = 0x00, + INVALID_COMMAND_OPERATION_CODE = 0x20, + LBA_OUT_OF_RANGE = 0x21, + INVALID_FIELD_IN_CDB = 0x24, + INVALID_LUN = 0x25, + WRITE_PROTECTED = 0x27, + MEDIUM_NOT_PRESENT = 0x3a + }; +}; //=========================================================================== // @@ -27,40 +64,37 @@ public: MONITOR = 2, }; - // Phase definition - enum phase_t { - busfree, // バスフリーフェーズ - arbitration, // アービトレーションフェーズ - selection, // セレクションフェーズ - reselection, // リセレクションフェーズ - command, // コマンドフェーズ - execute, // 実行フェーズ - datain, // データイン - dataout, // データアウト - status, // ステータスフェーズ - msgin, // メッセージフェーズ - msgout, // メッセージアウトフェーズ - reserved // 未使用/リザーブ + // Phase definitions + enum phase_t : BYTE { + busfree, + arbitration, + selection, + reselection, + command, + execute, // Execute phase is an extension of the command phase + datain, + dataout, + status, + msgin, + msgout, + reserved // Unused }; - // Basic Functions - // 基本ファンクション - virtual BOOL FASTCALL Init(mode_e mode) = 0; - // 初期化 - virtual void FASTCALL Reset() = 0; - // リセット - virtual void FASTCALL Cleanup() = 0; - // クリーンアップ - phase_t FASTCALL GetPhase(); - // フェーズ取得 + BUS() { }; + virtual ~BUS() { }; - static phase_t FASTCALL GetPhase(DWORD mci) + // Basic Functions + virtual BOOL Init(mode_e mode) = 0; + virtual void Reset() = 0; + virtual void Cleanup() = 0; + phase_t GetPhase(); + + static phase_t GetPhase(DWORD mci) { return phase_table[mci]; } - // フェーズ取得 - static const char* FASTCALL GetPhaseStrRaw(phase_t current_phase); + static const char* GetPhaseStrRaw(phase_t current_phase); // Get the string phase name, based upon the raw data // Extract as specific pin field from a raw data capture @@ -69,70 +103,52 @@ public: return ((raw_data >> pin_num) & 1); } - virtual BOOL FASTCALL GetBSY() = 0; - // BSYシグナル取得 - virtual void FASTCALL SetBSY(BOOL ast) = 0; - // BSYシグナル設定 + virtual bool GetBSY() = 0; + virtual void SetBSY(bool ast) = 0; - virtual BOOL FASTCALL GetSEL() = 0; - // SELシグナル取得 - virtual void FASTCALL SetSEL(BOOL ast) = 0; - // SELシグナル設定 + virtual BOOL GetSEL() = 0; + virtual void SetSEL(BOOL ast) = 0; - virtual BOOL FASTCALL GetATN() = 0; - // ATNシグナル取得 - virtual void FASTCALL SetATN(BOOL ast) = 0; - // ATNシグナル設定 + virtual BOOL GetATN() = 0; + virtual void SetATN(BOOL ast) = 0; - virtual BOOL FASTCALL GetACK() = 0; - // ACKシグナル取得 - virtual void FASTCALL SetACK(BOOL ast) = 0; - // ACKシグナル設定 + virtual BOOL GetACK() = 0; + virtual void SetACK(BOOL ast) = 0; - virtual BOOL FASTCALL GetRST() = 0; - // RSTシグナル取得 - virtual void FASTCALL SetRST(BOOL ast) = 0; - // RSTシグナル設定 + virtual BOOL GetRST() = 0; + virtual void SetRST(BOOL ast) = 0; - virtual BOOL FASTCALL GetMSG() = 0; - // MSGシグナル取得 - virtual void FASTCALL SetMSG(BOOL ast) = 0; - // MSGシグナル設定 + virtual BOOL GetMSG() = 0; + virtual void SetMSG(BOOL ast) = 0; - virtual BOOL FASTCALL GetCD() = 0; - // CDシグナル取得 - virtual void FASTCALL SetCD(BOOL ast) = 0; - // CDシグナル設定 + virtual BOOL GetCD() = 0; + virtual void SetCD(BOOL ast) = 0; - virtual BOOL FASTCALL GetIO() = 0; - // IOシグナル取得 - virtual void FASTCALL SetIO(BOOL ast) = 0; - // IOシグナル設定 + virtual BOOL GetIO() = 0; + virtual void SetIO(BOOL ast) = 0; - virtual BOOL FASTCALL GetREQ() = 0; - // REQシグナル取得 - virtual void FASTCALL SetREQ(BOOL ast) = 0; - // REQシグナル設定 + virtual BOOL GetREQ() = 0; + virtual void SetREQ(BOOL ast) = 0; - virtual BYTE FASTCALL GetDAT() = 0; - // データシグナル取得 - virtual void FASTCALL SetDAT(BYTE dat) = 0; - // データシグナル設定 - virtual BOOL FASTCALL GetDP() = 0; - // パリティシグナル取得 + virtual BYTE GetDAT() = 0; + virtual void SetDAT(BYTE dat) = 0; + virtual BOOL GetDP() = 0; // Get parity signal - virtual int FASTCALL CommandHandShake(BYTE *buf) = 0; - // コマンド受信ハンドシェイク - virtual int FASTCALL ReceiveHandShake(BYTE *buf, int count) = 0; - // データ受信ハンドシェイク - virtual int FASTCALL SendHandShake(BYTE *buf, int count) = 0; - // データ送信ハンドシェイク + virtual int CommandHandShake(BYTE *buf) = 0; + virtual int ReceiveHandShake(BYTE *buf, int count) = 0; + virtual int SendHandShake(BYTE *buf, int count, int delay_after_bytes) = 0; + + virtual BOOL GetSignal(int pin) = 0; + // Get SCSI input signal value + virtual void SetSignal(int pin, BOOL ast) = 0; + // Set SCSI output signal value + static const int SEND_NO_DELAY = -1; + // Passed into SendHandShake when we don't want to delay +protected: + phase_t m_current_phase = phase_t::reserved; private: static const phase_t phase_table[8]; - // フェーズテーブル static const char* phase_str_table[]; }; - -#endif // scsi_h diff --git a/src/raspberrypi/scsimon.cpp b/src/raspberrypi/scsimon.cpp index 8a2f9450..06dc79b3 100644 --- a/src/raspberrypi/scsimon.cpp +++ b/src/raspberrypi/scsimon.cpp @@ -10,7 +10,6 @@ //--------------------------------------------------------------------------- #include "os.h" -#include "xm6.h" #include "filepath.h" #include "fileio.h" #include "devices/disk.h" @@ -19,6 +18,9 @@ #include "rascsi_version.h" #include "spdlog/spdlog.h" #include +#include +#include +#include "rascsi.h" //--------------------------------------------------------------------------- // @@ -51,12 +53,11 @@ // //--------------------------------------------------------------------------- static BYTE prev_value[32] = {0xFF}; -static volatile BOOL running; // Running flag -static volatile BOOL active; // Processing flag +static volatile bool running; // Running flag GPIOBUS *bus; // GPIO Bus typedef struct data_capture{ DWORD data; - QWORD timestamp; + uint64_t timestamp; } data_capture_t; data_capture data_buffer[MAX_BUFF_SIZE]; @@ -77,7 +78,7 @@ char log_file_name[_MAX_FNAME/2] = "log.vcd"; void KillHandler(int sig) { // Stop instruction - running = FALSE; + running = false; } //--------------------------------------------------------------------------- @@ -94,7 +95,7 @@ void Banner(int argc, char* argv[]) __TIME__); LOGINFO("Powered by XM6 TypeG Technology "); LOGINFO("Copyright (C) 2016-2020 GIMONS"); - LOGINFO("Copyright (C) 2020 akuker"); + LOGINFO("Copyright (C) 2020-2021 Contributors to the RaSCSI project"); LOGINFO("Connect type : %s", CONNECT_DESC); LOGINFO(" %s - Value Change Dump file that can be opened with GTKWave", log_file_name); @@ -142,8 +143,7 @@ BOOL Init() bus->Reset(); // Other - running = FALSE; - active = FALSE; + running = false; return TRUE; } @@ -265,7 +265,9 @@ void create_value_change_dump() while(i < data_idx) { - fprintf(fp, "#%llu\n",(QWORD)(data_buffer[i].timestamp*ns_per_loop)); + ostringstream s; + s << (uint64_t)(data_buffer[i].timestamp*ns_per_loop); + fprintf(fp, "#%s\n",s.str().c_str()); vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_BSY, SYMBOL_PIN_BSY); vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_SEL, SYMBOL_PIN_SEL); vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_CD, SYMBOL_PIN_CD); @@ -349,7 +351,9 @@ static DWORD low_bits = 0xFFFFFFFF; //--------------------------------------------------------------------------- int main(int argc, char* argv[]) { -#ifdef DEBUG + ostringstream s; + + #ifdef DEBUG DWORD prev_high = high_bits; DWORD prev_low = low_bits; #endif @@ -358,9 +362,9 @@ int main(int argc, char* argv[]) int ret; struct sched_param schparam; timeval start_time, stop_time; - QWORD loop_count = 0; + uint64_t loop_count = 0; timeval time_diff; - QWORD elapsed_us; + uint64_t elapsed_us; int str_len; // If there is an argument specified and it is NOT -h or --help @@ -408,13 +412,14 @@ int main(int argc, char* argv[]) sched_setscheduler(0, SCHED_FIFO, &schparam); // Start execution - running = TRUE; + running = true; bus->SetACT(FALSE); (void)gettimeofday(&start_time, NULL); LOGDEBUG("ALL_SCSI_PINS %08X\n",ALL_SCSI_PINS); - // Main Loop + + // Main Loop while (running) { // Work initialization this_sample = (bus->Aquire() & ALL_SCSI_PINS); @@ -440,12 +445,14 @@ int main(int argc, char* argv[]) low_bits &= this_sample; if ((high_bits != prev_high) || (low_bits != prev_low)) { - LOGDEBUG(" %08lX %08lX\n",high_bits, low_bits); + LOGDEBUG(" %08X %08X\n",high_bits, low_bits); } prev_high = high_bits; prev_low = low_bits; if((data_idx % 1000) == 0){ - LOGDEBUG("Collected %lu samples...", data_idx); + s.str(""); + s << "Collected " << data_idx << " samples..."; + LOGDEBUG("%s", s.str().c_str()); } #endif data_buffer[data_idx].data = this_sample; @@ -470,12 +477,18 @@ int main(int argc, char* argv[]) timersub(&stop_time, &start_time, &time_diff); elapsed_us = ((time_diff.tv_sec*1000000) + time_diff.tv_usec); - LOGINFO("Elapsed time: %llu microseconds (%lf seconds)",elapsed_us, ((double)elapsed_us)/1000000); - LOGINFO("Collected %lu changes", data_idx); + s.str(""); + s << "Elapsed time: " << elapsed_us << " microseconds (" << elapsed_us / 1000000 << " seconds)"; + LOGINFO("%s", s.str().c_str()); + s.str(""); + s << "Collected " << data_idx << " changes"; + LOGINFO("%s", s.str().c_str()); // Note: ns_per_loop is a global variable that is used by Cleanup() to printout the timestamps. ns_per_loop = (elapsed_us * 1000) / (double)loop_count; - LOGINFO("Read the SCSI bus %llu times with an average of %lu ns for each read", loop_count, (DWORD)ns_per_loop); + s.str(""); + s << "Read the SCSI bus " << loop_count << " times with an average of " << ns_per_loop << " ns for each read"; + LOGINFO("%s", s.str().c_str()); // Cleanup Cleanup(); diff --git a/src/web/README.md b/src/web/README.md index 82088ba2..8ad84a76 100644 --- a/src/web/README.md +++ b/src/web/README.md @@ -1,12 +1,37 @@ # RaSCSI Web -## Mocking for local development +## Setup local dev env -Set a few env vars to point to the mock scripts and base dir - -``` -BASE_DIR=/tmp/images/ -PATH=$PATH:`pwd`/mock/bin/ +```bash +# Make a virtual env named venv +$ python3 -m venv venv +# Use that virtual env in this shell +$ source venv/bin/activate +# Install requirements +$ pip install -r requirements.txt +# Use mocks and a temp dir - start the web server +$ BASE_DIR=/tmp/images/ PATH=$PATH:`pwd`/mock/bin/ python3 web.py ``` -Edit response to commands in `mock/bin/*` +### Mocks for local development + +You may edit the files under `mock/bin` to simulate Linux command responses. +TODO: rascsi-web uses protobuf commands to send and receive data from rascsi. +A separate mocking solusion will be needed for this interface. + +## Pushing to the Pi via git + +Setup a bare repo on the rascsi +``` +$ ssh pi@rascsi +$ mkdir /home/pi/dev.git && cd /home/pi/dev.git +$ git --bare init +Initialized empty Git repository in /home/pi/dev.git +``` + +Locally +``` +$ cd ~/source/RASCSI +$ git remote add pi ssh://pi@rascsi/home/pi/dev.git +$ git push pi master +``` diff --git a/src/web/drive_properties.json b/src/web/drive_properties.json new file mode 100644 index 00000000..4505a4ba --- /dev/null +++ b/src/web/drive_properties.json @@ -0,0 +1,386 @@ +[ +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ22 (C) DEC", + "revision": "0A18", + "block_size": 512, + "size": 52445184, + "name": "DEC RZ22", + "file_type": "hds", + "description": "Page/Swap drive for satellite workstations", + "url": "http://lastin.dti.supsi.ch/VET/disks/EK-RZXXD-PS.pdf" +}, +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ23 (C) DEC", + "revision": "0A18", + "block_size": 512, + "size": 104890368, + "name": "DEC RZ23", + "file_type": "hds", + "description": "Smallest usable drive for OpenVMS/VAX", + "url": "http://lastin.dti.supsi.ch/VET/disks/EK-RZXXD-PS.pdf" +}, +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ24 (C) DEC", + "revision": "1D18", + "block_size": 512, + "size": 209813504, + "name": "DEC RZ24", + "file_type": "hds", + "description": "Smallest usable drive for OpenVMS/VAX + Motif", + "url": "http://lastin.dti.supsi.ch/VET/disks/EK-RZXXD-PS.pdf" +}, +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ26L (C) DEC", + "revision": "440C", + "block_size": 512, + "size": 1050040320, + "name": "DEC RZ26L", + "file_type": "hds", + "description": "Largest bootable drive on VAXstation 3100", + "url": "http://lastin.dti.supsi.ch/VET/disks/EK-RZXXD-PS.pdf" +}, +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ28M (C) DEC", + "revision": "0568", + "block_size": 512, + "size": 2104565760, + "name": "DEC RZ28M", + "file_type": "hds", + "description": "Typical 2GB data drive", + "url": "http://lastin.dti.supsi.ch/VET/disks/EK-RZXXD-PS.pdf" +}, +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ29B (C) DEC", + "revision": "0014", + "block_size": 512, + "size": 4290600960, + "name": "DEC RZ29B", + "file_type": "hds", + "description": "Common Alpha server/workstation drive", + "url": "http://lastin.dti.supsi.ch/VET/disks/EK-RZXXD-PS.pdf" +}, +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ55 (C) DEC", + "revision": "", + "block_size": 512, + "size": 332308480, + "name": "DEC RZ55", + "file_type": "hds", + "description": "Largest recognized drive on Ultrix 3.0", + "url": "http://lastin.dti.supsi.ch/VET/disks/EK-RZXXD-PS.pdf" +}, +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ56 (C) DEC", + "revision": "0400", + "block_size": 512, + "size": 665177088, + "name": "DEC RZ56", + "file_type": "hds", + "description": "Expansion 5.25 Drive", + "url": "http://lastin.dti.supsi.ch/VET/disks/EK-RZXXD-PS.pdf" +}, +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ57 (C) DEC", + "revision": "5000", + "block_size": 512, + "size": 1037203456, + "name": "DEC RZ57", + "file_type": "hds", + "description": "Largest recognized drive on Ultrix 3.1 - 4.3", + "url": "http://lastin.dti.supsi.ch/VET/disks/EK-RZXXD-PS.pdf" +}, +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ59 (C) DEC", + "revision": "2000", + "block_size": 512, + "size": 9090874368, + "name": "DEC RZ59", + "file_type": "hds", + "description": "Largest recognized drive on OSF/1 3.x - 5.x", + "url": "https://stason.org/TULARC/pc/hard-drives-hdd/dec/RZ74-3570MB-5-25-FH-SCSI2-FAST.html" +}, +{ + "device_type": "SCHD", + "vendor": "DEC", + "product": "RZ74 (C) DEC", + "revision": "427H", + "block_size": 512, + "size": 3571904000, + "name": "DEC RZ74", + "file_type": "hds", + "description": "Largest recognized drive on Ultrix 4.4 - 4.5", + "url": "" +}, +{ + "device_type": "SCHD", + "vendor": "CONNER", + "product": "CP3200", + "revision": "3.53233", + "block_size": 512, + "size": 212926464, + "name": "Conner CP3200", + "file_type": "hds", + "description": "Very commonly used with Sun-4 systems", + "url": "" +}, +{ + "device_type": "SCHD", + "vendor": "HP", + "product": "C3010", + "revision": "6.0", + "block_size": 512, + "size": 2003032064, + "name": "HP C3010", + "file_type": "hds", + "description": "Largest recognized drive on HP-UX 9.0", + "url": "https://stason.org/TULARC/pc/hard-drives-hdd/hewlett-packard/HP-C3010-001-2003MB-5-25-FH-SCSI2-FAST.html" +}, +{ + "device_type": "SCHD", + "vendor": "MICROP", + "product": "1528-15MD1066702", + "revision": "DD24", + "block_size": 512, + "size": 1342304256, + "name": "Micropolis 1528", + "file_type": "hds", + "description": "Largest recognized drive on HP-UX 8.0", + "url": "https://parisc.wiki.kernel.org/images-parisc/0/06/980723ng.pdf" +}, +{ + "device_type": "SCHD", + "vendor": "MICROP", + "product": "1325", + "revision": "", + "block_size": 512, + "size": 70885376, + "name": "Micropolis 1325", + "file_type": "hds", + "description": "Largest predefined on SunOS 2", + "url": "https://stason.org/TULARC/pc/hard-drives-hdd/micropolis/1325-69MB-5-25-FH-MFM-ST506.html" +}, +{ + "device_type": "SCHD", + "vendor": "MICROP", + "product": "1588T", + "revision": "", + "block_size": 512, + "size": 666324480, + "name": "Micropolis 1588T", + "file_type": "hds", + "description": "Largest predefined on SunOS 3/4 (Sun-3)", + "url": "https://stason.org/TULARC/pc/hard-drives-hdd/micropolis/1588-667MB-5-25-FH-SCSI1-SE.html" +}, +{ + "device_type": "SCHD", + "vendor": "SEAGATE", + "product": "ST32430N SUN2.1G", + "revision": "0444", + "block_size": 512, + "size": 2149071360, + "name": "Seagate SUN2.1G", + "file_type": "hds", + "description": "Largest predefined for SunOS 4 (Sun-4) and Solaris 2.0-2.3", + "url": "" +}, +{ + "device_type": "SCHD", + "vendor": "SEAGATE", + "product": "ST34371W SUN4.2G", + "revision": "7462", + "block_size": 512, + "size": 4290969600, + "name": "Seagate SUN4.2G", + "file_type": "hds", + "description": "Recommended for Solaris 2.4+", + "url": "" +}, +{ + "device_type": "SCHD", + "vendor": "SEAGATE", + "product": "ST39173W SUN9.0G", + "revision": "2815", + "block_size": 512, + "size": 9056904192, + "name": "Seagate SUN9.0G", + "file_type": "hds", + "description": "Recommended for Solaris 2.4+", + "url": "" +}, +{ + "device_type": "SCHD", + "vendor": "SEAGATE", + "product": "ST914603SSUN146G", + "revision": "0B70", + "block_size": 512, + "size": 146789695488, + "name": "Seagate SUN146G", + "file_type": "hds", + "description": "Recommended for Solaris 2.4+", + "url": "" +}, +{ + "device_type": "SCHD", + "vendor": "QUANTUM", + "product": "FIREBALL540S", + "revision": "", + "block_size": 512, + "size": 545400320, + "name": "Quantum Fireball 540S", + "file_type": "hds", + "description": "Recommended for older Macintosh systems.", + "url": "" +}, +{ + "device_type": "SCHD", + "vendor": "MINSCRIB", + "product": " M8425 - SCSI", + "revision": "209A", + "block_size": 512, + "size": 20994048, + "name": "Miniscribe M8425", + "file_type": "hds", + "description": "Recognized by unpatched Apple HD SC Setup.", + "url": "" +}, +{ + "device_type": "SCHD", + "vendor": "QUANTUM", + "product": "FIREBALL ST4.3S", + "revision": "0F0C", + "block_size": 512, + "size": 4337270784, + "name": "Quantum Fireball ST4.3S", + "file_type": "hds", + "description": "Recommended for MacOS 8.1 or later.", + "url": "" +}, +{ + "device_type": "SCHD", + "vendor": "SEAGATE", + "product": "ST32550N", + "revision": "0019", + "block_size": 512, + "size": 2147357696, + "name": "Seagate Barracuda 2GB", + "file_type": "hds", + "description": "2GB is the largest partition size of HFS on older Macs.", + "url": "" +}, +{ + "device_type": "SCRM", + "vendor": "IOMEGA", + "product": "ZIP 100", + "revision": "D.13", + "block_size": 512, + "size": 100663296, + "name": "Iomega ZIP 100", + "file_type": "hdr", + "description": "Removable Iomega ZIP drive, 100MB capacity", + "url": "https://www.win.tue.nl/~aeb/linux/zip/zip-1.html" +}, +{ + "device_type": "SCRM", + "vendor": "IOMEGA", + "product": "ZIP 250", + "revision": "D.58", + "block_size": 512, + "size": 250640384, + "name": "Iomega ZIP 250", + "file_type": "hdr", + "description": "Removable Iomega ZIP drive, 250MB capacity", + "url": "https://www.win.tue.nl/~aeb/linux/zip/zip-1.html" +}, +{ + "device_type": "SCRM", + "vendor": "IOMEGA", + "product": "JAZ 2GB", + "revision": "E.16", + "block_size": 512, + "size": 2001731584, + "name": "Iomega Jaz 2GB", + "file_type": "hdr", + "description": "Removable Iomega Jaz drive, 2GB capacity", + "url": "https://archive.eol.ucar.edu/docs/isf/facilities/iss/manual/creating-data-disks.html" +}, +{ + "device_type": "SCRM", + "vendor": "SYQUEST", + "product": "SQ5110C", + "revision": "4AA0", + "block_size": 512, + "size": 88809472, + "name": "SyQuest 88MB", + "file_type": "hdr", + "description": "SyQuest removable hard drive cartridges, 88MB capacity", + "url": "" +}, +{ + "device_type": "SCCD", + "vendor": "TOSHIBA", + "product": "CD-ROM XM-3401TA", + "revision": "0283", + "block_size": 512, + "size": null, + "name": "Toshiba XM-3401TA", + "file_type": null, + "description": "Boots most SGI, Sun, HP, IBM, DEC etc. Use only with Unix workstations of this vintage.", + "url": "" +}, +{ + "device_type": "SCCD", + "vendor": "SONY", + "product": "CD-ROM CDU-8012", + "revision": "3.1a", + "block_size": 512, + "size": null, + "name": "Sony CDU-8012", + "file_type": null, + "description": "Boots Sun-3. Use only with Unix workstations of this vintage.", + "url": "" +}, +{ + "device_type": "SCCD", + "vendor": "HP", + "product": "A1448A", + "revision": "", + "block_size": 512, + "size": null, + "name": "HP A1448A", + "file_type": null, + "description": "Recognized by HP-UX. Use only with Unix workstations of this vintage.", + "url": "" +}, +{ + "device_type": "SCCD", + "vendor": "DEC", + "product": "RRD42 (C) DEC", + "revision": "4.5d", + "block_size": 512, + "size": null, + "name": "DEC RRD42", + "file_type": null, + "description": "Boots DECstations and VAXstations. Use only with Unix workstations of this vintage.", + "url": "" +} +] diff --git a/src/web/file_cmds.py b/src/web/file_cmds.py index c424f82f..516c0064 100644 --- a/src/web/file_cmds.py +++ b/src/web/file_cmds.py @@ -1,76 +1,354 @@ -import fnmatch import os -import subprocess -import time +import logging -from ractl_cmds import attach_image +from ractl_cmds import ( + get_server_info, + attach_image, + detach_all, + list_devices, + send_pb_command, +) from settings import * - -valid_file_types = ["*.hda", "*.iso", "*.cdr"] -valid_file_types = r"|".join([fnmatch.translate(x) for x in valid_file_types]) +import rascsi_interface_pb2 as proto -def create_new_image(file_name, type, size): - if file_name == "": - file_name = "new_image." + str(int(time.time())) + "." + type - else: - file_name = file_name + "." + type +def list_files(file_types, dir_path): + """ + Takes a list or tuple of str file_types - e.g. ('hda', 'hds') + Returns list of lists files_list: + index 0 is str file name and index 1 is int size in bytes + """ + files_list = [] + for path, dirs, files in os.walk(dir_path): + # Only list selected file types + files = [f for f in files if f.lower().endswith(file_types)] + files_list.extend( + [ + ( + file, + os.path.getsize(os.path.join(path, file)) + ) + for file in files + ] + ) + return files_list - return subprocess.run( - ["dd", "if=/dev/zero", "of=" + base_dir + file_name, "bs=1M", "count=" + size], - capture_output=True, - ) + +def list_config_files(): + """ + Returns a list of RaSCSI config files in cfg_dir: + list of str files_list + """ + files_list = [] + for root, dirs, files in os.walk(cfg_dir): + for file in files: + if file.endswith(".json"): + files_list.append(file) + return files_list + + +def list_images(): + """ + Sends a IMAGE_FILES_INFO command to the server + Returns a dict with boolean status, str msg, and list of dicts files + + """ + command = proto.PbCommand() + command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + + # Get a list of all *.properties files in cfg_dir + from pathlib import PurePath + prop_data = list_files(PROPERTIES_SUFFIX, cfg_dir) + prop_files = [PurePath(x[0]).stem for x in prop_data] + + from zipfile import ZipFile, is_zipfile + server_info = get_server_info() + files = [] + for f in result.image_files_info.image_files: + # Add properties meta data for the image, if applicable + if f.name in prop_files: + process = read_drive_properties(f"{cfg_dir}/{f.name}.{PROPERTIES_SUFFIX}") + prop = process["conf"] + else: + prop = False + if f.name.lower().endswith(".zip"): + zip_path = f"{server_info['image_dir']}/{f.name}" + if is_zipfile(zip_path): + zip = ZipFile(zip_path) + # Get a list of str containing all zipfile members + zip_members = zip.namelist() + # Strip out directories from the list + zip_members = [x for x in zip_members if not x.endswith("/")] + else: + logging.warning(f"{zip_path} is an invalid zip file") + zip_members = False + else: + zip_members = False + + size_mb = "{:,.1f}".format(f.size / 1024 / 1024) + dtype = proto.PbDeviceType.Name(f.type) + files.append( + { + "name": f.name, + "size": f.size, + "size_mb": size_mb, + "detected_type": dtype, + "prop": prop, + "zip": zip_members, + } + ) + + return {"status": result.status, "msg": result.msg, "files": files} + + +def create_new_image(file_name, file_type, size): + """ + Takes str file_name, str file_type, and int size + Sends a CREATE_IMAGE command to the server + Returns dict with boolean status and str msg + """ + command = proto.PbCommand() + command.operation = proto.PbOperation.CREATE_IMAGE + + command.params["file"] = file_name + "." + file_type + command.params["size"] = str(size) + command.params["read_only"] = "false" + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} def delete_image(file_name): - full_path = base_dir + file_name - if os.path.exists(full_path): - os.remove(full_path) - return True + """ + Takes str file_name + Sends a DELETE_IMAGE command to the server + Returns dict with boolean status and str msg + """ + command = proto.PbCommand() + command.operation = proto.PbOperation.DELETE_IMAGE + + command.params["file"] = file_name + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} + + +def delete_file(file_path): + """ + Takes str file_path with the full path to the file to delete + Returns dict with boolean status and str msg + """ + if os.path.exists(file_path): + os.remove(file_path) + return {"status": True, "msg": "File deleted"} else: - return False + return {"status": False, "msg": "Could not delete file"} -def unzip_file(file_name): - import zipfile +def unzip_file(file_name, member=False): + """ + Takes (str) file_name, optional (str) member + Returns dict with (boolean) status and (list of str) msg + """ + from subprocess import run + from re import escape + server_info = get_server_info() - with zipfile.ZipFile(base_dir + file_name, "r") as zip_ref: - zip_ref.extractall(base_dir) - return True + if member == False: + unzip_proc = run( + ["unzip", "-d", server_info["image_dir"], "-n", "-j", \ + f"{server_info['image_dir']}/{file_name}"], capture_output=True + ) + else: + unzip_proc = run( + ["unzip", "-d", server_info["image_dir"], "-n", "-j", \ + f"{server_info['image_dir']}/{file_name}", escape(member)], capture_output=True + ) + if unzip_proc.returncode != 0: + stderr = unzip_proc.stderr.decode("utf-8") + logging.warning(f"Unzipping failed: {stderr}") + return {"status": False, "msg": stderr} - -def rascsi_service(action): - # start/stop/restart - return ( - subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode - == 0 - ) + from re import findall + unzipped = findall("(?:inflating|extracting):(.+)\n", unzip_proc.stdout.decode("utf-8")) + return {"status": True, "msg": unzipped} def download_file_to_iso(scsi_id, url): - import urllib.request + """ + Takes int scsi_id and str url + Returns dict with boolean status and str msg + """ + from time import time + from subprocess import run + + server_info = get_server_info() file_name = url.split("/")[-1] - tmp_ts = int(time.time()) + tmp_ts = int(time()) tmp_dir = "/tmp/" + str(tmp_ts) + "/" os.mkdir(tmp_dir) tmp_full_path = tmp_dir + file_name - iso_filename = base_dir + file_name + ".iso" + iso_filename = f"{server_info['image_dir']}/{file_name}.iso" - urllib.request.urlretrieve(url, tmp_full_path) - # iso_filename = make_cd(tmp_full_path, None, None) # not working yet - iso_proc = subprocess.run( + req_proc = download_to_dir(url, tmp_dir) + + if req_proc["status"] == False: + return {"status": False, "msg": req_proc["msg"]} + + iso_proc = run( ["genisoimage", "-hfs", "-o", iso_filename, tmp_full_path], capture_output=True ) if iso_proc.returncode != 0: - return iso_proc - return attach_image(scsi_id, iso_filename, "SCCD") + return {"status": False, "msg": iso_proc.stderr.decode("utf-8")} + + return {"status": True, "msg": iso_proc.stdout.decode("utf-8"), "file_name": iso_filename} -def download_image(url): - import urllib.request +def download_to_dir(url, save_dir): + """ + Takes str url, str save_dir + Returns dict with boolean status and str msg + """ + import requests + from pathlib import PurePath + file_name = PurePath(url).name + logging.info(f"Making a request to download {url}") - file_name = url.split("/")[-1] - full_path = base_dir + file_name + try: + with requests.get(url, stream=True, headers={"User-Agent": "Mozilla/5.0"}) as req: + req.raise_for_status() + with open(f"{save_dir}/{file_name}", "wb") as download: + for chunk in req.iter_content(chunk_size=8192): + download.write(chunk) + except requests.exceptions.RequestException as e: + logging.warning(f"Request failed: {str(e)}") + return {"status": False, "msg": str(e)} - urllib.request.urlretrieve(url, full_path) + + logging.info(f"Response encoding: {req.encoding}") + logging.info(f"Response content-type: {req.headers['content-type']}") + logging.info(f"Response status code: {req.status_code}") + + return {"status": True, "msg": f"{url} downloaded to {save_dir}"} + + +def write_config(file_name): + """ + Takes str file_name + Returns dict with boolean status and str msg + """ + from json import dump + file_name = cfg_dir + file_name + try: + with open(file_name, "w") as json_file: + devices = list_devices()["device_list"] + if len(devices) == 0: + return {"status": False, "msg": "No attached devices."} + for device in devices: + # Remove keys that we don't want to store in the file + del device["status"] + del device["file"] + # It's cleaner not to store an empty parameter for every device without media + if device["image"] == "": + device["image"] = None + # RaSCSI product names will be generated on the fly by RaSCSI + if device["vendor"] == "RaSCSI": + device["vendor"] = device["product"] = device["revision"] = None + # A block size of 0 is how RaSCSI indicates N/A for block size + if device["block_size"] == 0: + device["block_size"] = None + # Convert to a data type that can be serialized + device["params"] = dict(device["params"]) + dump(devices, json_file, indent=4) + return {"status": True, "msg": f"Successfully wrote to file: {file_name}"} + except (IOError, ValueError, EOFError, TypeError) as e: + logging.error(str(e)) + delete_file(file_name) + return {"status": False, "msg": str(e)} + except: + logging.error(f"Could not write to file: {file_name}") + delete_file(file_name) + return {"status": False, "msg": f"Could not write to file: {file_name}"} + + +def read_config(file_name): + """ + Takes str file_name + Returns dict with boolean status and str msg + """ + from json import load + file_name = cfg_dir + file_name + try: + with open(file_name) as json_file: + detach_all() + devices = load(json_file) + for row in devices: + kwargs = {"device_type": row["device_type"], \ + "image": row["image"], "unit": int(row["un"]), \ + "vendor": row["vendor"], "product": row["product"], \ + "revision": row["revision"], "block_size": row["block_size"]} + params = dict(row["params"]) + for p in params.keys(): + kwargs[p] = params[p] + process = attach_image(row["id"], **kwargs) + if process["status"] == True: + return {"status": process["status"], "msg": f"Successfully read from file: {file_name}"} + else: + return {"status": process["status"], "msg": process["msg"]} + except (IOError, ValueError, EOFError, TypeError) as e: + logging.error(str(e)) + return {"status": False, "msg": str(e)} + except: + logging.error(f"Could not read file: {file_name}") + return {"status": False, "msg": f"Could not read file: {file_name}"} + + +def write_drive_properties(file_name, conf): + """ + Writes a drive property configuration file to the config dir. + Takes file name base (str) and conf (list of dicts) as arguments + Returns dict with boolean status and str msg + """ + from json import dump + file_path = cfg_dir + file_name + try: + with open(file_path, "w") as json_file: + dump(conf, json_file, indent=4) + return {"status": True, "msg": f"Successfully wrote to file: {file_path}"} + except (IOError, ValueError, EOFError, TypeError) as e: + logging.error(str(e)) + delete_file(file_path) + return {"status": False, "msg": str(e)} + except: + logging.error(f"Could not write to file: {file_path}") + delete_file(file_path) + return {"status": False, "msg": f"Could not write to file: {file_path}"} + + + +def read_drive_properties(path_name): + """ + Reads drive properties from json formatted file. + Takes (str) path_name as argument. + Returns dict with boolean status, str msg, dict conf + """ + from json import load + try: + with open(path_name) as json_file: + conf = load(json_file) + return {"status": True, "msg": f"Read data from file: {path_name}", "conf": conf} + except (IOError, ValueError, EOFError, TypeError) as e: + logging.error(str(e)) + return {"status": False, "msg": str(e)} + except: + logging.error(f"Could not read file: {file_name}") + return {"status": False, "msg": f"Could not read file: {path_name}"} diff --git a/src/web/mock/bin/ip b/src/web/mock/bin/ip deleted file mode 100644 index 259dc4b6..00000000 --- a/src/web/mock/bin/ip +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -# Mock responses to rascsi-web -case $1 in - "link") - echo "link here" - ;; - - **) - echo "default" - ;; -esac diff --git a/src/web/mock/bin/rasctl b/src/web/mock/bin/rasctl deleted file mode 100755 index c414d9b1..00000000 --- a/src/web/mock/bin/rasctl +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -# Mock responses to rascsi-web -case $1 in - -f) - echo "logs here" - ;; - - **) - echo "default" - ;; -esac diff --git a/src/web/pi_cmds.py b/src/web/pi_cmds.py index 17f716cc..61554b96 100644 --- a/src/web/pi_cmds.py +++ b/src/web/pi_cmds.py @@ -1,24 +1,26 @@ import subprocess -def rascsi_service(action): +def systemd_service(service, action): # start/stop/restart return ( - subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode + subprocess.run(["sudo", "/bin/systemctl", action, service]).returncode == 0 ) def reboot_pi(): - return subprocess.run(["sudo", "reboot"]).returncode == 0 + subprocess.Popen(["sudo", "reboot"]) + return True def shutdown_pi(): - return subprocess.run(["sudo", "shutdown", "-h", "now"]).returncode == 0 + subprocess.Popen(["sudo", "shutdown", "-h", "now"]) + return True -def running_version(): - ra_web_version = ( +def running_env(): + ra_git_version = ( subprocess.run(["git", "rev-parse", "HEAD"], capture_output=True) .stdout.decode("utf-8") .strip() @@ -28,4 +30,29 @@ def running_version(): .stdout.decode("utf-8") .strip() ) - return ra_web_version + " " + pi_version + return {"git": ra_git_version, "env": pi_version} + + +def running_netatalk(): + """ + Returns int afpd, which is the number of afpd processes currently running + """ + process = subprocess.run(["ps", "aux"], capture_output=True) + output = process.stdout.decode("utf-8") + from re import findall + afpd = findall("afpd", output) + return len(afpd) + + +def is_bridge_setup(): + process = subprocess.run(["brctl", "show"], capture_output=True) + output = process.stdout.decode("utf-8") + if "rascsi_bridge" in output: + return True + return False + + +def disk_space(): + from shutil import disk_usage + total, used, free = disk_usage(__file__) + return {"total": total, "used": used, "free": free} diff --git a/src/web/pwa/android-icon-144x144.png b/src/web/pwa/android-icon-144x144.png new file mode 100644 index 00000000..3ac0f16c Binary files /dev/null and b/src/web/pwa/android-icon-144x144.png differ diff --git a/src/web/pwa/android-icon-192x192.png b/src/web/pwa/android-icon-192x192.png new file mode 100644 index 00000000..eec81071 Binary files /dev/null and b/src/web/pwa/android-icon-192x192.png differ diff --git a/src/web/pwa/android-icon-36x36.png b/src/web/pwa/android-icon-36x36.png new file mode 100644 index 00000000..c477ec80 Binary files /dev/null and b/src/web/pwa/android-icon-36x36.png differ diff --git a/src/web/pwa/android-icon-48x48.png b/src/web/pwa/android-icon-48x48.png new file mode 100644 index 00000000..f23548f7 Binary files /dev/null and b/src/web/pwa/android-icon-48x48.png differ diff --git a/src/web/pwa/android-icon-72x72.png b/src/web/pwa/android-icon-72x72.png new file mode 100644 index 00000000..273c7d62 Binary files /dev/null and b/src/web/pwa/android-icon-72x72.png differ diff --git a/src/web/pwa/android-icon-96x96.png b/src/web/pwa/android-icon-96x96.png new file mode 100644 index 00000000..3c8654ad Binary files /dev/null and b/src/web/pwa/android-icon-96x96.png differ diff --git a/src/web/pwa/apple-icon-114x114.png b/src/web/pwa/apple-icon-114x114.png new file mode 100644 index 00000000..e03fa69a Binary files /dev/null and b/src/web/pwa/apple-icon-114x114.png differ diff --git a/src/web/pwa/apple-icon-120x120.png b/src/web/pwa/apple-icon-120x120.png new file mode 100644 index 00000000..436dcf65 Binary files /dev/null and b/src/web/pwa/apple-icon-120x120.png differ diff --git a/src/web/pwa/apple-icon-144x144.png b/src/web/pwa/apple-icon-144x144.png new file mode 100644 index 00000000..3ac0f16c Binary files /dev/null and b/src/web/pwa/apple-icon-144x144.png differ diff --git a/src/web/pwa/apple-icon-152x152.png b/src/web/pwa/apple-icon-152x152.png new file mode 100644 index 00000000..96b6fd5c Binary files /dev/null and b/src/web/pwa/apple-icon-152x152.png differ diff --git a/src/web/pwa/apple-icon-180x180.png b/src/web/pwa/apple-icon-180x180.png new file mode 100644 index 00000000..c2500d01 Binary files /dev/null and b/src/web/pwa/apple-icon-180x180.png differ diff --git a/src/web/pwa/apple-icon-57x57.png b/src/web/pwa/apple-icon-57x57.png new file mode 100644 index 00000000..fd2dd738 Binary files /dev/null and b/src/web/pwa/apple-icon-57x57.png differ diff --git a/src/web/pwa/apple-icon-60x60.png b/src/web/pwa/apple-icon-60x60.png new file mode 100644 index 00000000..25a6e5c7 Binary files /dev/null and b/src/web/pwa/apple-icon-60x60.png differ diff --git a/src/web/pwa/apple-icon-72x72.png b/src/web/pwa/apple-icon-72x72.png new file mode 100644 index 00000000..273c7d62 Binary files /dev/null and b/src/web/pwa/apple-icon-72x72.png differ diff --git a/src/web/pwa/apple-icon-76x76.png b/src/web/pwa/apple-icon-76x76.png new file mode 100644 index 00000000..4b81937b Binary files /dev/null and b/src/web/pwa/apple-icon-76x76.png differ diff --git a/src/web/pwa/apple-icon-precomposed.png b/src/web/pwa/apple-icon-precomposed.png new file mode 100644 index 00000000..eec81071 Binary files /dev/null and b/src/web/pwa/apple-icon-precomposed.png differ diff --git a/src/web/pwa/apple-icon.png b/src/web/pwa/apple-icon.png new file mode 100644 index 00000000..eec81071 Binary files /dev/null and b/src/web/pwa/apple-icon.png differ diff --git a/src/web/pwa/browserconfig.xml b/src/web/pwa/browserconfig.xml new file mode 100644 index 00000000..22b9700d --- /dev/null +++ b/src/web/pwa/browserconfig.xml @@ -0,0 +1,2 @@ + +#ffffff diff --git a/src/web/pwa/favicon-16x16.png b/src/web/pwa/favicon-16x16.png new file mode 100644 index 00000000..84e83e18 Binary files /dev/null and b/src/web/pwa/favicon-16x16.png differ diff --git a/src/web/pwa/favicon-32x32.png b/src/web/pwa/favicon-32x32.png new file mode 100644 index 00000000..7d90ecef Binary files /dev/null and b/src/web/pwa/favicon-32x32.png differ diff --git a/src/web/pwa/favicon-96x96.png b/src/web/pwa/favicon-96x96.png new file mode 100644 index 00000000..3c8654ad Binary files /dev/null and b/src/web/pwa/favicon-96x96.png differ diff --git a/src/web/pwa/favicon.ico b/src/web/pwa/favicon.ico new file mode 100644 index 00000000..01e040e0 Binary files /dev/null and b/src/web/pwa/favicon.ico differ diff --git a/src/web/pwa/manifest.json b/src/web/pwa/manifest.json new file mode 100644 index 00000000..15c5ce09 --- /dev/null +++ b/src/web/pwa/manifest.json @@ -0,0 +1,41 @@ +{ + "name": "RaSCSI", + "icons": [ + { + "src": "\/pwa\/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "\/pwa\/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "\/pwa\/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "\/pwa\/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "\/pwa\/android-icon-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "\/pwa\/android-icon-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + } + ] +} diff --git a/src/web/pwa/ms-icon-144x144.png b/src/web/pwa/ms-icon-144x144.png new file mode 100644 index 00000000..3ac0f16c Binary files /dev/null and b/src/web/pwa/ms-icon-144x144.png differ diff --git a/src/web/pwa/ms-icon-150x150.png b/src/web/pwa/ms-icon-150x150.png new file mode 100644 index 00000000..5caee48d Binary files /dev/null and b/src/web/pwa/ms-icon-150x150.png differ diff --git a/src/web/pwa/ms-icon-310x310.png b/src/web/pwa/ms-icon-310x310.png new file mode 100644 index 00000000..edc173cc Binary files /dev/null and b/src/web/pwa/ms-icon-310x310.png differ diff --git a/src/web/pwa/ms-icon-70x70.png b/src/web/pwa/ms-icon-70x70.png new file mode 100644 index 00000000..1db78288 Binary files /dev/null and b/src/web/pwa/ms-icon-70x70.png differ diff --git a/src/web/ractl_cmds.py b/src/web/ractl_cmds.py index fbc9cef5..ab03ddf0 100644 --- a/src/web/ractl_cmds.py +++ b/src/web/ractl_cmds.py @@ -1,164 +1,452 @@ -import fnmatch -import os -import subprocess -import re +import logging from settings import * - -valid_file_types = ["*.hda", "*.iso", "*.cdr", "*.zip"] -valid_file_types = r"|".join([fnmatch.translate(x) for x in valid_file_types]) -# List of SCSI ID's you'd like to exclude - eg if you are on a Mac, the System is usually 7 -EXCLUDE_SCSI_IDS = [7] +import rascsi_interface_pb2 as proto -def is_active(): - process = subprocess.run(["systemctl", "is-active", "rascsi"], capture_output=True) - return process.stdout.decode("utf-8").strip() == "active" +def get_server_info(): + """ + Sends a SERVER_INFO command to the server. + Returns a dict with: + - boolean status + - str version (RaSCSI version number) + - list of str log_levels (the log levels RaSCSI supports) + - str current_log_level + - list of int reserved_ids + - 5 distinct lists of strs with file endings recognized by RaSCSI + """ + command = proto.PbCommand() + command.operation = proto.PbOperation.SERVER_INFO + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + version = str(result.server_info.version_info.major_version) + "." +\ + str(result.server_info.version_info.minor_version) + "." +\ + str(result.server_info.version_info.patch_version) + log_levels = result.server_info.log_level_info.log_levels + current_log_level = result.server_info.log_level_info.current_log_level + reserved_ids = list(result.server_info.reserved_ids_info.ids) + image_dir = result.server_info.image_files_info.default_image_folder + + # Creates lists of file endings recognized by RaSCSI + mappings = result.server_info.mapping_info.mapping + sahd = [] + schd = [] + scrm = [] + scmo = [] + sccd = [] + for m in mappings: + if mappings[m] == proto.PbDeviceType.SAHD: + sahd.append(m) + elif mappings[m] == proto.PbDeviceType.SCHD: + schd.append(m) + elif mappings[m] == proto.PbDeviceType.SCRM: + scrm.append(m) + elif mappings[m] == proto.PbDeviceType.SCMO: + scmo.append(m) + elif mappings[m] == proto.PbDeviceType.SCCD: + sccd.append(m) + + return { + "status": result.status, + "version": version, + "log_levels": log_levels, + "current_log_level": current_log_level, + "reserved_ids": reserved_ids, + "image_dir": image_dir, + "sahd": sahd, + "schd": schd, + "scrm": scrm, + "scmo": scmo, + "sccd": sccd, + } -def list_files(): - files_list = [] - for path, dirs, files in os.walk(base_dir): - # Only list valid file types - files = [f for f in files if re.match(valid_file_types, f)] - files_list.extend( - [ - ( - os.path.join(path, file), - # TODO: move formatting to template - "{:,.0f}".format( - os.path.getsize(os.path.join(path, file)) / float(1 << 20) - ) - + " MB", - ) - for file in files - ] - ) - return files_list +def get_network_info(): + """ + Sends a NETWORK_INTERFACES_INFO command to the server. + Returns a dict with: + - boolean status + - list of str ifs (network interfaces detected by RaSCSI) + """ + command = proto.PbCommand() + command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + ifs = result.network_interfaces_info.name + return {"status": result.status, "ifs": ifs} -def list_config_files(): - files_list = [] - for root, dirs, files in os.walk(base_dir): - for file in files: - if file.endswith(".csv"): - files_list.append(file) - return files_list +def get_device_types(): + """ + Sends a DEVICE_TYPES_INFO command to the server. + Returns a dict with: + - boolean status + - list of str device_types (device types that RaSCSI supports, ex. SCHD, SCCD, etc) + """ + command = proto.PbCommand() + command.operation = proto.PbOperation.DEVICE_TYPES_INFO + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + device_types = [] + for t in result.device_types_info.properties: + device_types.append(proto.PbDeviceType.Name(t.type)) + return {"status": result.status, "device_types": device_types} + -def get_valid_scsi_ids(devices): - invalid_list = EXCLUDE_SCSI_IDS.copy() - for device in devices: - if device["file"] != "NO MEDIA" and device["file"] != "-": - invalid_list.append(int(device["id"])) +def get_valid_scsi_ids(devices, reserved_ids): + """ + Takes a list of dicts devices, and list of ints reserved_ids. + Returns: + - list of ints valid_ids, which are the SCSI ids that are not reserved + - int recommended_id, which is the id that the Web UI should default to recommend + """ + occupied_ids = [] + for d in devices: + occupied_ids.append(d["id"]) - valid_list = list(range(8)) - for id in invalid_list: - valid_list.remove(id) - valid_list.reverse() + unoccupied_ids = [i for i in list(range(8)) if i not in reserved_ids + occupied_ids] + unoccupied_ids.sort() + valid_ids = [i for i in list(range(8)) if i not in reserved_ids] + valid_ids.sort(reverse=True) - return valid_list - - -def get_type(scsi_id): - return list_devices()[int(scsi_id)]["type"] - - -def attach_image(scsi_id, image, device_type): - if device_type == "SCCD" and get_type(scsi_id) == "SCCD": - return insert(scsi_id, image) - elif device_type == "SCDP": - attach_daynaport(scsi_id) + if len(unoccupied_ids) > 0: + recommended_id = unoccupied_ids[-1] else: - if device_type == "SCCD": - device_type = "cd" - return subprocess.run( - ["rasctl", "-c", "attach", "-t", device_type, "-i", scsi_id, "-f", image], - capture_output=True, - ) + recommended_id = occupied_ids.pop(0) + + return valid_ids, recommended_id -def detach_by_id(scsi_id): - return subprocess.run(["rasctl", "-c" "detach", "-i", scsi_id], capture_output=True) +def attach_image(scsi_id, **kwargs): + """ + Takes int scsi_id and kwargs containing 0 or more device properties + + If the current attached device is a removable device wihout media inserted, + this sends a INJECT command to the server. + If there is no currently attached device, this sends the ATTACH command to the server. + + Returns boolean status and str msg + + """ + command = proto.PbCommand() + devices = proto.PbDeviceDefinition() + devices.id = int(scsi_id) + + if "device_type" in kwargs.keys(): + if kwargs["device_type"] not in [None, ""]: + devices.type = proto.PbDeviceType.Value(str(kwargs["device_type"])) + if "unit" in kwargs.keys(): + if kwargs["unit"] not in [None, ""]: + devices.unit = kwargs["unit"] + if "image" in kwargs.keys(): + if kwargs["image"] not in [None, ""]: + devices.params["file"] = kwargs["image"] + + # Handling the inserting of media into an attached removable type device + device_type = kwargs.get("device_type", None) + currently_attached = list_devices(scsi_id, kwargs.get("unit"))["device_list"] + if len(currently_attached) > 0: + current_type = currently_attached[0]["device_type"] + else: + current_type = None + + if device_type in REMOVABLE_DEVICE_TYPES and current_type in REMOVABLE_DEVICE_TYPES: + if current_type != device_type: + return {"status": False, "msg": f"Cannot insert an image for \ + {device_type} into a {current_type} device."} + else: + command.operation = proto.PbOperation.INSERT + # Handling attaching a new device + else: + command.operation = proto.PbOperation.ATTACH + if "interfaces" in kwargs.keys(): + if kwargs["interfaces"] not in [None, ""]: + devices.params["interfaces"] = kwargs["interfaces"] + if "vendor" in kwargs.keys(): + if kwargs["vendor"] != None: + devices.vendor = kwargs["vendor"] + if "product" in kwargs.keys(): + if kwargs["product"] != None: + devices.product = kwargs["product"] + if "revision" in kwargs.keys(): + if kwargs["revision"] != None: + devices.revision = kwargs["revision"] + if "block_size" in kwargs.keys(): + if kwargs["block_size"] not in [None, ""]: + devices.block_size = int(kwargs["block_size"]) + + command.devices.append(devices) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} + + +def detach_by_id(scsi_id, un=None): + """ + Takes int scsi_id and optional int un + Sends a DETACH command to the server. + Returns boolean status and str msg. + """ + devices = proto.PbDeviceDefinition() + devices.id = int(scsi_id) + if un != None: + devices.unit = int(un) + + command = proto.PbCommand() + command.operation = proto.PbOperation.DETACH + command.devices.append(devices) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} def detach_all(): - for scsi_id in range(0, 7): - subprocess.run(["rasctl", "-c" "detach", "-i", str(scsi_id)]) + """ + Sends a DETACH_ALL command to the server. + Returns boolean status and str msg. + """ + command = proto.PbCommand() + command.operation = proto.PbOperation.DETACH_ALL + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} -def disconnect_by_id(scsi_id): - return subprocess.run( - ["rasctl", "-c", "disconnect", "-i", scsi_id], capture_output=True - ) +def eject_by_id(scsi_id, un=None): + """ + Takes int scsi_id and optional int un. + Sends an EJECT command to the server. + Returns boolean status and str msg. + """ + devices = proto.PbDeviceDefinition() + devices.id = int(scsi_id) + if un != None: + devices.unit = int(un) + + command = proto.PbCommand() + command.operation = proto.PbOperation.EJECT + command.devices.append(devices) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} -def eject_by_id(scsi_id): - return subprocess.run(["rasctl", "-i", scsi_id, "-c", "eject"]) +def list_devices(scsi_id=None, un=None): + """ + Takes optional int scsi_id and optional int un. + Sends a DEVICES_INFO command to the server. + If no scsi_id is provided, returns a list of dicts of all attached devices. + If scsi_id is is provided, returns a list of one dict for the given device. + If no attached device is found, returns an empty list. + Returns boolean status, list of dicts device_list + """ + from os import path + command = proto.PbCommand() + command.operation = proto.PbOperation.DEVICES_INFO + # If method is called with scsi_id parameter, return the info on those devices + # Otherwise, return the info on all attached devices + if scsi_id != None: + device = proto.PbDeviceDefinition() + device.id = int(scsi_id) + if un != None: + device.unit = int(un) + command.devices.append(device) -def insert(scsi_id, image): - return subprocess.run( - ["rasctl", "-i", scsi_id, "-c", "insert", "-f", image], capture_output=True - ) + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) - -def attach_daynaport(scsi_id): - return subprocess.run( - ["rasctl", "-i", scsi_id, "-c", "attach", "-t", "daynaport"], - capture_output=True, - ) - - -def is_bridge_setup(interface): - process = subprocess.run(["brctl", "show"], capture_output=True) - output = process.stdout.decode("utf-8") - if "rascsi_bridge" in output and interface in output: - return True - return False - - -def daynaport_setup_bridge(interface): - return subprocess.run( - [f"{base_dir}../RASCSI/src/raspberrypi/setup_bridge.sh", interface], - capture_output=True, - ) - - -def rascsi_service(action): - # start/stop/restart - return ( - subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode - == 0 - ) - - -def list_devices(): device_list = [] - for id in range(8): - device_list.append({"id": str(id), "un": "-", "type": "-", "file": "-"}) - output = subprocess.run(["rasctl", "-l"], capture_output=True).stdout.decode( - "utf-8" - ) - for line in output.splitlines(): - # Valid line to process, continue - if ( - not line.startswith("+") - and not line.startswith("| ID |") - and ( - not line.startswith("No device is installed.") - or line.startswith("No images currently attached.") - ) - and len(line) > 0 - ): - line.rstrip() - device = {} - segments = line.split("|") - if len(segments) > 4: - idx = int(segments[1].strip()) - device_list[idx]["id"] = str(idx) - device_list[idx]["un"] = segments[2].strip() - device_list[idx]["type"] = segments[3].strip() - device_list[idx]["file"] = segments[4].strip() + n = 0 - return device_list + # Return an empty list if no devices are attached + if len(result.devices_info.devices) == 0: + return {"status": False, "device_list": []} + + while n < len(result.devices_info.devices): + did = result.devices_info.devices[n].id + dun = result.devices_info.devices[n].unit + dtype = proto.PbDeviceType.Name(result.devices_info.devices[n].type) + dstat = result.devices_info.devices[n].status + dprop = result.devices_info.devices[n].properties + + # Building the status string + # TODO: This formatting should probably be moved elsewhere + dstat_msg = [] + if dprop.read_only == True: + dstat_msg.append("Read-Only") + if dstat.protected == True and dprop.protectable == True: + dstat_msg.append("Write-Protected") + if dstat.removed == True and dprop.removable == True: + dstat_msg.append("No Media") + if dstat.locked == True and dprop.lockable == True: + dstat_msg.append("Locked") + + dpath = result.devices_info.devices[n].file.name + dfile = path.basename(dpath) + dparam = result.devices_info.devices[n].params + dven = result.devices_info.devices[n].vendor + dprod = result.devices_info.devices[n].product + drev = result.devices_info.devices[n].revision + dblock = result.devices_info.devices[n].block_size + dsize = int(result.devices_info.devices[n].block_count) * int(dblock) + + device_list.append( + { + "id": did, + "un": dun, + "device_type": dtype, + "status": ", ".join(dstat_msg), + "image": dpath, + "file": dfile, + "params": dparam, + "vendor": dven, + "product": dprod, + "revision": drev, + "block_size": dblock, + "size": dsize, + } + ) + n += 1 + + return {"status": True, "device_list": device_list} + + +def sort_and_format_devices(devices): + """ + Takes a list of dicts devices and returns a list of dicts. + Sorts by SCSI ID acending (0 to 7). + For SCSI IDs where no device is attached, inject a dict with placeholder text. + """ + occupied_ids = [] + for d in devices: + occupied_ids.append(d["id"]) + + formatted_devices = devices + + # Add padding devices and sort the list + for id in range(8): + if id not in occupied_ids: + formatted_devices.append({"id": id, "device_type": "-", \ + "status": "-", "file": "-", "product": "-"}) + # Sort list of devices by id + formatted_devices.sort(key=lambda dic: str(dic["id"])) + + return formatted_devices + + +def set_log_level(log_level): + """ + Sends a LOG_LEVEL command to the server. + Takes str log_level as an argument. + Returns boolean status and str msg. + """ + command = proto.PbCommand() + command.operation = proto.PbOperation.LOG_LEVEL + command.params["level"] = str(log_level) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} + + +def send_pb_command(payload): + """ + Takes a str containing a serialized protobuf as argument. + Establishes a socket connection with RaSCSI. + """ + # Host and port number where rascsi is listening for socket connections + HOST = 'localhost' + PORT = 6868 + + counter = 0 + tries = 100 + error_msg = "" + + import socket + while counter < tries: + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((HOST, PORT)) + return send_over_socket(s, payload) + except socket.error as error: + counter += 1 + logging.warning("The RaSCSI service is not responding - attempt " + \ + str(counter) + "/" + str(tries)) + error_msg = str(error) + + logging.error(error_msg) + + # After failing all attempts, throw a 404 error + from flask import abort + abort(404, "Failed to connect to RaSCSI at " + str(HOST) + ":" + str(PORT) + \ + " with error: " + error_msg + ". Is the RaSCSI service running?") + + +def send_over_socket(s, payload): + """ + Takes a socket object and str payload with serialized protobuf. + Sends payload to RaSCSI over socket and captures the response. + Tries to extract and interpret the protobuf header to get response size. + Reads data from socket in 2048 bytes chunks until all data is received. + + """ + from struct import pack, unpack + + # Sending the magic word "RASCSI" to authenticate with the server + s.send(b"RASCSI") + # Prepending a little endian 32bit header with the message size + s.send(pack("= 4: + # Extracting the response header to get the length of the response message + response_length = unpack(" - RaSCSI-web is Starting + RaSCSI-Web is Starting
-

RaSCSI Web is starting....

+

RaSCSI-Web is Starting....

This page will automatically refresh.

-

First boot and upgrades can take a second to resolve dependancies.

+

First boot and upgrades can take a second while resolving dependencies.

If you're seeing this page for over a minute please check the logs at sudo journalctl -f

- \ No newline at end of file + diff --git a/src/web/service-infra/nginx-default.conf b/src/web/service-infra/nginx-default.conf index bffa96cc..58804911 100644 --- a/src/web/service-infra/nginx-default.conf +++ b/src/web/service-infra/nginx-default.conf @@ -1,12 +1,21 @@ # /etc/nginx/sites-available/default # Simple proxy_pass for RaSCSI-web server { + listen [::]:80 default_server; + listen 80 default_server; + location / { - proxy_pass http://localhost:8080; + proxy_pass http://127.0.0.1:8080; } + # Large files + client_max_body_size 0; + proxy_read_timeout 1000; + proxy_connect_timeout 1000; + proxy_send_timeout 1000; + error_page 502 /502.html; location = /502.html { root /var/www/html/; } -} \ No newline at end of file +} diff --git a/src/web/service-infra/rascsi-web.service b/src/web/service-infra/rascsi-web.service index 24d667a2..9c75d28f 100644 --- a/src/web/service-infra/rascsi-web.service +++ b/src/web/service-infra/rascsi-web.service @@ -11,4 +11,4 @@ StandardError=syslog SyslogIdentifier=RASCSIWEB [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target diff --git a/src/web/settings.py b/src/web/settings.py index 85036168..d9231f6d 100644 --- a/src/web/settings.py +++ b/src/web/settings.py @@ -1,4 +1,20 @@ -import os +from os import getenv, getcwd -base_dir = os.getenv("BASE_DIR", "/home/pi/images/") -MAX_FILE_SIZE = os.getenv("MAX_FILE_SIZE", 1024 * 1024 * 1024 * 2) # 2gb +# TODO: Make home_dir portable when running rascsi-web +# as a service, since the HOME env variable doesn't get set then. +home_dir = getenv("HOME", "/home/pi") +cfg_dir = f"{home_dir}/.config/rascsi/" +afp_dir = f"{home_dir}/afpshare" +web_dir = getcwd() + +DEFAULT_CONFIG = "default.json" +MAX_FILE_SIZE = getenv("MAX_FILE_SIZE", 1024 * 1024 * 1024 * 4) # 4gb + +ARCHIVE_FILE_SUFFIX = ("zip",) + +# File containing canonical drive properties +DRIVE_PROPERTIES_FILE = web_dir + "/drive_properties.json" +# File ending used for drive properties files +PROPERTIES_SUFFIX = "properties" + +REMOVABLE_DEVICE_TYPES = ("SCCD", "SCRM", "SCMO") diff --git a/src/web/start.sh b/src/web/start.sh index ee0b1b37..6e65909d 100755 --- a/src/web/start.sh +++ b/src/web/start.sh @@ -2,7 +2,7 @@ set -e # set -x # Uncomment to Debug -cd $(dirname $0) +cd "$(dirname "$0")" # verify packages installed ERROR=0 if ! command -v genisoimage &> /dev/null ; then @@ -37,6 +37,7 @@ if ! test -e venv; then echo "Activating venv" source venv/bin/activate echo "Installing requirements.txt" + pip install wheel pip install -r requirements.txt git rev-parse HEAD > current fi @@ -55,4 +56,4 @@ else fi echo "Starting web server..." -python3 web.py \ No newline at end of file +python3 web.py diff --git a/src/web/static/style.css b/src/web/static/style.css index 9d5dd136..cf7ebf5c 100644 --- a/src/web/static/style.css +++ b/src/web/static/style.css @@ -41,4 +41,74 @@ table, tr, td { color: white; font-size:20px; background-color:green; -} \ No newline at end of file +} + +td.inactive { + text-align:center; + background-color:tan; +} + +summary.heading { + color: black; + font-size: large; + font-weight: bold; + margin: 0px; +} + +.dropzone, .dropzone * { + box-sizing: border-box; +} + +.dropzone { + position: relative; +} + +.dropzone .dz-preview { + position: relative; + display: inline-block; + width: 120px; + margin: .5em; +} + +.dropzone .dz-preview .dz-progress { + display: block; + height: 15px; + border: 1px solid #aaa; +} + +.dropzone .dz-preview .dz-progress .dz-upload { + display: block; + height: 100%; + width: 0; + background: green; +} + +.dropzone .dz-preview .dz-error-message { + color: red; + display: none; +} + +.dropzone .dz-preview.dz-error .dz-error-message { + display: block; +} + +.dropzone .dz-preview.dz-error .dz-error-mark { + display: block; + filter: drop-shadow(0px 0px 2px red); +} + +.dropzone .dz-preview.dz-success .dz-success-mark { + display: block; + filter: drop-shadow(0px 0px 2px green); +} + +.dropzone .dz-preview .dz-error-mark, .dropzone .dz-preview .dz-success-mark { + position: absolute; + display: none; + left: 30px; + top: 30px; + width: 54px; + height: 58px; + left: 50%; + margin-left: -27px; +} diff --git a/src/web/templates/base.html b/src/web/templates/base.html index ddc9dc40..751040cb 100644 --- a/src/web/templates/base.html +++ b/src/web/templates/base.html @@ -1,10 +1,44 @@ RaSCSI Control Page + + + + + + + + + + + + + + + + + + + + + +
- {% block header %}{% endblock %} +Service Running + + + + + + +
+ +

RaSCSI - 68kmla Edition

+
+
{% for category, message in get_flashed_messages(with_categories=true) %} @@ -19,6 +53,7 @@ {% block content %}{% endblock %}
-
\ No newline at end of file + diff --git a/src/web/templates/drives.html b/src/web/templates/drives.html new file mode 100644 index 00000000..3217ed53 --- /dev/null +++ b/src/web/templates/drives.html @@ -0,0 +1,141 @@ +{% extends "base.html" %} + + {% block content %} +

Cancel

+

Disclaimer

+

These device profiles are provided as-is with no guarantee to work on the systems mentioned. You may need appropirate device drivers and/or configuration parameters. If you have improvement suggestions or success stories to share we would love to hear from you, so please connect with us at GitHub or Discord!

+

Hard Drives

+ + + + + + + + + + + {% for hd in hd_conf %} + + + + + + + + {% endfor %} + +
NameSize (MB)DescriptionRef.Action
{{hd.name}}{{hd.size_mb}}{{hd.description}} + {% if hd.url != "" %} + Link + {% else %} + - + {% endif %} + +
+ + + + + + + + + .{{hd.file_type}} + +
+
+ +
+ +

CD-ROM Drives

+

This will create a properties file for the given CD-ROM image. No new image file will be created.

+ + + + + + + + + + {% for cd in cd_conf %} + + + + + + + + {% endfor %} + +
NameSize (MB)DescriptionRef.Action
{{cd.name}}{{cd.size_mb}}{{cd.description}} + {% if cd.url != "" %} + Link + {% else %} + - + {% endif %} + +
+ + + + + + + +
+
+ +
+ +

Removable Drives

+ + + + + + + + + + {% for rm in rm_conf %} + + + + + + + + {% endfor %} + +
NameSize (MB)DescriptionRef.Action
{{rm.name}}{{rm.size_mb}}{{rm.description}} + {% if rm.url != "" %} + Link + {% else %} + - + {% endif %} + +
+ + + + + + + + + .{{rm.file_type}} + +
+
+

Available disk space on the Pi: {{free_disk}} MB

+

Cancel

+ +{% endblock %} diff --git a/src/web/templates/index.html b/src/web/templates/index.html index d7bbccdd..169ecbe7 100644 --- a/src/web/templates/index.html +++ b/src/web/templates/index.html @@ -1,81 +1,125 @@ {% extends "base.html" %} -{% block header %} -{% if active %} -Service Running -{% else %} -Service Stopped -{% endif %} - - - - - - -
- -

RaSCSI - 68kmla Edition

-
-
-{% endblock %} - {% block content %} -

Current RaSCSI Configuration

+
+ Current RaSCSI Configuration +
    +
  • Displays the currently attached devices for each available SCSI ID.
  • +
  • Save and load device configurations into {{cfg_dir}}
  • +
  • The default configuration will be loaded when the Web UI starts up, if available.
  • +
+
+ +

- + {% if config_files %} {% for config in config_files %} - + {% endfor %} + {% else %} + + {% endif %} - + +
+

+

- +
-
- -
+

+ {% if luns %} + + {% endif %} + - + + {% for device in devices %} - {% if device.id != "7" %} + {% if device["id"] not in reserved_scsi_ids %} - - - + {% endif %} + + + + {% if device.vendor == "RaSCSI" %} + + {% else %} + + {% endif %} + {% else %} - - - - + + {% if luns %} + + {% endif %} + + + + + {% endif %} {% endfor %}
IDLUNTypeStatus FileActionProductActions
{{device.id}}{{device.type}}{{device.file}} - {% if device.type == "SCCD" and device.file != "NO MEDIA" %} + {% if luns %} + {{device.un}}{{device.device_type}}{{device.status}}{{device.file}}{{device.product}}{{device.vendor}} {{device.product}} + {% if device.device_type != "-" %} + {% if device.device_type in removable_device_types and "No Media" not in device.status %}
+
+
+ + + +
{% else %}
+
+
+ + + +
+ {% endif %} {% endif %}
{{device.id}}-Host Machine-{{device.id}}Reserved ID
+

+

+ +
+

+ +
+ +
+ Image File Management +
    +
  • Manage image files in the active RaSCSI image directory: {{base_dir}}
  • +
  • Select a valid SCSI ID and LUN to attach to. Unless you know what you're doing, always use LUN 0.
  • +
  • If RaSCSI was unable to detect the device type associated with the image, you can choose the type from the dropdown.
  • +
  • Types: SAHD = SASI HDD | SCHD = SCSI HDD | SCRM = Removable | SCMO = Magneto-Optical | SCCD = CD-ROM | SCBR = Host Bridge | SCDP = DaynaPORT
  • +
+
-

Image File Management

@@ -85,93 +129,175 @@ {% for file in files %} - + {% if file["prop"] %} + + {% elif file["zip"] %} + + {% else %} + + {% endif %} - {% endfor %}
{{file[0].replace(base_dir, '')}} +
+ {{file["name"]}} +
    + {% for key in file["prop"] %} +
  • {{key}}: {{file['prop'][key]}}
  • + {% endfor %} +
+
+
+
+ {{file["name"]}} +
    + {% for member in file["zip"] %} +
  • + +
    + + + +
    +
  • + {% endfor %} +
+
+
{{file["name"]}}
- - + +
-
- -
+ {% if file["name"] in attached_images %} +
Attached!
+ {% else %} + {% if file["name"].lower().endswith(archive_file_suffix) %} + + + + + {% else %} +
+ + + + + + + {% if file["detected_type"] != "UNDEFINED" %} + + {{file["detected_type"]}} + {% else %} + + {% endif %}
- +
- {% if file[0].endswith('.zip') or file[0].endswith('.ZIP') %} -
- - -
- {% endif %} + {% endif %}
+

Available disk space on the Pi: {{free_disk}} MB


-

Attach Ethernet Adapter

-

Emulates a SCSI DaynaPORT Ethernet Adapter. Host drivers required.

+ +
+ Attach Ethernet Adapter +
    +
  • Emulates a SCSI DaynaPORT Ethernet Adapter. Host drivers and configuration required.
  • +
  • If you have a DHCP setup, choose only the interface, and ignore the Static IP fields when attaching.
  • +
  • Configure network forwarding by running easyinstall.sh, or follow the manual steps in the wiki.
  • + {% if bridge_configured %} +
  • The rascsi_bridge interface is active and ready to be used by DaynaPORT!
  • + {% endif %} +
+
+
+ + + + + +
+ +
+ +
+ Upload File +
    +
  • Uploads file to {{base_dir}}. The largest file size accepted is {{max_file_size}} MB.
  • +
  • For unrecognized file types, try renaming hard drive images to '.hds' and CD-ROM images to '.iso' before uploading.
  • +
  • Recognized file types: {{valid_file_suffix}}
  • +
+
+ + - -
- {% if bridge_configured %} - Bridge is currently configured! - {% else %} -
- -
- {% endif %} -
- -
-

Upload File

-

Uploads file to {{base_dir}}. Max file size is set to {{max_file_size / 1024 /1024 }}MB

- - -
-
- - - +
+
-

Download File from Web

-

Given a URL, download that file to the {{base_dir}}

+
+ Download File to Images +
    +
  • Given a URL, download that file to the {{base_dir}} directory.
  • +
+
+ - - + @@ -180,8 +306,47 @@
-

Download File from web and create HFS CD

-

Given a URL this will download a file, create a HFS iso, and mount it on the device id given.

+
+ Download File to AppleShare +
    +
  • Given a URL, download that file to the {{afp_dir}} directory and share it over AFP.
  • +
  • Manage the files you download here through AppleShare on your vintage Mac.
  • +
  • Requires Netatalk to be installed and configured correctly for your network.
  • +
+
+ {% if netatalk_configured %} +
-
+
+ - +
+ + + +
+
+ + + +
+
+ {% if netatalk_configured == 1 %} +

The AppleShare server is running. No active connections

+ {% elif netatalk_configured == 2 %} +

{{netatalk_configured - 1}} active AFP connection

+ {% elif netatalk_configured > 2 %} +

{{netatalk_configured - 1}} active AFP connections

+ {% endif %} + {% else %} +

Install Netatalk to use the AppleTalk File Server. + {% endif %} + +


+ +
+ Download File from Web and Create HFS CD (Macintosh) +
    +
  • Given a URL this will download a file, create a HFS iso, and mount it on the SCSI ID given.
  • +
  • Requires a compatible CD-ROM driver installed on the target system.
  • +
+
+ @@ -201,25 +366,31 @@
@@ -189,11 +354,11 @@
- +

-

Create Empty Disk Image File

+ +
+ Create Empty Disk Image File +
    +
  • The Generic image type is recommended for most systems
  • +
  • APPLE GENUINE and NEC GENUINE image types will make RaSCSI masquerade as a particular drive type that are recognized by Mac and PC98 systems, respectively.
  • +
  • SASI images should only be used on early X68000 or UNIX workstation systems that use this pre-SCSI standard.
  • +
+
@@ -227,7 +398,80 @@
- + - - + +

-

Raspberry Pi Operations

+ +
+ Create Named Drive +
    +
  • Here you can create pairs of images and properties files from a list of real-life drives.
  • +
  • This will make RaSCSI use certain vendor strings and block sizes that may improve compatibility with certain systems
  • +
+
+ +

Create a named disk image that mimics real-life drives

+ +
+ +
+ Logging +
    +
  • Get a certain number of lines of service logs with the given scope.
  • +
+
+ + + + + +
+
+ + + + + +
+
+ +
+ +
+ Server Log Level +
    +
  • Change the log level of the RaSCSI backend service.
  • +
  • The dropdown will indicate the current log level.
  • +
+
+ + + + + +
+
+ + + +
+
+ +
+ +
+ Raspberry Pi Operations +
    +
  • Issue reboot or shutdown commands to the Raspberr Pi.
  • +
  • You can also restart the RaSCSI backend service here.
  • +
+
+
@@ -248,9 +492,5 @@
-{% endblock %} -{% block footer %} -
{{version}}
-
Logs
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/src/web/web.py b/src/web/web.py index 2268d43d..c8085af0 100644 --- a/src/web/web.py +++ b/src/web/web.py @@ -1,27 +1,51 @@ -from flask import Flask, render_template, request, flash, url_for, redirect, send_file -from werkzeug.utils import secure_filename +import logging +from flask import ( + Flask, + render_template, + request, + flash, + url_for, + redirect, + send_file, + send_from_directory, + make_response, +) from file_cmds import ( + list_config_files, + list_images, create_new_image, download_file_to_iso, delete_image, + delete_file, unzip_file, - download_image, + download_to_dir, + write_config, + read_config, + write_drive_properties, + read_drive_properties, +) +from pi_cmds import ( + shutdown_pi, + reboot_pi, + running_env, + systemd_service, + running_netatalk, + is_bridge_setup, + disk_space, ) -from pi_cmds import shutdown_pi, reboot_pi, running_version, rascsi_service from ractl_cmds import ( attach_image, list_devices, - is_active, - list_files, + sort_and_format_devices, detach_by_id, eject_by_id, get_valid_scsi_ids, - attach_daynaport, - is_bridge_setup, - daynaport_setup_bridge, - list_config_files, detach_all, + get_server_info, + get_network_info, + get_device_types, + set_log_level, ) from settings import * @@ -30,63 +54,243 @@ app = Flask(__name__) @app.route("/") def index(): + server_info = get_server_info() + disk = disk_space() devices = list_devices() - scsi_ids = get_valid_scsi_ids(devices) + device_types=get_device_types() + files = list_images() + config_files = list_config_files() + + sorted_image_files = sorted(files["files"], key = lambda x: x["name"].lower()) + sorted_config_files = sorted(config_files, key = lambda x: x.lower()) + + from pathlib import Path + attached_images = [] + luns = 0 + for d in devices["device_list"]: + attached_images.append(Path(d["image"]).name) + luns += int(d["un"]) + + reserved_scsi_ids = server_info["reserved_ids"] + scsi_ids, recommended_id = get_valid_scsi_ids(devices["device_list"], reserved_scsi_ids) + formatted_devices = sort_and_format_devices(devices["device_list"]) + + valid_file_suffix = "."+", .".join( + server_info["sahd"] + + server_info["schd"] + + server_info["scrm"] + + server_info["scmo"] + + server_info["sccd"] + + list(ARCHIVE_FILE_SUFFIX) + ) + return render_template( "index.html", - bridge_configured=is_bridge_setup("eth0"), - devices=devices, - active=is_active(), - files=list_files(), - config_files=list_config_files(), - base_dir=base_dir, + bridge_configured=is_bridge_setup(), + netatalk_configured=running_netatalk(), + devices=formatted_devices, + files=sorted_image_files, + config_files=sorted_config_files, + base_dir=server_info["image_dir"], + cfg_dir=cfg_dir, + afp_dir=afp_dir, scsi_ids=scsi_ids, - max_file_size=MAX_FILE_SIZE, - version=running_version(), + recommended_id=recommended_id, + attached_images=attached_images, + luns=luns, + reserved_scsi_ids=reserved_scsi_ids, + max_file_size=int(MAX_FILE_SIZE / 1024 / 1024), + running_env=running_env(), + version=server_info["version"], + log_levels=server_info["log_levels"], + current_log_level=server_info["current_log_level"], + netinfo=get_network_info(), + device_types=device_types["device_types"], + free_disk=int(disk["free"] / 1024 / 1024), + valid_file_suffix=valid_file_suffix, + archive_file_suffix=ARCHIVE_FILE_SUFFIX, + removable_device_types=REMOVABLE_DEVICE_TYPES, ) +@app.route("/drive/list", methods=["GET"]) +def drive_list(): + """ + Sets up the data structures and kicks off the rendering of the drive list page + """ + server_info = get_server_info() + disk = disk_space() + + # Reads the canonical drive properties into a dict + # The file resides in the current dir of the web ui process + from pathlib import Path + drive_properties = Path(DRIVE_PROPERTIES_FILE) + if drive_properties.is_file(): + process = read_drive_properties(str(drive_properties)) + if process["status"] == False: + flash(process["msg"], "error") + return redirect(url_for("index")) + conf = process["conf"] + else: + flash("Could not read drive properties from " + str(drive_properties), "error") + return redirect(url_for("index")) + + hd_conf = [] + cd_conf = [] + rm_conf = [] + + from werkzeug.utils import secure_filename + for d in conf: + if d["device_type"] == "SCHD": + d["secure_name"] = secure_filename(d["name"]) + d["size_mb"] = "{:,.2f}".format(d["size"] / 1024 / 1024) + hd_conf.append(d) + elif d["device_type"] == "SCCD": + d["size_mb"] = "N/A" + cd_conf.append(d) + elif d["device_type"] == "SCRM": + d["secure_name"] = secure_filename(d["name"]) + d["size_mb"] = "{:,.2f}".format(d["size"] / 1024 / 1024) + rm_conf.append(d) + + files=list_images() + sorted_image_files = sorted(files["files"], key = lambda x: x["name"].lower()) + hd_conf = sorted(hd_conf, key = lambda x: x["name"].lower()) + cd_conf = sorted(cd_conf, key = lambda x: x["name"].lower()) + rm_conf = sorted(rm_conf, key = lambda x: x["name"].lower()) + + return render_template( + "drives.html", + files=sorted_image_files, + base_dir=server_info["image_dir"], + hd_conf=hd_conf, + cd_conf=cd_conf, + rm_conf=rm_conf, + running_env=running_env(), + version=server_info["version"], + free_disk=int(disk["free"] / 1024 / 1024), + cdrom_file_suffix=tuple(server_info["sccd"]), + ) + + +@app.route('/pwa/') +def send_pwa_files(path): + return send_from_directory('pwa', path) + + +@app.route("/drive/create", methods=["POST"]) +def drive_create(): + vendor = request.form.get("vendor") + product = request.form.get("product") + revision = request.form.get("revision") + block_size = request.form.get("block_size") + size = request.form.get("size") + file_type = request.form.get("file_type") + file_name = request.form.get("file_name") + + # Creating the image file + process = create_new_image(file_name, file_type, size) + if process["status"] == True: + flash(f"Drive image file {file_name}.{file_type} created") + flash(process["msg"]) + else: + flash(f"Failed to create file {file_name}.{file_type}", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) + + # Creating the drive properties file + prop_file_name = f"{file_name}.{file_type}.{PROPERTIES_SUFFIX}" + properties = {"vendor": vendor, "product": product, \ + "revision": revision, "block_size": block_size} + process = write_drive_properties(prop_file_name, properties) + if process["status"] == True: + flash(f"Drive properties file {prop_file_name} created") + return redirect(url_for("index")) + else: + flash(f"Failed to create drive properties file {prop_file_name}", "error") + return redirect(url_for("index")) + + +@app.route("/drive/cdrom", methods=["POST"]) +def drive_cdrom(): + vendor = request.form.get("vendor") + product = request.form.get("product") + revision = request.form.get("revision") + block_size = request.form.get("block_size") + file_name = request.form.get("file_name") + + # Creating the drive properties file + file_name = f"{file_name}.{PROPERTIES_SUFFIX}" + properties = { + "vendor": vendor, + "product": product, + "revision": revision, + "block_size": block_size, + } + process = write_drive_properties(file_name, properties) + if process["status"] == True: + flash(f"Drive properties file {file_name} created") + return redirect(url_for("index")) + else: + flash(f"Failed to create drive properties file {file_name}", "error") + return redirect(url_for("index")) + + @app.route("/config/save", methods=["POST"]) def config_save(): file_name = request.form.get("name") or "default" - file_name = f"{base_dir}{file_name}.csv" - import csv + file_name = f"{file_name}.json" - with open(file_name, "w") as csv_file: - writer = csv.writer(csv_file) - for device in list_devices(): - if device["type"] is not "-": - writer.writerow(device.values()) - flash(f"Saved config to {file_name}!") - return redirect(url_for("index")) + process = write_config(file_name) + if process["status"] == True: + flash(f"Saved config to {file_name}!") + flash(process["msg"]) + return redirect(url_for("index")) + else: + flash(f"Failed to save config to {file_name}!", "error") + flash(process['msg'], "error") + return redirect(url_for("index")) @app.route("/config/load", methods=["POST"]) def config_load(): - file_name = request.form.get("name") or "default.csv" - file_name = f"{base_dir}{file_name}" - detach_all() - import csv + file_name = request.form.get("name") - with open(file_name) as csv_file: - config_reader = csv.reader(csv_file) - for row in config_reader: - image_name = row[3].replace("(WRITEPROTECT)", "") - attach_image(row[0], image_name, row[2]) - flash(f"Loaded config from {file_name}!") - return redirect(url_for("index")) + if "load" in request.form: + process = read_config(file_name) + if process["status"] == True: + flash(f"Loaded config from {file_name}!") + flash(process["msg"]) + return redirect(url_for("index")) + else: + flash(f"Failed to load {file_name}!", "error") + flash(process['msg'], "error") + return redirect(url_for("index")) + elif "delete" in request.form: + process = delete_file(cfg_dir + file_name) + if process["status"] == True: + flash(f"Deleted config {file_name}!") + return redirect(url_for("index")) + else: + flash(f"Failed to delete {file_name}!", "error") + flash(process['msg'], "error") + return redirect(url_for("index")) -@app.route("/logs") -def logs(): - import subprocess +@app.route("/logs/show", methods=["POST"]) +def show_logs(): + lines = request.form.get("lines") or "200" + scope = request.form.get("scope") or "default" - lines = request.args.get("lines") or "100" - process = subprocess.run(["journalctl", "-n", lines], capture_output=True) + from subprocess import run + if scope != "default": + process = run(["journalctl", "-n", lines, "-u", scope], capture_output=True) + else: + process = run(["journalctl", "-n", lines], capture_output=True) if process.returncode == 0: headers = {"content-type": "text/plain"} - return process.stdout.decode("utf-8"), 200, headers + return process.stdout.decode("utf-8"), int(lines), headers else: flash("Failed to get logs") flash(process.stdout.decode("utf-8"), "stdout") @@ -94,205 +298,388 @@ def logs(): return redirect(url_for("index")) +@app.route("/logs/level", methods=["POST"]) +def log_level(): + level = request.form.get("level") or "info" + + process = set_log_level(level) + if process["status"] == True: + flash(f"Log level set to {level}!") + return redirect(url_for("index")) + else: + flash(f"Failed to set log level to {level}!", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) + + @app.route("/daynaport/attach", methods=["POST"]) def daynaport_attach(): scsi_id = request.form.get("scsi_id") - process = attach_daynaport(scsi_id) - if process.returncode == 0: - flash(f"Attached DaynaPORT to SCSI id {scsi_id}!") + interface = request.form.get("if") + ip = request.form.get("ip") + mask = request.form.get("mask") + + kwargs = {"device_type": "SCDP"} + if interface != "": + arg = interface + if "" not in (ip, mask): + arg += (":" + ip + "/" + mask) + kwargs["interfaces"] = arg + + process = attach_image(scsi_id, **kwargs) + if process["status"] == True: + flash(f"Attached DaynaPORT to SCSI ID {scsi_id}!") return redirect(url_for("index")) else: - flash(f"Failed to attach DaynaPORT to SCSI id {scsi_id}!", "error") - flash(process.stdout.decode("utf-8"), "stdout") - flash(process.stderr.decode("utf-8"), "stderr") - return redirect(url_for("index")) - - -@app.route("/daynaport/setup", methods=["POST"]) -def daynaport_setup(): - # Future use for wifi - interface = request.form.get("interface") or "eth0" - process = daynaport_setup_bridge(interface) - if process.returncode == 0: - flash(f"Configured DaynaPORT bridge on {interface}!") - return redirect(url_for("index")) - else: - flash(f"Failed to configure DaynaPORT bridge on {interface}!", "error") - flash(process.stdout.decode("utf-8"), "stdout") - flash(process.stderr.decode("utf-8"), "stderr") + flash(f"Failed to attach DaynaPORT to SCSI ID {scsi_id}!", "error") + flash(process["msg"], "error") return redirect(url_for("index")) @app.route("/scsi/attach", methods=["POST"]) def attach(): file_name = request.form.get("file_name") + file_size = request.form.get("file_size") scsi_id = request.form.get("scsi_id") + un = request.form.get("un") + device_type = request.form.get("type") - # Validate image type by suffix - if file_name.lower().endswith(".iso") or file_name.lower().endswith("iso"): - image_type = "cd" - elif file_name.lower().endswith(".hda"): - image_type = "hd" - else: - flash("Unknown file type. Valid files are .iso, .hda, .cdr", "error") - return redirect(url_for("index")) + kwargs = {"unit": int(un), "image": file_name} - process = attach_image(scsi_id, file_name, image_type) - if process.returncode == 0: - flash(f"Attached {file_name} to SCSI id {scsi_id}!") + # The most common block size is 512 bytes + expected_block_size = 512 + + if device_type != "": + kwargs["device_type"] = device_type + if device_type == "SCCD": + expected_block_size = 2048 + elif device_type == "SAHD": + expected_block_size = 256 + + # Attempt to load the device properties file: + # same base path but with PROPERTIES_SUFFIX appended + from pathlib import Path + drive_properties = f"{cfg_dir}{file_name}.{PROPERTIES_SUFFIX}" + if Path(drive_properties).is_file(): + process = read_drive_properties(drive_properties) + if process["status"] == False: + flash(f"Failed to load the device properties \ + file {file_name_base}.{PROPERTIES_SUFFIX}", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) + conf = process["conf"] + kwargs["vendor"] = conf["vendor"] + kwargs["product"] = conf["product"] + kwargs["revision"] = conf["revision"] + kwargs["block_size"] = conf["block_size"] + expected_block_size = conf["block_size"] + + process = attach_image(scsi_id, **kwargs) + if process["status"] == True: + flash(f"Attached {file_name} to SCSI ID {scsi_id} LUN {un}!") + if int(file_size) % int(expected_block_size): + flash(f"The image file size {file_size} bytes is not a multiple of \ + {expected_block_size} and RaSCSI will ignore the trailing data. \ + The image may be corrupted so proceed with caution.", "error") return redirect(url_for("index")) else: - flash(f"Failed to attach {file_name} to SCSI id {scsi_id}!", "error") - flash(process.stdout.decode("utf-8"), "stdout") - flash(process.stderr.decode("utf-8"), "stderr") + flash(f"Failed to attach {file_name} to SCSI ID {scsi_id} LUN {un}!", "error") + flash(process["msg"], "error") return redirect(url_for("index")) @app.route("/scsi/detach_all", methods=["POST"]) def detach_all_devices(): - detach_all() - flash("Detached all SCSI devices!") - return redirect(url_for("index")) + process = detach_all() + if process["status"] == True: + flash("Detached all SCSI devices!") + return redirect(url_for("index")) + else: + flash("Failed to detach all SCSI devices!", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) @app.route("/scsi/detach", methods=["POST"]) def detach(): scsi_id = request.form.get("scsi_id") - process = detach_by_id(scsi_id) - if process.returncode == 0: - flash("Detached SCSI id " + scsi_id + "!") + un = request.form.get("un") + process = detach_by_id(scsi_id, un) + if process["status"] == True: + flash(f"Detached SCSI ID {scsi_id} LUN {un}!") return redirect(url_for("index")) else: - flash("Failed to detach SCSI id " + scsi_id + "!", "error") - flash(process.stdout, "stdout") - flash(process.stderr, "stderr") + flash(f"Failed to detach SCSI ID {scsi_id} LUN {un}!", "error") + flash(process["msg"], "error") return redirect(url_for("index")) @app.route("/scsi/eject", methods=["POST"]) def eject(): scsi_id = request.form.get("scsi_id") - process = eject_by_id(scsi_id) - if process.returncode == 0: - flash("Ejected scsi id " + scsi_id + "!") + un = request.form.get("un") + + process = eject_by_id(scsi_id, un) + if process["status"] == True: + flash(f"Ejected SCSI ID {scsi_id} LUN {un}!") return redirect(url_for("index")) else: - flash("Failed to eject SCSI id " + scsi_id + "!", "error") - flash(process.stdout, "stdout") - flash(process.stderr, "stderr") + flash(f"Failed to eject SCSI ID {scsi_id} LUN {un}!", "error") + flash(process["msg"], "error") return redirect(url_for("index")) +@app.route("/scsi/info", methods=["POST"]) +def device_info(): + scsi_id = request.form.get("scsi_id") + un = request.form.get("un") + + devices = list_devices(scsi_id, un) + + # First check if any device at all was returned + if devices["status"] == False: + flash(f"No device attached to SCSI ID {scsi_id} LUN {un}!", "error") + return redirect(url_for("index")) + # Looking at the first dict in list to get + # the one and only device that should have been returned + device = devices["device_list"][0] + if str(device["id"]) == scsi_id: + flash("=== DEVICE INFO ===") + flash(f"SCSI ID: {device['id']}") + flash(f"LUN: {device['un']}") + flash(f"Type: {device['device_type']}") + flash(f"Status: {device['status']}") + flash(f"File: {device['image']}") + flash(f"Parameters: {device['params']}") + flash(f"Vendor: {device['vendor']}") + flash(f"Product: {device['product']}") + flash(f"Revision: {device['revision']}") + flash(f"Block Size: {device['block_size']} bytes") + flash(f"Image Size: {device['size']} bytes") + return redirect(url_for("index")) + else: + flash(f"Failed to get device info for SCSI ID {scsi_id} LUN {un}!", "error") + return redirect(url_for("index")) @app.route("/pi/reboot", methods=["POST"]) def restart(): + detach_all() + flash("Safely detached all devices.") + flash("Rebooting the Pi momentarily...") reboot_pi() - flash("Restarting...") return redirect(url_for("index")) @app.route("/rascsi/restart", methods=["POST"]) def rascsi_restart(): - rascsi_service("restart") + detach_all() + flash("Safely detached all devices.") flash("Restarting RaSCSI Service...") + systemd_service("rascsi.service", "restart") + systemd_service("monitor_rascsi.service", "restart") return redirect(url_for("index")) @app.route("/pi/shutdown", methods=["POST"]) def shutdown(): + detach_all() + flash("Safely detached all devices.") + flash("Shutting down the Pi momentarily...") shutdown_pi() - flash("Shutting down...") return redirect(url_for("index")) @app.route("/files/download_to_iso", methods=["POST"]) -def download_file(): +def download_to_iso(): scsi_id = request.form.get("scsi_id") + url = request.form.get("url") process = download_file_to_iso(scsi_id, url) - if process.returncode == 0: - flash("File Downloaded") + if process["status"] == True: + flash(f"Created CD-ROM image {process['file_name']}") + flash(process["msg"]) + else: + flash(f"Failed to create CD-ROM image from {url}", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) + + process_attach = attach_image(scsi_id, device_type="SCCD", image=process["file_name"]) + if process_attach["status"] == True: + flash(f"Attached to SCSI ID {scsi_id}") return redirect(url_for("index")) else: - flash("Failed to download file", "error") - flash(process.stdout, "stdout") - flash(process.stderr, "stderr") + flash(f"Failed to attach to SCSI ID {scsi_id}. Try attaching it manually.", "error") + flash(process_attach["msg"], "error") return redirect(url_for("index")) -@app.route("/files/download_image", methods=["POST"]) +@app.route("/files/download_to_images", methods=["POST"]) def download_img(): url = request.form.get("url") - # TODO: error handling - download_image(url) - flash("File Downloaded") - return redirect(url_for("index")) + server_info = get_server_info() + process = download_to_dir(url, server_info["image_dir"]) + if process["status"] == True: + flash(f"File Downloaded from {url} to {server_info['image_dir']}") + return redirect(url_for("index")) + else: + flash(f"Failed to download file {url}", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) + + +@app.route("/files/download_to_afp", methods=["POST"]) +def download_afp(): + url = request.form.get("url") + process = download_to_dir(url, afp_dir) + if process["status"] == True: + flash(f"File Downloaded from {url} to {afp_dir}") + return redirect(url_for("index")) + else: + flash(f"Failed to download file {url}", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) @app.route("/files/upload", methods=["POST"]) def upload_file(): - if "file" not in request.files: - flash("No file part", "error") - return redirect(url_for("index")) + from werkzeug.utils import secure_filename + from os import path + import pydrop + + log = logging.getLogger("pydrop") file = request.files["file"] - if file: - filename = secure_filename(file.filename) - file.save(os.path.join(app.config["UPLOAD_FOLDER"], filename)) - return redirect(url_for("index", filename=filename)) + filename = secure_filename(file.filename) + + server_info = get_server_info() + + save_path = path.join(server_info["image_dir"], filename) + current_chunk = int(request.form['dzchunkindex']) + + # Makes sure not to overwrite an existing file, + # but continues writing to a file transfer in progress + if path.exists(save_path) and current_chunk == 0: + return make_response((f"The file {file.filename} already exists!", 400)) + + try: + with open(save_path, "ab") as f: + f.seek(int(request.form["dzchunkbyteoffset"])) + f.write(file.stream.read()) + except OSError: + log.exception("Could not write to file") + return make_response(("Unable to write the file to disk!", 500)) + + total_chunks = int(request.form["dztotalchunkcount"]) + + if current_chunk + 1 == total_chunks: + # Validate the resulting file size after writing the last chunk + if path.getsize(save_path) != int(request.form["dztotalfilesize"]): + log.error(f"Finished transferring {file.filename}, " + f"but it has a size mismatch with the original file." + f"Got {path.getsize(save_path)} but we " + f"expected {request.form['dztotalfilesize']}.") + return make_response(("Transferred file corrupted!", 500)) + else: + log.info(f"File {file.filename} has been uploaded successfully") + else: + log.debug(f"Chunk {current_chunk + 1} of {total_chunks} " + f"for file {file.filename} completed.") + + return make_response(("File upload successful!", 200)) @app.route("/files/create", methods=["POST"]) def create_file(): file_name = request.form.get("file_name") - size = request.form.get("size") - type = request.form.get("type") + size = (int(request.form.get("size")) * 1024 * 1024) + file_type = request.form.get("type") - process = create_new_image(file_name, type, size) - if process.returncode == 0: - flash("Drive created") + from werkzeug.utils import secure_filename + file_name = secure_filename(file_name) + + process = create_new_image(file_name, file_type, size) + if process["status"] == True: + flash(f"Drive image created as {file_name}.{file_type}") + flash(process["msg"]) return redirect(url_for("index")) else: - flash("Failed to create file", "error") - flash(process.stdout, "stdout") - flash(process.stderr, "stderr") + flash(f"Failed to create file {file_name}.{file_type}", "error") + flash(process["msg"], "error") return redirect(url_for("index")) @app.route("/files/download", methods=["POST"]) def download(): image = request.form.get("image") - return send_file(base_dir + image, as_attachment=True) + server_info = get_server_info() + return send_file(f"{server_info['image_dir']}/{image}", as_attachment=True) @app.route("/files/delete", methods=["POST"]) def delete(): - image = request.form.get("image") - if delete_image(image): - flash("File " + image + " deleted") - return redirect(url_for("index")) + file_name = request.form.get("image") + + process = delete_image(file_name) + if process["status"] == True: + flash(f"File {file_name} deleted!") + flash(process["msg"]) else: - flash("Failed to Delete " + image, "error") + flash(f"Failed to delete file {file_name}!", "error") + flash(process["msg"], "error") return redirect(url_for("index")) + # Delete the drive properties file, if it exists + from pathlib import Path + prop_file_path = f"{cfg_dir}{file_name}.{PROPERTIES_SUFFIX}" + if Path(prop_file_path).is_file(): + process = delete_file(prop_file_path) + if process["status"] == True: + flash(f"File {prop_file_path} deleted!") + return redirect(url_for("index")) + else: + flash(f"Failed to delete file {prop_file_path}!", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) + + return redirect(url_for("index")) + @app.route("/files/unzip", methods=["POST"]) def unzip(): image = request.form.get("image") + member = request.form.get("member") or False - if unzip_file(image): - flash("Unzipped file " + image) + process = unzip_file(image, member) + if process["status"]: + if len(process["msg"]) < 1: + flash("Aborted unzip: File(s) with the same name already exists.", "error") + return redirect(url_for("index")) + flash("Unzipped the following files:") + for m in process["msg"]: + flash(m) return redirect(url_for("index")) else: flash("Failed to unzip " + image, "error") + flash(process["msg"], "error") return redirect(url_for("index")) if __name__ == "__main__": app.secret_key = "rascsi_is_awesome_insecure_secret_key" app.config["SESSION_TYPE"] = "filesystem" - app.config["UPLOAD_FOLDER"] = base_dir - os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True) + + server_info = get_server_info() + app.config["UPLOAD_FOLDER"] = server_info["image_dir"] + + from os import makedirs + makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True) app.config["MAX_CONTENT_LENGTH"] = MAX_FILE_SIZE - from waitress import serve + # Load the default configuration file, if found + from pathlib import Path + default_config_path = Path(cfg_dir + DEFAULT_CONFIG) + if default_config_path.is_file(): + read_config(DEFAULT_CONFIG) + + import bjoern + print("Serving rascsi-web...") + bjoern.run(app, "0.0.0.0", 8080) - serve(app, host="0.0.0.0", port=8080) diff --git a/src/x68k/RASCTL/Makefile b/src/x68k/RASCTL/Makefile new file mode 100644 index 00000000..b10bd182 --- /dev/null +++ b/src/x68k/RASCTL/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for RASCTL.X +# + +CC = gcc -O2 -Wall +LK = hlk -l -x + +RASCTL.X : RASCTL.o + $(LK) -o $@ $^ libc.a libiocs.a libscsi.a libgnu.a + +RASCTL.o : RASCTL.C + $(CC) -c -o $@ $^ + \ No newline at end of file diff --git a/src/x68k/RASCTL/RASCTL.C b/src/x68k/RASCTL/RASCTL.C new file mode 100644 index 00000000..c711683f --- /dev/null +++ b/src/x68k/RASCTL/RASCTL.C @@ -0,0 +1,433 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// Powered by XM6 TypeG Technology. +// +// Copyright (C) 2016-2021 GIMONS(Twitter:@kugimoto0715) +// +// [ Sending commands ] +// +//--------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- +// +// Primitive definitions +// +//--------------------------------------------------------------------------- +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +typedef unsigned long long UL64; +typedef int BOOL; +typedef char TCHAR; +typedef char *LPTSTR; +typedef const char *LPCTSTR; +typedef const char *LPCSTR; + +#if !defined(FALSE) +#define FALSE 0 +#endif + +#if !defined(TRUE) +#define TRUE 1 +#endif + +//--------------------------------------------------------------------------- +// +// Constant definitions +// +//--------------------------------------------------------------------------- +#define ENOTCONN 107 + +//--------------------------------------------------------------------------- +// +// Struct definitions +// +//--------------------------------------------------------------------------- +typedef struct +{ + char DeviceType; + char RMB; + char ANSI_Ver; + char RDF; + char AddLen; + char RESV0; + char RESV1; + char OptFunc; + char VendorID[8]; + char ProductID[16]; + char FirmRev[4]; +} INQUIRY_T; + +typedef struct +{ + INQUIRY_T info; + char options[8]; +} INQUIRYOPT_T; + +//--------------------------------------------------------------------------- +// +// Global variables +// +//--------------------------------------------------------------------------- +int scsiid = -1; + +//--------------------------------------------------------------------------- +// +// Search for bridge device +// +//--------------------------------------------------------------------------- +BOOL SearchRaSCSI() +{ + int i; + INQUIRYOPT_T inq; + + for (i = 7; i >= 0; i--) { + // Search for bridge device + if (S_INQUIRY(sizeof(INQUIRY_T) , i, (struct INQUIRY*)&inq) < 0) { + continue; + } + + if (memcmp(&(inq.info.ProductID), "RASCSI BRIDGE", 13) != 0) { + continue; + } + + // Check if option functions are initialized + if (S_INQUIRY(sizeof(INQUIRYOPT_T) , i, (struct INQUIRY*)&inq) < 0) { + continue; + } + + if (inq.options[0] != '1') { + continue; + } + + // Confirm SCSI ID + scsiid = i; + return TRUE; + } + + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// Get messages +// +//--------------------------------------------------------------------------- +BOOL ScsiGetMessage(BYTE *buf) +{ + unsigned char cmd[10]; + unsigned char sts; + unsigned char msg; + + if (S_SELECT(scsiid) != 0) { + return FALSE; + } + + cmd[0] = 0x28; + cmd[1] = 0; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = 0; + cmd[5] = 0; + cmd[6] = 0; + cmd[7] = 0x04; + cmd[8] = 0; + cmd[9] = 0; + if (S_CMDOUT(10, cmd) != 0) { + return FALSE; + } + + if (S_DATAIN_P(1024, buf) != 0) { + return FALSE; + } + + S_STSIN(&sts); + S_MSGIN(&msg); + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Send messages +// +//--------------------------------------------------------------------------- +BOOL ScsiSendMessage(BYTE *buf) +{ + unsigned char cmd[10]; + unsigned char sts; + unsigned char msg; + + if (S_SELECT(scsiid) != 0) { + return FALSE; + } + + cmd[0] = 0x2a; + cmd[1] = 0; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = 0; + cmd[5] = 0; + cmd[6] = 0; + cmd[7] = 0x04; + cmd[8] = 0; + cmd[9] = 0; + if (S_CMDOUT(10, cmd) != 0) { + return FALSE; + } + + S_DATAOUT_P(1024, buf); + + S_STSIN(&sts); + S_MSGIN(&msg); + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Send commands +// +//--------------------------------------------------------------------------- +BOOL SendCommand(char *buf) +{ + BYTE message[1024]; + + memset(message, 0x00, 1024); + memcpy(message, buf, strlen(buf)); + + if (ScsiSendMessage(message)) { + memset(message, 0x00, 1024); + if (ScsiGetMessage(message)) { + printf("%s", (char*)message); + return TRUE; + } + } + + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// Main process +// +//--------------------------------------------------------------------------- +void main(int argc, char* argv[]) +{ + int opt; + int id; + int un; + int cmd; + int type; + char *file; + int len; + char *ext; + char buf[BUFSIZ]; + + id = -1; + un = 0; + cmd = -1; + type = -1; + file = NULL; + + // Display help + if (argc < 2) { + fprintf(stderr, "SCSI Target Emulator RaSCSI Controller\n"); + fprintf(stderr, + "Usage: %s -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE]\n", + argv[0]); + fprintf(stderr, " where ID := {0|1|2|3|4|5|6|7}\n"); + fprintf(stderr, " UNIT := {0|1} default setting is 0.\n"); + fprintf(stderr, " CMD := {attach|detach|insert|eject|protect}\n"); + fprintf(stderr, " TYPE := {hd|mo|cd|bridge}\n"); + fprintf(stderr, " FILE := image file path\n"); + fprintf(stderr, " CMD is 'attach' or 'insert' and FILE parameter is required.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: %s -l\n\n", argv[0]); + fprintf(stderr, " Print device list.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: %s --stop\n\n", argv[0]); + fprintf(stderr, " Stop rascsi prosess.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: %s --shutdown\n\n", argv[0]); + fprintf(stderr, " Shutdown raspberry pi.\n"); + exit(0); + } + + // Look for bridge device + if (!SearchRaSCSI()) { + fprintf(stderr, "Error: bridge not found\n"); + exit(ENOTCONN); + } + + // Argument parsing + opterr = 0; + while ((opt = getopt(argc, argv, "i:u:c:t:f:l-:")) != -1) { + switch (opt) { + case 'i': + id = optarg[0] - '0'; + break; + + case 'u': + un = optarg[0] - '0'; + break; + + case 'c': + switch (optarg[0]) { + case 'a': // ATTACH + case 'A': + cmd = 0; + break; + case 'd': // DETACH + case 'D': + cmd = 1; + break; + case 'i': // INSERT + case 'I': + cmd = 2; + break; + case 'e': // EJECT + case 'E': + cmd = 3; + break; + case 'p': // PROTECT + case 'P': + cmd = 4; + break; + } + break; + + case 't': + switch (optarg[0]) { + case 's': // HD(SASI) + case 'S': + case 'h': // HD(SCSI) + case 'H': + type = 0; + break; + case 'm': // MO + case 'M': + type = 2; + break; + case 'c': // CD + case 'C': + type = 3; + break; + case 'b': // BRIDGE + case 'B': + type = 4; + break; + } + break; + + case 'f': + file = optarg; + break; + + case 'l': + sprintf(buf, "list\n"); + SendCommand(buf); + exit(0); + + case '-': + if (strcmp(optarg, "shutdown") == 0) { + sprintf(buf, "shutdown\n"); + SendCommand(buf); + exit(0); + } else if (strcmp(optarg, "stop") == 0) { + sprintf(buf, "stop\n"); + SendCommand(buf); + exit(0); + } + break; + } + } + + // ID check + if (id < 0 || id > 7) { + fprintf(stderr, "Error : Invalid ID\n"); + exit(EINVAL); + } + + // Unit check + if (un < 0 || un > 1) { + fprintf(stderr, "Error : Invalid UNIT\n"); + exit(EINVAL); + } + + // Command check + if (cmd < 0) { + cmd = 0; // The default is 'attach' + } + + // Type check + if (cmd == 0 && type < 0) { + + // Attempt type detection from extension + len = file ? strlen(file) : 0; + if (len > 4 && file[len - 4] == '.') { + ext = &file[len - 3]; + if (strcasecmp(ext, "hdf") == 0 || + strcasecmp(ext, "hds") == 0 || + strcasecmp(ext, "hdn") == 0 || + strcasecmp(ext, "hdi") == 0 || + strcasecmp(ext, "nhd") == 0 || + strcasecmp(ext, "hda") == 0) { + // HD(SASI/SCSI) + type = 0; + } else if (strcasecmp(ext, "mos") == 0) { + // MO + type = 2; + } else if (strcasecmp(ext, "iso") == 0) { + // CD + type = 3; + } + } + + if (type < 0) { + fprintf(stderr, "Error : Invalid type\n"); + exit(EINVAL); + } + } + + // File check (the command is 'attach' with type HD) + if (cmd == 0 && type >= 0 && type <= 1) { + if (!file) { + fprintf(stderr, "Error : Invalid file path\n"); + exit(EINVAL); + } + } + + // File check (the command is 'insert') + if (cmd == 2) { + if (!file) { + fprintf(stderr, "Error : Invalid file path\n"); + exit(EINVAL); + } + } + + // Useless types are set to 0 + if (type < 0) { + type = 0; + } + + // Create command to send + sprintf(buf, "%d %d %d %d %s\n", id, un, cmd, type, file ? file : "-"); + if (!SendCommand(buf)) { + exit(ENOTCONN); + } + + // Quit + exit(0); +} diff --git a/src/x68k/RASDRV/BRIDGE.C b/src/x68k/RASDRV/BRIDGE.C index 1ecce912..c9c85e71 100644 --- a/src/x68k/RASDRV/BRIDGE.C +++ b/src/x68k/RASDRV/BRIDGE.C @@ -3,9 +3,9 @@ // SCSI Target Emulator RaSCSI (*^..^*) // for Raspberry Pi // -// Powered by XM6 TypeG Technorogy. +// Powered by XM6 TypeG Technology. // Copyright (C) 2016-2017 GIMONS -// [ zXgt@CVXe ubWhCo ] +// [ Host Filesystem Bridge Driver ] // //--------------------------------------------------------------------------- @@ -18,16 +18,16 @@ //--------------------------------------------------------------------------- // -// ϐ錾 +// Variable definitions // //--------------------------------------------------------------------------- -volatile BYTE *request; // NGXgwb_AhX -DWORD command; // R}hԍ -DWORD unit; // jbgԍ +volatile BYTE *request; // Request header address +DWORD command; // Command number +DWORD unit; // Unit number //=========================================================================== // -/// t@CVXe(SCSIAg) +/// File system (SCSI integration) // //=========================================================================== typedef struct @@ -49,7 +49,7 @@ int scsiid; // SCSI ID //--------------------------------------------------------------------------- // -// +// Initialize // //--------------------------------------------------------------------------- BOOL SCSI_Init(void) @@ -57,7 +57,7 @@ BOOL SCSI_Init(void) int i; INQUIRY_T inq; - // SCSI ID + // SCSI ID not set scsiid = -1; for (i = 0; i <= 7; i++) { @@ -69,7 +69,7 @@ BOOL SCSI_Init(void) continue; } - // SCSI IDm + // SCSI ID set scsiid = i; return TRUE; } @@ -79,7 +79,7 @@ BOOL SCSI_Init(void) //--------------------------------------------------------------------------- // -// R}hM +// Send commands // //--------------------------------------------------------------------------- int SCSI_SendCmd(BYTE *buf, int len) @@ -103,7 +103,7 @@ int SCSI_SendCmd(BYTE *buf, int len) cmdbuf[8] = (BYTE)len; cmdbuf[9] = 0; - // ZNg^CAEg鎞ɂ͍Ďs + // Retry when select times out if (S_SELECT(scsiid) != 0) { S_RESET(); if (S_SELECT(scsiid) != 0) { @@ -138,7 +138,7 @@ int SCSI_SendCmd(BYTE *buf, int len) cmdbuf[8] = 4; cmdbuf[9] = 0; - // ZNg^CAEg鎞ɂ͍Ďs + // Retry when select times out if (S_SELECT(scsiid) != 0) { S_RESET(); if (S_SELECT(scsiid) != 0) { @@ -168,7 +168,7 @@ int SCSI_SendCmd(BYTE *buf, int len) //--------------------------------------------------------------------------- // -// R}hĂяo +// Call commands // //--------------------------------------------------------------------------- int SCSI_CalCmd(BYTE *buf, int len, BYTE *outbuf, int outlen) @@ -192,7 +192,7 @@ int SCSI_CalCmd(BYTE *buf, int len, BYTE *outbuf, int outlen) cmdbuf[8] = (BYTE)len; cmdbuf[9] = 0; - // ZNg^CAEg鎞ɂ͍Ďs + // Retry when select times out if (S_SELECT(scsiid) != 0) { S_RESET(); if (S_SELECT(scsiid) != 0) { @@ -227,7 +227,7 @@ int SCSI_CalCmd(BYTE *buf, int len, BYTE *outbuf, int outlen) cmdbuf[8] = 4; cmdbuf[9] = 0; - // ZNg^CAEg鎞ɂ͍Ďs + // Retry when select times out if (S_SELECT(scsiid) != 0) { S_RESET(); if (S_SELECT(scsiid) != 0) { @@ -251,7 +251,7 @@ int SCSI_CalCmd(BYTE *buf, int len, BYTE *outbuf, int outlen) return ret; } - // G[Ȃԋpf[^̎M~߂ + // Stop receiving return data on error ret = *(int*)retbuf; if (ret < 0) { return ret; @@ -268,7 +268,7 @@ int SCSI_CalCmd(BYTE *buf, int len, BYTE *outbuf, int outlen) cmdbuf[8] = (BYTE)outlen; cmdbuf[9] = 1; - // ZNg^CAEg鎞ɂ͍Ďs + // Retry when select times out if (S_SELECT(scsiid) != 0) { S_RESET(); if (S_SELECT(scsiid) != 0) { @@ -298,7 +298,7 @@ int SCSI_CalCmd(BYTE *buf, int len, BYTE *outbuf, int outlen) //--------------------------------------------------------------------------- // -// IvVf[^擾 +// Get option data // //--------------------------------------------------------------------------- BOOL SCSI_ReadOpt(BYTE *buf, int len) @@ -318,7 +318,7 @@ BOOL SCSI_ReadOpt(BYTE *buf, int len) cmdbuf[8] = (BYTE)len; cmdbuf[9] = 2; - // ZNg^CAEg鎞ɂ͍Ďs + // Retry when select times out if (S_SELECT(scsiid) != 0) { S_RESET(); if (S_SELECT(scsiid) != 0) { @@ -347,7 +347,7 @@ BOOL SCSI_ReadOpt(BYTE *buf, int len) //--------------------------------------------------------------------------- // -// IvVf[^ +// Write option data // //--------------------------------------------------------------------------- BOOL SCSI_WriteOpt(BYTE *buf, int len) @@ -367,7 +367,7 @@ BOOL SCSI_WriteOpt(BYTE *buf, int len) cmdbuf[8] = (BYTE)len; cmdbuf[9] = 1; - // ZNg^CAEg鎞ɂ͍Ďs + // Retry when select times out if (S_SELECT(scsiid) != 0) { S_RESET(); if (S_SELECT(scsiid) != 0) { @@ -396,13 +396,13 @@ BOOL SCSI_WriteOpt(BYTE *buf, int len) //=========================================================================== // -/// t@CVXe +/// File System // //=========================================================================== //--------------------------------------------------------------------------- // -// $40 - foCXN +// $40 - Device startup // //--------------------------------------------------------------------------- DWORD FS_InitDevice(const argument_t* pArgument) @@ -412,7 +412,7 @@ DWORD FS_InitDevice(const argument_t* pArgument) //--------------------------------------------------------------------------- // -// $41 - fBNg`FbN +// $41 - Directory check // //--------------------------------------------------------------------------- int FS_CheckDir(const namests_t* pNamests) @@ -434,7 +434,7 @@ int FS_CheckDir(const namests_t* pNamests) //--------------------------------------------------------------------------- // -// $42 - fBNg쐬 +// $42 - Create directory // //--------------------------------------------------------------------------- int FS_MakeDir(const namests_t* pNamests) @@ -456,7 +456,7 @@ int FS_MakeDir(const namests_t* pNamests) //--------------------------------------------------------------------------- // -// $43 - fBNg폜 +// $43 - Delete directory // //--------------------------------------------------------------------------- int FS_RemoveDir(const namests_t* pNamests) @@ -478,7 +478,7 @@ int FS_RemoveDir(const namests_t* pNamests) //--------------------------------------------------------------------------- // -// $44 - t@CύX +// $44 - Change file name // //--------------------------------------------------------------------------- int FS_Rename(const namests_t* pNamests, const namests_t* pNamestsNew) @@ -503,7 +503,7 @@ int FS_Rename(const namests_t* pNamests, const namests_t* pNamestsNew) //--------------------------------------------------------------------------- // -// $45 - t@C폜 +// $45 - Delete file // //--------------------------------------------------------------------------- int FS_Delete(const namests_t* pNamests) @@ -525,7 +525,7 @@ int FS_Delete(const namests_t* pNamests) //--------------------------------------------------------------------------- // -// $46 - t@C擾/ݒ +// $46 - Get/set file attribute // //--------------------------------------------------------------------------- int FS_Attribute(const namests_t* pNamests, DWORD nHumanAttribute) @@ -551,7 +551,7 @@ int FS_Attribute(const namests_t* pNamests, DWORD nHumanAttribute) //--------------------------------------------------------------------------- // -// $47 - t@C +// $47 - File search // //--------------------------------------------------------------------------- int FS_Files(DWORD nKey, @@ -581,7 +581,7 @@ int FS_Files(DWORD nKey, //--------------------------------------------------------------------------- // -// $48 - t@C +// $48 - Search next file // //--------------------------------------------------------------------------- int FS_NFiles(DWORD nKey, files_t* info) @@ -607,7 +607,7 @@ int FS_NFiles(DWORD nKey, files_t* info) //--------------------------------------------------------------------------- // -// $49 - t@C쐬 +// $49 - Create new file // //--------------------------------------------------------------------------- int FS_Create(DWORD nKey, @@ -646,7 +646,7 @@ int FS_Create(DWORD nKey, //--------------------------------------------------------------------------- // -// $4A - t@CI[v +// $4A - File open // //--------------------------------------------------------------------------- int FS_Open(DWORD nKey, @@ -676,7 +676,7 @@ int FS_Open(DWORD nKey, //--------------------------------------------------------------------------- // -// $4B - t@CN[Y +// $4B - File close // //--------------------------------------------------------------------------- int FS_Close(DWORD nKey, fcb_t* pFcb) @@ -702,7 +702,7 @@ int FS_Close(DWORD nKey, fcb_t* pFcb) //--------------------------------------------------------------------------- // -// $4C - t@Cǂݍ +// $4C - Read file // //--------------------------------------------------------------------------- int FS_Read(DWORD nKey, fcb_t* pFcb, BYTE* pAddress, DWORD nSize) @@ -735,7 +735,7 @@ int FS_Read(DWORD nKey, fcb_t* pFcb, BYTE* pAddress, DWORD nSize) //--------------------------------------------------------------------------- // -// $4D - t@C +// $4D - Write file // //--------------------------------------------------------------------------- int FS_Write(DWORD nKey, fcb_t* pFcb, BYTE* pAddress, DWORD nSize) @@ -767,7 +767,7 @@ int FS_Write(DWORD nKey, fcb_t* pFcb, BYTE* pAddress, DWORD nSize) //--------------------------------------------------------------------------- // -// $4E - t@CV[N +// $4E - File seek // //--------------------------------------------------------------------------- int FS_Seek(DWORD nKey, fcb_t* pFcb, DWORD nMode, int nOffset) @@ -797,7 +797,7 @@ int FS_Seek(DWORD nKey, fcb_t* pFcb, DWORD nMode, int nOffset) //--------------------------------------------------------------------------- // -// $4F - t@C擾/ݒ +// $4F - Get/set file time stamp // //--------------------------------------------------------------------------- DWORD FS_TimeStamp(DWORD nKey, @@ -828,7 +828,7 @@ DWORD FS_TimeStamp(DWORD nKey, //--------------------------------------------------------------------------- // -// $50 - eʎ擾 +// $50 - Get capacity // //--------------------------------------------------------------------------- int FS_GetCapacity(capacity_t* cap) @@ -847,28 +847,28 @@ int FS_GetCapacity(capacity_t* cap) //--------------------------------------------------------------------------- // -// $51 - hCuԌ/ +// $51 - Inspect/control drive status // //--------------------------------------------------------------------------- int FS_CtrlDrive(ctrldrive_t* pCtrlDrive) { #if 1 - // ׂ̂łŎb菈 + // Do tentative processing here due to high load switch (pCtrlDrive->status) { - case 0: // Ԍ - case 9: // Ԍ2 + case 0: // Inspect status + case 9: // Inspect status 2 pCtrlDrive->status = 0x42; return pCtrlDrive->status; - case 1: // CWFNg - case 2: // CWFNg֎~1 () - case 3: // CWFNg1 () - case 4: // fBA}LED_ () - case 5: // fBA}LED () - case 6: // CWFNg֎~2 () - case 7: // CWFNg2 () + case 1: // Eject + case 2: // Eject forbidden 1 (not implemented) + case 3: // Eject allowed 1 (not implemented) + case 4: // Flash LED when media is not inserted (not implemented) + case 5: // Turn off LED when media is not inserted (not implemented) + case 6: // Eject forbidden 2 (not implemented) + case 7: // Eject allowed 2 (not implemented) return 0; - case 8: // CWFNg + case 8: // Eject inspection return 1; } @@ -892,7 +892,7 @@ int FS_CtrlDrive(ctrldrive_t* pCtrlDrive) //--------------------------------------------------------------------------- // -// $52 - DPB擾 +// $52 - Get DPB // //--------------------------------------------------------------------------- int FS_GetDPB(dpb_t* pDpb) @@ -911,7 +911,7 @@ int FS_GetDPB(dpb_t* pDpb) //--------------------------------------------------------------------------- // -// $53 - ZN^ǂݍ +// $53 - Read sector // //--------------------------------------------------------------------------- int FS_DiskRead(BYTE* pBuffer, DWORD nSector, DWORD nSize) @@ -938,7 +938,7 @@ int FS_DiskRead(BYTE* pBuffer, DWORD nSector, DWORD nSize) //--------------------------------------------------------------------------- // -// $54 - ZN^ +// $54 - Write sector // //--------------------------------------------------------------------------- int FS_DiskWrite() @@ -980,13 +980,13 @@ int FS_Ioctrl(DWORD nFunction, ioctrl_t* pIoctrl) //--------------------------------------------------------------------------- // -// $56 - tbV +// $56 - Flush // //--------------------------------------------------------------------------- int FS_Flush() { #if 1 - // T|[g + // Not supported return 0; #else BYTE buf[256]; @@ -1001,13 +1001,13 @@ int FS_Flush() //--------------------------------------------------------------------------- // -// $57 - fBA`FbN +// $57 - Media change check // //--------------------------------------------------------------------------- int FS_CheckMedia() { #if 1 - // ׂ̂Ŏb菈 + // Do tentative processing due to high load return 0; #else BYTE buf[256]; @@ -1022,13 +1022,13 @@ int FS_CheckMedia() //--------------------------------------------------------------------------- // -// $58 - r +// $58 - Lock // //--------------------------------------------------------------------------- int FS_Lock() { #if 1 - // T|[g + // Not supported return 0; #else BYTE buf[256]; @@ -1043,7 +1043,7 @@ int FS_Lock() //=========================================================================== // -// R}hnh +// Command handlers // //=========================================================================== #define GetReqByte(a) (request[a]) @@ -1056,7 +1056,7 @@ int FS_Lock() //--------------------------------------------------------------------------- // -// NAMESTSǂݍ +// Read NAMESTS // //--------------------------------------------------------------------------- void GetNameStsPath(BYTE *addr, namests_t* pNamests) @@ -1066,30 +1066,30 @@ void GetNameStsPath(BYTE *addr, namests_t* pNamests) ASSERT(this); ASSERT(pNamests); - // ChJ[h + // Wildcard data pNamests->wildcard = *addr; - // hCuԍ + // Drive number pNamests->drive = addr[1]; - // pX + // Pathname for (i = 0; i < sizeof(pNamests->path); i++) { pNamests->path[i] = addr[2 + i]; } - // t@C1 + // Filename 1 memset(pNamests->name, 0x20, sizeof(pNamests->name)); - // gq + // File ending memset(pNamests->ext, 0x20, sizeof(pNamests->ext)); - // t@C2 + // Filename 2 memset(pNamests->add, 0, sizeof(pNamests->add)); } //--------------------------------------------------------------------------- // -// NAMESTSǂݍ +// Read NAMESTS // //--------------------------------------------------------------------------- void GetNameSts(BYTE *addr, namests_t* pNamests) @@ -1100,28 +1100,28 @@ void GetNameSts(BYTE *addr, namests_t* pNamests) ASSERT(pNamests); ASSERT(addr <= 0xFFFFFF); - // ChJ[h + // Wildcard data pNamests->wildcard = *addr; - // hCuԍ + // Drive number pNamests->drive = addr[1]; - // pX + // Pathname for (i = 0; i < sizeof(pNamests->path); i++) { pNamests->path[i] = addr[2 + i]; } - // t@C1 + // Filename 1 for (i = 0; i < sizeof(pNamests->name); i++) { pNamests->name[i] = addr[67 + i]; } - // gq + // File ending for (i = 0; i < sizeof(pNamests->ext); i++) { pNamests->ext[i] = addr[75 + i]; } - // t@C2 + // Filename 2 for (i = 0; i < sizeof(pNamests->add); i++) { pNamests->add[i] = addr[78 + i]; } @@ -1129,7 +1129,7 @@ void GetNameSts(BYTE *addr, namests_t* pNamests) //--------------------------------------------------------------------------- // -// FILESǂݍ +// Read FILES // //--------------------------------------------------------------------------- void GetFiles(BYTE *addr, files_t* pFiles) @@ -1138,7 +1138,7 @@ void GetFiles(BYTE *addr, files_t* pFiles) ASSERT(pFiles); ASSERT(addr <= 0xFFFFFF); - // + // Search data pFiles->fatr = *addr; pFiles->sector = *((DWORD*)&addr[2]); pFiles->offset = *((WORD*)&addr[8]);; @@ -1152,7 +1152,7 @@ void GetFiles(BYTE *addr, files_t* pFiles) //--------------------------------------------------------------------------- // -// FILES +// Write FILES // //--------------------------------------------------------------------------- void SetFiles(BYTE *addr, const files_t* pFiles) @@ -1165,13 +1165,13 @@ void SetFiles(BYTE *addr, const files_t* pFiles) *((DWORD*)&addr[2]) = pFiles->sector; *((WORD*)&addr[8]) = pFiles->offset; - // t@C + // File data addr[21] = pFiles->attr; *((WORD*)&addr[22]) = pFiles->time; *((WORD*)&addr[24]) = pFiles->date; *((DWORD*)&addr[26]) = pFiles->size; - // tt@C + // Full filename addr += 30; for (i = 0; i < sizeof(pFiles->full); i++) { *addr = pFiles->full[i]; @@ -1181,7 +1181,7 @@ void SetFiles(BYTE *addr, const files_t* pFiles) //--------------------------------------------------------------------------- // -// FCBǂݍ +// Read FCB // //--------------------------------------------------------------------------- void GetFcb(BYTE *addr, fcb_t* pFcb) @@ -1189,14 +1189,14 @@ void GetFcb(BYTE *addr, fcb_t* pFcb) ASSERT(this); ASSERT(pFcb); - // FCB + // FCB data pFcb->fileptr = *((DWORD*)&addr[6]); pFcb->mode = *((WORD*)&addr[14]); - // + // Attribute pFcb->attr = addr[47]; - // FCB + // FCB data pFcb->time = *((WORD*)&addr[58]); pFcb->date = *((WORD*)&addr[60]); pFcb->size = *((DWORD*)&addr[64]); @@ -1204,7 +1204,7 @@ void GetFcb(BYTE *addr, fcb_t* pFcb) //--------------------------------------------------------------------------- // -// FCB +// Write FCB // //--------------------------------------------------------------------------- void SetFcb(BYTE *addr, const fcb_t* pFcb) @@ -1212,14 +1212,14 @@ void SetFcb(BYTE *addr, const fcb_t* pFcb) ASSERT(this); ASSERT(pFcb); - // FCB + // FCB data *((DWORD*)&addr[6]) = pFcb->fileptr; *((WORD*)&addr[14]) = pFcb->mode; - // + // Attribute addr[47] = pFcb->attr; - // FCB + // FCB data *((WORD*)&addr[58]) = pFcb->time; *((WORD*)&addr[60]) = pFcb->date; *((DWORD*)&addr[64]) = pFcb->size; @@ -1227,7 +1227,7 @@ void SetFcb(BYTE *addr, const fcb_t* pFcb) //--------------------------------------------------------------------------- // -// CAPACITY +// Write CAPACITY // //--------------------------------------------------------------------------- void SetCapacity(BYTE *addr, const capacity_t* pCapacity) @@ -1243,7 +1243,7 @@ void SetCapacity(BYTE *addr, const capacity_t* pCapacity) //--------------------------------------------------------------------------- // -// DPB +// Write DPB // //--------------------------------------------------------------------------- void SetDpb(BYTE *addr, const dpb_t* pDpb) @@ -1251,7 +1251,7 @@ void SetDpb(BYTE *addr, const dpb_t* pDpb) ASSERT(this); ASSERT(pDpb); - // DPB + // DPB data *((WORD*)&addr[0]) = pDpb->sector_size; addr[2] = pDpb->cluster_size; addr[3] = pDpb->shift; @@ -1267,7 +1267,7 @@ void SetDpb(BYTE *addr, const dpb_t* pDpb) //--------------------------------------------------------------------------- // -// IOCTRLǂݍ +// Read IOCTRL // //--------------------------------------------------------------------------- void GetIoctrl(DWORD param, DWORD func, ioctrl_t* pIoctrl) @@ -1279,12 +1279,12 @@ void GetIoctrl(DWORD param, DWORD func, ioctrl_t* pIoctrl) switch (func) { case 2: - // fBAĔF + // Re-identify media pIoctrl->param = param; return; case -2: - // IvVݒ + // Configure options lp = (DWORD*)param; pIoctrl->param = *lp; return; @@ -1293,7 +1293,7 @@ void GetIoctrl(DWORD param, DWORD func, ioctrl_t* pIoctrl) //--------------------------------------------------------------------------- // -// IOCTRL +// Write IOCTRL // //--------------------------------------------------------------------------- void SetIoctrl(DWORD param, DWORD func, ioctrl_t* pIoctrl) @@ -1308,19 +1308,19 @@ void SetIoctrl(DWORD param, DWORD func, ioctrl_t* pIoctrl) switch (func) { case 0: - // fBAID̊l + // Acquire media ID wp = (WORD*)param; *wp = pIoctrl->media; return; case 1: - // Human68k݊̂߂̃_~[ + // Dummy for Human68k compatibility lp = (DWORD*)param; *lp = pIoctrl->param; return; case -1: - // 풓 + // Resident evaluation bp = (BYTE*)param; for (i = 0; i < 8; i++) { *bp = pIoctrl->buffer[i]; @@ -1329,7 +1329,7 @@ void SetIoctrl(DWORD param, DWORD func, ioctrl_t* pIoctrl) return; case -3: - // IvVl + // Acquire options lp = (DWORD*)param; *lp = pIoctrl->param; return; @@ -1338,9 +1338,9 @@ void SetIoctrl(DWORD param, DWORD func, ioctrl_t* pIoctrl) //--------------------------------------------------------------------------- // -// ARGUMENTǂݍ +// Read ARGUMENT // -// obt@TCYꍇ͓]ł؂ĕKI[B +// When this exceeds the buffer size, interrupt transfer and exit. // //--------------------------------------------------------------------------- void GetArgument(BYTE *addr, argument_t* pArgument) @@ -1374,7 +1374,7 @@ void GetArgument(BYTE *addr, argument_t* pArgument) //--------------------------------------------------------------------------- // -// Il +// Write return value // //--------------------------------------------------------------------------- void SetResult(DWORD nResult) @@ -1383,7 +1383,7 @@ void SetResult(DWORD nResult) ASSERT(this); - // vIG[ + // Handle fatal errors switch (nResult) { case FS_FATAL_INVALIDUNIT: code = 0x5001; @@ -1399,8 +1399,8 @@ void SetResult(DWORD nResult) fatal: SetReqByte(3, (BYTE)code); SetReqByte(4, code >> 8); - // @note gC”\ԂƂ́A(a5 + 18)Ă͂ȂB - // ̌㔒т Retry IꍇAlǂݍŌ듮삵Ă܂B + // @note When returning retryability, never overwrite with (a5 + 18). + // If you do that, the system will start behaving erratically if you hit Retry at the system error screen. if (code & 0x2000) break; nResult = FS_INVALIDFUNC; @@ -1412,23 +1412,23 @@ void SetResult(DWORD nResult) //--------------------------------------------------------------------------- // -// $40 - foCXN +// $40 - Device startup // // in (offset size) -// 0 1.b 萔(22) -// 2 1.b R}h($40/$c0) -// 18 1.l p[^AhX -// 22 1.b hCuԍ +// 0 1.b constant (22) +// 2 1.b command ($40/$c0) +// 18 1.l parameter address +// 22 1.b drive number // out (offset size) -// 3 1.b G[R[h() -// 4 1.b V () -// 13 1.b jbg -// 14 1.l foCXhCȍIAhX + 1 +// 3 1.b error code (lower) +// 4 1.b '' (upper) +// 13 1.b number of units +// 14 1.l device driver exit address + 1 // -// [JhCũR}h 0 ƓlɑgݍݎɌĂ΂邪ABPB y -// т̃|C^̔zpӂKv͂Ȃ. -// ̃R}hƈႢÃR}ha5 + 1ɂ͗LȒlĂȂ -// (0Ȃǂ҂Ă͂Ȃ)̂Œӂ邱ƁB +// This is called command 0 when the driver is loaded similarly to a local drive, +// but there is no need to prepare BPB or its pointer array. +// Unlike other commands, only this command does not include a valid a5 + 1, +// since it doesn't expect 0 initialization etc., so be careful. // //--------------------------------------------------------------------------- DWORD InitDevice(void) @@ -1439,13 +1439,13 @@ DWORD InitDevice(void) ASSERT(this); ASSERT(fs); - // IvVel + // Get option contents GetArgument(GetReqAddr(18), &arg); - // Human68kŗp”\ȃhCu͈̔͂ŁAt@CVXe\z + // Contstruct the filesystem to the extent Human68k can support number of drivers etc. units = FS_InitDevice(&arg); - // hCuԐM + // Return number of drives SetReqByte(13, units); return 0; @@ -1453,10 +1453,10 @@ DWORD InitDevice(void) //--------------------------------------------------------------------------- // -// $41 - fBNg`FbN +// $41 - Directory check // // in (offset size) -// 14 1.L NAMESTS\̃AhX +// 14 1.L NAMESTS struct address // //--------------------------------------------------------------------------- DWORD CheckDir(void) @@ -1467,10 +1467,10 @@ DWORD CheckDir(void) ASSERT(this); ASSERT(fs); - // Ώۃt@Cl + // Get filename to search GetNameStsPath(GetReqAddr(14), &ns); - // t@CVXeĂяo + // Call filesystem nResult = FS_CheckDir(&ns); return nResult; @@ -1478,10 +1478,10 @@ DWORD CheckDir(void) //--------------------------------------------------------------------------- // -// $42 - fBNg쐬 +// $42 - Create directory // // in (offset size) -// 14 1.L NAMESTS\̃AhX +// 14 1.L NAMESTS struct address // //--------------------------------------------------------------------------- DWORD MakeDir(void) @@ -1492,10 +1492,10 @@ DWORD MakeDir(void) ASSERT(this); ASSERT(fs); - // Ώۃt@Cl + // Get filename to search GetNameSts(GetReqAddr(14), &ns); - // t@CVXeĂяo + // Call filesystem nResult = FS_MakeDir(&ns); return nResult; @@ -1503,10 +1503,10 @@ DWORD MakeDir(void) //--------------------------------------------------------------------------- // -// $43 - fBNg폜 +// $43 - Delete directory // // in (offset size) -// 14 1.L NAMESTS\̃AhX +// 14 1.L NAMESTS struct address // //--------------------------------------------------------------------------- DWORD RemoveDir(void) @@ -1517,10 +1517,10 @@ DWORD RemoveDir(void) ASSERT(this); ASSERT(fs); - // Ώۃt@Cl + // Get filename to search GetNameSts(GetReqAddr(14), &ns); - // t@CVXeĂяo + // Call filesystem nResult = FS_RemoveDir(&ns); return nResult; @@ -1528,11 +1528,11 @@ DWORD RemoveDir(void) //--------------------------------------------------------------------------- // -// $44 - t@CύX +// $44 - Change filename // // in (offset size) -// 14 1.L NAMESTS\̃AhX t@C -// 18 1.L NAMESTS\̃AhX Vt@C +// 14 1.L NAMESTS struct address old filename +// 18 1.L NAMESTS struct address new filename // //--------------------------------------------------------------------------- DWORD Rename(void) @@ -1544,11 +1544,11 @@ DWORD Rename(void) ASSERT(this); ASSERT(fs); - // Ώۃt@Cl + // Get filename to search GetNameSts(GetReqAddr(14), &ns); GetNameSts(GetReqAddr(18), &ns_new); - // t@CVXeĂяo + // Call filesystem nResult = FS_Rename(&ns, &ns_new); return nResult; @@ -1556,10 +1556,10 @@ DWORD Rename(void) //--------------------------------------------------------------------------- // -// $45 - t@C폜 +// $45 - Delete file // // in (offset size) -// 14 1.L NAMESTS\̃AhX +// 14 1.L NAMESTS struct address // //--------------------------------------------------------------------------- DWORD Delete(void) @@ -1570,10 +1570,10 @@ DWORD Delete(void) ASSERT(this); ASSERT(fs); - // Ώۃt@Cl + // Get filename to search GetNameSts(GetReqAddr(14), &ns); - // t@CVXeĂяo + // Call filesystem nResult = FS_Delete(&ns); return nResult; @@ -1581,12 +1581,12 @@ DWORD Delete(void) //--------------------------------------------------------------------------- // -// $46 - t@C擾/ݒ +// $46 - Get/set file attribute // // in (offset size) -// 12 1.B ǂݏo0x01ɂȂ̂Œ -// 13 1.B $FFƓǂݏo -// 14 1.L NAMESTS\̃AhX +// 12 1.B Note that this becomes 0x01 when read from memory +// 13 1.B attribute; read from memory when $FF +// 14 1.L NAMESTS struct address // //--------------------------------------------------------------------------- DWORD Attribute(void) @@ -1598,13 +1598,13 @@ DWORD Attribute(void) ASSERT(this); ASSERT(fs); - // Ώۃt@Cl + // Get filename to search GetNameSts(GetReqAddr(14), &ns); - // Ώۑ + // Target attribute attr = GetReqByte(13); - // t@CVXeĂяo + // Call filesystem nResult = FS_Attribute(&ns, attr); return nResult; @@ -1612,39 +1612,39 @@ DWORD Attribute(void) //--------------------------------------------------------------------------- // -// $47 - t@C +// $47 - File search // // in (offset size) -// 0 1.b 萔(26) -// 1 1.b jbgԍ -// 2 1.b R}h($47/$c7) -// 13 1.b (WindrvXMł͖gpBobt@ɒlĂ) -// 14 1.l t@Cobt@(namests `) -// 18 1.l obt@(files `) ̃obt@Ɍrƌʂ +// 0 1.b constant (26) +// 1 1.b unit number +// 2 1.b command ($47/$c7) +// 13 1.b search attribute (Unused in WindrvXM. Written directly to search buffer.) +// 14 1.l Filename buffer (namests format) +// 18 1.l Search buffer (files format) Search data in progress and search results are written to this buffer. // out (offset size) -// 3 1.b G[R[h() -// 4 1.b V () -// 18 1.l UgXe[^X +// 3 1.b error code (lower) +// 4 1.b '' (upper) +// 18 1.l result status // -// fBNgwt@C. DOS _FILES Ăяo. -// ɎsꍇAႵ͌ɐĂChJ[hg -// Ȃꍇ́A񌟍ɕKsׂɌobt@̃ItZbg -// -1 . ꍇ͌‚t@C̏ݒ肷 -// ƋɁAp̏̃ZN^ԍAItZbgA[gfBNg̏ -// ͍XɎcZN^ݒ肷. hCuEApX DOS R[ -// Őݒ肳̂ŏޕKv͂Ȃ. +// Search files from specified directory. Called from DOS _FILES. +// When a search fails, or succeeds but not using wildcards, +// we write -1 to the search buffer offset to make the next search fail. +// When a file is found, we set its data as well as the following data +// for the next search: sector number, offset, and in the case of the +// root directory, also the remaining sectors. The search drive, attribute, +// and path name are set in the DOS call processing, so writing is not needed. // -// +// // (offset size) -// 0 1.b NAMWLD 0:ChJ[hȂ -1:t@CwȂ -// (ChJ[h̕) -// 1 1.b NAMDRV hCuԍ(A=0,B=1,c,Z=25) -// 2 65.b NAMPTH pX('\'{΃TufBNg{'\') -// 67 8.b NAMNM1 t@C(擪 8 ) -// 75 3.b NAMEXT gq -// 78 10.b NAMNM2 t@C(c 10 ) +// 0 1.b NAMWLD 0: no wildcard -1:no specified file +// (number of chars of wildcard) +// 1 1.b NAMDRV drive number (A=0,B=1,...,Z=25) +// 2 65.b NAMPTH path ('\' + optional subdirectory + '\') +// 67 8.b NAMNM1 filename (first 8 chars) +// 75 3.b NAMEXT file ending +// 78 10.b NAMNM2 filename (remaining 10 chars) // -// pX؂蕶0x2F(/)0x5C(\)ł͂Ȃ0x09(TAB)gĂ̂ŒӁB +// Note that 0x09 (TAB) is used for path separator, rather than 0x2F (/) or 0x5C (\) // //--------------------------------------------------------------------------- DWORD Files(void) @@ -1657,17 +1657,17 @@ DWORD Files(void) ASSERT(this); ASSERT(fs); - // roߊi[̈ + // Memory for storing search in progress files = GetReqAddr(18); GetFiles(files, &info); - // Ώۃt@Cl + // Get filename to search GetNameSts(GetReqAddr(14), &ns); - // t@CVXeĂяo + // Call filesystem nResult = FS_Files((DWORD)files, &ns, &info); - // ʂ̔f + // Apply search results if (nResult >= 0) { SetFiles(files, &info); } @@ -1677,10 +1677,10 @@ DWORD Files(void) //--------------------------------------------------------------------------- // -// $48 - t@C +// $48 - Search next file // // in (offset size) -// 18 1.L FILES\̃AhX +// 18 1.L FILES struct address // //--------------------------------------------------------------------------- DWORD NFiles(void) @@ -1692,14 +1692,14 @@ DWORD NFiles(void) ASSERT(this); ASSERT(fs); - // [N̈̓ǂݍ + // Read work memory files = GetReqAddr(18); GetFiles(files, &info); - // t@CVXeĂяo + // Call filesystem nResult = FS_NFiles((DWORD)files, &info); - // ʂ̔f + // Apply search results if (nResult >= 0) { SetFiles(files, &info); } @@ -1709,14 +1709,14 @@ DWORD NFiles(void) //--------------------------------------------------------------------------- // -// $49 - t@C쐬(Create) +// $49 - Create file (Create) // // in (offset size) -// 1 1.B jbgԍ -// 13 1.B -// 14 1.L NAMESTS\̃AhX -// 18 1.L [h (0:_NEWFILE 1:_CREATE) -// 22 1.L FCB\̃AhX +// 1 1.B unit number +// 13 1.B attribute +// 14 1.L NAMESTS struct address +// 18 1.L mode (0:_NEWFILE 1:_CREATE) +// 22 1.L FCB struct address // //--------------------------------------------------------------------------- DWORD Create(void) @@ -1731,23 +1731,23 @@ DWORD Create(void) ASSERT(this); ASSERT(fs); - // Ώۃt@Cl + // Get target filename GetNameSts(GetReqAddr(14), &ns); - // FCBl + // Get FCB pFcb = GetReqAddr(22); GetFcb(pFcb, &fcb); - // + // Attribute attr = GetReqByte(13); - // ㏑[h + // Forced overwrite mode force = (BOOL)GetReqLong(18); - // t@CVXeĂяo + // Call filesystem nResult = FS_Create((DWORD)pFcb, &ns, &fcb, attr, force); - // ʂ̔f + // Apply results if (nResult >= 0) { SetFcb(pFcb, &fcb); } @@ -1757,15 +1757,15 @@ DWORD Create(void) //--------------------------------------------------------------------------- // -// $4A - t@CI[v +// $4A - File open // // in (offset size) -// 1 1.B jbgԍ -// 14 1.L NAMESTS\̃AhX -// 22 1.L FCB\̃AhX -// FCBɂ͂قƂǂ̃p[^ݒς -// Et̓I[vuԂ̂̂ɂȂĂ̂ŏ㏑ -// TCY0ɂȂĂ̂ŏ㏑ +// 1 1.B unit number +// 14 1.L NAMESTS struct address +// 22 1.L FCB struct address +// Most parameters are already set in FCB. +// Overwrite time and date at the moment of opening. +// Overwrite if size is 0. // //--------------------------------------------------------------------------- DWORD Open(void) @@ -1778,17 +1778,17 @@ DWORD Open(void) ASSERT(this); ASSERT(fs); - // Ώۃt@Cl + // Get target filename GetNameSts(GetReqAddr(14), &ns); - // FCBl + // Get FCB pFcb = GetReqAddr(22); GetFcb(pFcb, &fcb); - // t@CVXeĂяo + // Call filesystem nResult = FS_Open((DWORD)pFcb, &ns, &fcb); - // ʂ̔f + // Apply results if (nResult >= 0) { SetFcb(pFcb, &fcb); } @@ -1798,10 +1798,10 @@ DWORD Open(void) //--------------------------------------------------------------------------- // -// $4B - t@CN[Y +// $4B - File close // // in (offset size) -// 22 1.L FCB\̃AhX +// 22 1.L FCB struct address // //--------------------------------------------------------------------------- DWORD Close(void) @@ -1813,14 +1813,14 @@ DWORD Close(void) ASSERT(this); ASSERT(fs); - // FCBl + // Get FCB pFcb = GetReqAddr(22); GetFcb(pFcb, &fcb); - // t@CVXeĂяo + // Call filesystem nResult = FS_Close((DWORD)pFcb, &fcb); - // ʂ̔f + // Apply results if (nResult >= 0) { SetFcb(pFcb, &fcb); } @@ -1830,21 +1830,21 @@ DWORD Close(void) //--------------------------------------------------------------------------- // -// $4C - t@Cǂݍ +// $4C - Read file // // in (offset size) -// 14 1.L ǂݍ݃obt@ Ƀt@Ceǂݍ -// 18 1.L TCY 16MB𒴂lw肳”\ -// 22 1.L FCB\̃AhX +// 14 1.L Reading buffer; Read the file contents here. +// 18 1.L Size; It may be set to values exceeding 16MB +// 22 1.L FCB struct address // -// oXG[AhXw肵ꍇ͕̓ۏ؂ȂB +// No guarantee for behavior when passing address where bus error occurs. // -// 20ĨAv́u̐ƃt@CSǂށvƂ@ŏĂ -// ”\B[H̃t@C16MBĂ[H(CV.R݂Ȃ) +// In the 20th century, a small number of apps used a trick where reading a +// negative value meant "read all the files!" Wh, what? This file is over 16MB! // -// ނ4`12MB炢̓̃vO}[猩Iȁuv̒l -// Nbv悤Ȕzł͕KvȂ̂ł͂Ȃ낤B -// ܂́A12MB16MBʒu𒴂TCYNbvƗǂB +// Back then, anything over 4~12MB was treated as "inifity" and often truncated +// out of helpfulness, which is not needed in this day and age. +// On the other hand, it might be a good idea to truncate at 12MB or 16MB. // //--------------------------------------------------------------------------- DWORD Read(void) @@ -1858,25 +1858,25 @@ DWORD Read(void) ASSERT(this); ASSERT(fs); - // FCBl + // Get FCB pFcb = GetReqAddr(22); GetFcb(pFcb, &fcb); - // ǂݍ݃obt@ + // Read buffer pAddress = GetReqAddr(14); - // ǂݍ݃TCY + // Read size nSize = GetReqLong(18); - // NbsO + // Clipping if (nSize >= WINDRV_CLIPSIZE_MAX) { nSize = WINDRV_CLIPSIZE_MAX; } - // t@CVXeĂяo + // Call filesystem nResult = FS_Read((DWORD)pFcb, &fcb, pAddress, nSize); - // ʂ̔f + // Apply results if (nResult >= 0) { SetFcb(pFcb, &fcb); } @@ -1886,14 +1886,14 @@ DWORD Read(void) //--------------------------------------------------------------------------- // -// $4D - t@C +// $4D - Write file // // in (offset size) -// 14 1.L ݃obt@ Ƀt@Ce -// 18 1.L TCY ̐Ȃt@CTCYw肵̂Ɠ -// 22 1.L FCB\̃AhX +// 14 1.L Write buffer; Write file contents here +// 18 1.L Size; If a negative number, treated the same as specifying file size +// 22 1.L FCB struct address // -// oXG[AhXw肵ꍇ͕̓ۏ؂ȂB +// No guarantee for behavior when passing address where bus error occurs. // //--------------------------------------------------------------------------- DWORD Write(void) @@ -1907,25 +1907,25 @@ DWORD Write(void) ASSERT(this); ASSERT(fs); - // FCBl + // Get FCB pFcb = GetReqAddr(22); GetFcb(pFcb, &fcb); - // ݃obt@ + // Write buffer pAddress = GetReqAddr(14); - // ݃TCY + // Write size nSize = GetReqLong(18); - // NbsO + // Clipping if (nSize >= WINDRV_CLIPSIZE_MAX) { nSize = WINDRV_CLIPSIZE_MAX; } - // t@CVXeĂяo + // Call filesystem nResult = FS_Write((DWORD)pFcb, &fcb, pAddress, nSize); - // ʂ̔f + // Apply results if (nResult >= 0) { SetFcb(pFcb, &fcb); } @@ -1935,13 +1935,13 @@ DWORD Write(void) //--------------------------------------------------------------------------- // -// $4E - t@CV[N +// $4E - File seek // // in (offset size) -// 12 1.B 0x2B ɂȂĂƂ 0̂Ƃ -// 13 1.B [h -// 18 1.L ItZbg -// 22 1.L FCB\̃AhX +// 12 1.B This becomes 0x2B sometimes, and 0 other times +// 13 1.B mode +// 18 1.L offset +// 22 1.L FCB struct address // //--------------------------------------------------------------------------- DWORD Seek(void) @@ -1955,20 +1955,20 @@ DWORD Seek(void) ASSERT(this); ASSERT(fs); - // FCBl + // Get FCB pFcb = GetReqAddr(22); GetFcb(pFcb, &fcb); - // V[N[h + // Seek mode nMode = GetReqByte(13); - // V[NItZbg + // Seek offset nOffset = GetReqLong(18); - // t@CVXeĂяo + // Call filesystem nResult = FS_Seek((DWORD)pFcb, &fcb, nMode, nOffset); - // ʂ̔f + // Apply results if (nResult >= 0) { SetFcb(pFcb, &fcb); } @@ -1978,15 +1978,15 @@ DWORD Seek(void) //--------------------------------------------------------------------------- // -// $4F - t@C擾/ݒ +// $4F - Get/set file timestamp // // in (offset size) // 18 1.W DATE // 20 1.W TIME -// 22 1.L FCB\̃AhX +// 22 1.L FCB struct address // -// FCBǂݍ݃[hŊJꂽԂłݒύX”\B -// FCBł͏݋֎~̔肪łȂ̂ŒӁB +// Possible to change settings when FCB is opened in read mode too. +// Note: Cannot make the judgement of read-only with only FCB // //--------------------------------------------------------------------------- DWORD TimeStamp(void) @@ -1999,17 +1999,17 @@ DWORD TimeStamp(void) ASSERT(this); ASSERT(fs); - // FCBl + // Get FCB pFcb = GetReqAddr(22); GetFcb(pFcb, &fcb); - // l + // Get timestamp nTime = GetReqLong(18); - // t@CVXeĂяo + // Call filesystem nResult = FS_TimeStamp((DWORD)pFcb, &fcb, nTime); - // ʂ̔f + // Apply results if (nResult < 0xFFFF0000) { SetFcb(pFcb, &fcb); } @@ -2019,27 +2019,27 @@ DWORD TimeStamp(void) //--------------------------------------------------------------------------- // -// $50 - eʎ擾 +// $50 - Get capacity // // in (offset size) -// 0 1.b 萔(26) -// 1 1.b jbgԍ -// 2 1.b R}h($50/$d0) -// 14 1.l obt@AhX +// 0 1.b constant (26) +// 1 1.b unit number +// 2 1.b command ($50/$d0) +// 14 1.l buffer address // out (offset size) -// 3 1.b G[R[h() -// 4 1.b V () -// 18 1.l UgXe[^X +// 3 1.b error code (lower) +// 4 1.b '' (upper) +// 18 1.l result status // -// fBȂe/󂫗eʁANX^/ZN^TCY. obt@ -// ɏޓe͈ȉ̒ʂ. UgXe[^XƂĎgp”\ȃoCg -// Ԃ. +// Get the total and available media capacity, as well as cluster / sector size. +// The contents to write to buffer follows. Returns number of bytes that can be +// used for result status. // // (offset size) -// 0 1.w gp”\ȃNX^ -// 2 1.w NX^ -// 4 1.w 1 NX^̃ZN^ -// 6 1.w 1 ZN^̃oCg +// 0 1.w available number of clusters +// 2 1.w total number of clusters +// 4 1.w number of sectors per 1 cluster +// 6 1.w number of bytes per 1 sector // //--------------------------------------------------------------------------- DWORD GetCapacity(void) @@ -2051,14 +2051,14 @@ DWORD GetCapacity(void) ASSERT(this); ASSERT(fs); - // obt@擾 + // Get buffer pCapacity = GetReqAddr(14); #if 0 - // t@CVXeĂяo + // Call filesystem nResult = FS_GetCapacity(&cap); #else - // ‚eԂĂ̂ŃXLbvĂ݂ + // Try skipping since the contents are always returned cap.freearea = 0xFFFF; cap.clusters = 0xFFFF; cap.sectors = 64; @@ -2066,7 +2066,7 @@ DWORD GetCapacity(void) nResult = 0x7FFF8000; #endif - // ʂ̔f + // Apply results if (nResult >= 0) { SetCapacity(pCapacity, &cap); } @@ -2076,11 +2076,11 @@ DWORD GetCapacity(void) //--------------------------------------------------------------------------- // -// $51 - hCuԌ/ +// $51 - Inspect/control drive status // // in (offset size) -// 1 1.B jbgԍ -// 13 1.B 0: Ԍ 1: CWFNg +// 1 1.B unit number +// 13 1.B status 0: status inspection 1: eject // //--------------------------------------------------------------------------- DWORD CtrlDrive(void) @@ -2091,13 +2091,13 @@ DWORD CtrlDrive(void) ASSERT(this); ASSERT(fs); - // hCuԎ擾 + // Get drive status ctrl.status = GetReqByte(13); - // t@CVXeĂяo + // Call filesystem nResult = FS_CtrlDrive(&ctrl); - // ʂ̔f + // Apply results if (nResult >= 0) { SetReqByte(13, ctrl.status); } @@ -2107,42 +2107,42 @@ DWORD CtrlDrive(void) //--------------------------------------------------------------------------- // -// $52 - DPB擾 +// $52 - Get DPB // // in (offset size) -// 0 1.b 萔(26) -// 1 1.b jbgԍ -// 2 1.b R}h($52/$d2) -// 14 1.l obt@AhX(擪AhX + 2 w) +// 0 1.b constant (26) +// 1 1.b unit number +// 2 1.b command ($52/$d2) +// 14 1.l buffer address (points at starting address + 2) // out (offset size) -// 3 1.b G[R[h() -// 4 1.b V () -// 18 1.l UgXe[^X +// 3 1.b error code (lower) +// 4 1.b '' (upper) +// 18 1.l result status // -// w胁fBȀ v1 ` DPB ŕԂ. ̃R}hŐݒ肷Kv -// ͈ȉ̒ʂ(ʓ DOS R[ݒ肷). Aobt -// @AhX̓ItZbg 2 wAhXn̂Œӂ邱. +// Specified media data is returned as DPB v1. Data needed to be set +// for this command are as follows (parantheses indicate what's set by DOS calls.) +// Note that the buffer adress is what's pointed at by offset 2. // // (offset size) -// 0 1.b (hCuԍ) -// 1 1.b (jbgԍ) -// 2 1.w 1 ZN^̃oCg -// 4 1.b 1 NX^̃ZN^ - 1 -// 5 1.b NX^ZN^̃Vtg -// bit 7 = 1 MS-DOS ` FAT(16bit Intel z) -// 6 1.w FAT ̐擪ZN^ԍ -// 8 1.b FAT ̈̌ -// 9 1.b FAT ̐߂ZN^(ʕ) -// 10 1.w [gfBNgɓt@Ȑ -// 12 1.w f[^̈̐擪ZN^ԍ -// 14 1.w NX^ + 1 -// 16 1.w [gfBNg̐擪ZN^ԍ -// 18 1.l (hCowb_̃AhX) -// 22 1.b (̕hCu) -// 23 1.b (DPB gptO: 0) -// 24 1.l ( DPB ̃AhX) -// 28 1.w (JgfBNg̃NX^ԍ: 0) -// 30 64.b (JgfBNg) +// 0 1.b (drive number) +// 1 1.b (unit number) +// 2 1.w number of bytes per 1 sector +// 4 1.b number of sectors - 1 per 1 cluster +// 5 1.b Number of cluster -> sector +// bit 7 = 1 in MS-DOS format FAT (16bit Intel array) +// 6 1.w FAT first sector number +// 8 1.b Number of FAT allocations +// 9 1.b Number of FAT controlled sectors (excluding duplicates) +// 10 1.w Number of files in the root directory +// 12 1.w First sector number of data memory +// 14 1.w Total number of clusters + 1 +// 16 1.w First sector number of root directory +// 18 1.l (Driver head address) +// 22 1.b (Physical drive name in lower-case) +// 23 1.b (DPB usage flag: normally 0) +// 24 1.l (Next DPB address) +// 28 1.w (Cluster number of current directory: normally 0) +// 30 64.b (Current directory name) // //--------------------------------------------------------------------------- DWORD GetDPB(void) @@ -2154,13 +2154,13 @@ DWORD GetDPB(void) ASSERT(this); ASSERT(fs); - // DPB擾 + // Get DPB pDpb = GetReqAddr(14); - // t@CVXeĂяo + // Call filesystem nResult = FS_GetDPB(&dpb); - // ʂ̔f + // Apply results if (nResult >= 0) { SetDpb(pDpb, &dpb); } @@ -2170,13 +2170,13 @@ DWORD GetDPB(void) //--------------------------------------------------------------------------- // -// $53 - ZN^ǂݍ +// $53 - Read sector // // in (offset size) -// 1 1.B jbgԍ -// 14 1.L obt@AhX -// 18 1.L ZN^ -// 22 1.L ZN^ԍ +// 1 1.B unit number +// 14 1.L buffer address +// 18 1.L number of sectors +// 22 1.L sector number // //--------------------------------------------------------------------------- DWORD DiskRead(void) @@ -2191,14 +2191,14 @@ DWORD DiskRead(void) ASSERT(this); ASSERT(fs); - pAddress = GetReqAddr(14); // AhX (ʃrbggtO) - nSize = GetReqLong(18); // ZN^ - nSector = GetReqLong(22); // ZN^ԍ + pAddress = GetReqAddr(14); // Address (upper bit is extension flag) + nSize = GetReqLong(18); // Number of sectors + nSector = GetReqLong(22); // Sector number - // t@CVXeĂяo + // Call filesystem nResult = FS_DiskRead(buffer, nSector, nSize); - // ʂ̔f + // Apply results if (nResult >= 0) { for (i = 0; i < sizeof(buffer); i++) { *pAddress = buffer[i]; @@ -2211,13 +2211,13 @@ DWORD DiskRead(void) //--------------------------------------------------------------------------- // -// $54 - ZN^ +// $54 - Write sector // // in (offset size) -// 1 1.B jbgԍ -// 14 1.L obt@AhX -// 18 1.L ZN^ -// 22 1.L ZN^ԍ +// 1 1.B unit number +// 14 1.L buffer address +// 18 1.L number of sectors +// 22 1.L sector number // //--------------------------------------------------------------------------- DWORD DiskWrite(void) @@ -2230,11 +2230,11 @@ DWORD DiskWrite(void) ASSERT(this); ASSERT(fs); - pAddress = GetReqAddr(14); // AhX(ʃrbggtO) - nSize = GetReqLong(18); // ZN^ - nSector = GetReqLong(22); // ZN^ԍ + pAddress = GetReqAddr(14); // Address (upper bit is extension flag) + nSize = GetReqLong(18); // Number of sectors + nSector = GetReqLong(22); // Sector number - // t@CVXeĂяo + // Call filesystem nResult = FS_DiskWrite(); return nResult; @@ -2245,9 +2245,9 @@ DWORD DiskWrite(void) // $55 - IOCTRL // // in (offset size) -// 1 1.B jbgԍ -// 14 1.L p[^ -// 18 1.W @\ԍ +// 1 1.B unit number +// 14 1.L parameter +// 18 1.W feature number // //--------------------------------------------------------------------------- DWORD Ioctrl(void) @@ -2260,15 +2260,15 @@ DWORD Ioctrl(void) ASSERT(this); ASSERT(fs); - // IOCTRL擾 - param = GetReqLong(14); // p[^ - func = GetReqWord(18); // @\ԍ + // Get IOCTRL + param = GetReqLong(14); // Parameter + func = GetReqWord(18); // Feature number GetIoctrl(param, func, &ioctrl); - // t@CVXeĂяo + // Call filesystem nResult = FS_Ioctrl(func, &ioctrl); - // ʂ̔f + // Apply results if (nResult >= 0) SetIoctrl(param, func, &ioctrl); @@ -2277,10 +2277,10 @@ DWORD Ioctrl(void) //--------------------------------------------------------------------------- // -// $56 - tbV +// $56 - Flush // // in (offset size) -// 1 1.B jbgԍ +// 1 1.b unit number // //--------------------------------------------------------------------------- DWORD Flush(void) @@ -2290,7 +2290,7 @@ DWORD Flush(void) ASSERT(this); ASSERT(fs); - // t@CVXeĂяo + // Call filesystem nResult = FS_Flush(); return nResult; @@ -2298,19 +2298,19 @@ DWORD Flush(void) //--------------------------------------------------------------------------- // -// $57 - fBA`FbN +// $57 - Media change check // // in (offset size) -// 0 1.b 萔(26) -// 1 1.b jbgԍ -// 2 1.b R}h($57/$d7) +// 0 1.b constant (26) +// 1 1.b unit number +// 2 1.b Command ($57/$d7) // out (offset size) -// 3 1.b G[R[h() -// 4 1.b V () -// 18 1.l UgXe[^X +// 3 1.b error code (lower) +// 4 1.b '' (upper) +// 18 1.l result status // -// fBAꂽۂ𒲂ׂ. Ăꍇ̃tH[}bg -// mF͂̃R}hōs. +// Checks if the media has been changed or not. Format if it has changed. +// The verification is done within this command. // //--------------------------------------------------------------------------- DWORD CheckMedia(void) @@ -2320,7 +2320,7 @@ DWORD CheckMedia(void) ASSERT(this); ASSERT(fs); - // t@CVXeĂяo + // Call filesystem nResult = FS_CheckMedia(); return nResult; @@ -2328,10 +2328,10 @@ DWORD CheckMedia(void) //--------------------------------------------------------------------------- // -// $58 - r +// $58 - Lock // // in (offset size) -// 1 1.B jbgԍ +// 1 1.b unit number // //--------------------------------------------------------------------------- DWORD Lock(void) @@ -2341,7 +2341,7 @@ DWORD Lock(void) ASSERT(this); ASSERT(fs); - // t@CVXeĂяo + // Call filesystem nResult = FS_Lock(); return nResult; @@ -2349,50 +2349,50 @@ DWORD Lock(void) //--------------------------------------------------------------------------- // -// R}hs +// Execute command // //--------------------------------------------------------------------------- DWORD ExecuteCommand() { ASSERT(this); - // G[NA + // Clear error data SetReqByte(3, 0); SetReqByte(4, 0); - // R}hԍ - command = (DWORD)GetReqByte(2); // rbg7̓xt@CtO + // Command number + command = (DWORD)GetReqByte(2); // Bit 7 is verify flag - // jbgԍ + // Unit number unit = GetReqByte(1); - // R}h + // Command branching switch (command & 0x7F) { - case 0x40: return InitDevice(); // $40 - foCXN - case 0x41: return CheckDir(); // $41 - fBNg`FbN - case 0x42: return MakeDir(); // $42 - fBNg쐬 - case 0x43: return RemoveDir(); // $43 - fBNg폜 - case 0x44: return Rename(); // $44 - t@CύX - case 0x45: return Delete(); // $45 - t@C폜 - case 0x46: return Attribute(); // $46 - t@C擾/ݒ - case 0x47: return Files(); // $47 - t@C - case 0x48: return NFiles(); // $48 - t@C - case 0x49: return Create(); // $49 - t@C쐬 - case 0x4A: return Open(); // $4A - t@CI[v - case 0x4B: return Close(); // $4B - t@CN[Y - case 0x4C: return Read(); // $4C - t@Cǂݍ - case 0x4D: return Write(); // $4D - t@C - case 0x4E: return Seek(); // $4E - t@CV[N - case 0x4F: return TimeStamp(); // $4F - t@CXV̎擾/ݒ - case 0x50: return GetCapacity();// $50 - eʎ擾 - case 0x51: return CtrlDrive(); // $51 - hCu/Ԍ - case 0x52: return GetDPB(); // $52 - DPB擾 - case 0x53: return DiskRead(); // $53 - ZN^ǂݍ - case 0x54: return DiskWrite(); // $54 - ZN^ + case 0x40: return InitDevice(); // $40 - Device startup + case 0x41: return CheckDir(); // $41 - Directory check + case 0x42: return MakeDir(); // $42 - Create directory + case 0x43: return RemoveDir(); // $43 - Delete directory + case 0x44: return Rename(); // $44 - Change file name + case 0x45: return Delete(); // $45 - Delete file + case 0x46: return Attribute(); // $46 - Get / set file attribute + case 0x47: return Files(); // $47 - Find file + case 0x48: return NFiles(); // $48 - Find next file + case 0x49: return Create(); // $49 - Create file + case 0x4A: return Open(); // $4A - Open file + case 0x4B: return Close(); // $4B - Close file + case 0x4C: return Read(); // $4C - Read file + case 0x4D: return Write(); // $4D - Write file + case 0x4E: return Seek(); // $4E - Seek file + case 0x4F: return TimeStamp(); // $4F - Get / set file timestamp + case 0x50: return GetCapacity();// $50 - Get capacity + case 0x51: return CtrlDrive(); // $51 - Inspect / control drive status + case 0x52: return GetDPB(); // $52 - Get DPB + case 0x53: return DiskRead(); // $53 - Read sectors + case 0x54: return DiskWrite(); // $54 - Write sectors case 0x55: return Ioctrl(); // $55 - IOCTRL - case 0x56: return Flush(); // $56 - tbV - case 0x57: return CheckMedia(); // $57 - fBA`FbN - case 0x58: return Lock(); // $58 - r + case 0x56: return Flush(); // $56 - Flush + case 0x57: return CheckMedia(); // $57 - Media change check + case 0x58: return Lock(); // $58 - Lock } return FS_FATAL_INVALIDCOMMAND; @@ -2400,7 +2400,7 @@ DWORD ExecuteCommand() //--------------------------------------------------------------------------- // -// +// Initialization // //--------------------------------------------------------------------------- BOOL Init() @@ -2412,7 +2412,7 @@ BOOL Init() //--------------------------------------------------------------------------- // -// s +// Execution // //--------------------------------------------------------------------------- void Process(DWORD nA5) @@ -2422,9 +2422,9 @@ void Process(DWORD nA5) ASSERT(m_bAlloc); ASSERT(m_bFree); - // NGXgwb_̃AhX + // Request header address request = (BYTE*)nA5; - // R}hs + // Command execution SetResult(ExecuteCommand()); } diff --git a/src/x68k/RASDRV/BRIDGE.H b/src/x68k/RASDRV/BRIDGE.H index b59c642d..1d531583 100644 --- a/src/x68k/RASDRV/BRIDGE.H +++ b/src/x68k/RASDRV/BRIDGE.H @@ -3,9 +3,9 @@ // SCSI Target Emulator RaSCSI (*^..^*) // for Raspberry Pi // -// Powered by XM6 TypeG Technorogy. +// Powered by XM6 TypeG Technology. // Copyright (C) 2016-2017 GIMONS -// [ zXgt@CVXe ubWhCo ] +// [ Host Filesystem Bridge Driver ] // //--------------------------------------------------------------------------- @@ -30,7 +30,7 @@ typedef int BOOL; //--------------------------------------------------------------------------- // -// 萔` +// Constant definitions // //--------------------------------------------------------------------------- #define FILEPATH_MAX _MAX_PATH @@ -38,219 +38,218 @@ typedef int BOOL; //--------------------------------------------------------------------------- // -// Xe[^XR[h` +// Status code definitions // //--------------------------------------------------------------------------- -#define FS_INVALIDFUNC 0xFFFFFFFF // ȃt@NVR[hs -#define FS_FILENOTFND 0xFFFFFFFE // w肵t@C‚Ȃ -#define FS_DIRNOTFND 0xFFFFFFFD // w肵fBNg‚Ȃ -#define FS_OVEROPENED 0xFFFFFFFC // I[vĂt@C -#define FS_CANTACCESS 0xFFFFFFFB // fBNg{[x̓ANZXs -#define FS_NOTOPENED 0xFFFFFFFA // w肵nh̓I[vĂȂ -#define FS_INVALIDMEM 0xFFFFFFF9 // Ǘ̈悪j󂳂ꂽ -#define FS_OUTOFMEM 0xFFFFFFF8 // sɕKvȃȂ -#define FS_INVALIDPTR 0xFFFFFFF7 // ȃǗ|C^w肵 -#define FS_INVALIDENV 0xFFFFFFF6 // sȊ‹w肵 -#define FS_ILLEGALFMT 0xFFFFFFF5 // st@C̃tH[}bgُ -#define FS_ILLEGALMOD 0xFFFFFFF4 // I[ṽANZX[hُ -#define FS_INVALIDPATH 0xFFFFFFF3 // t@C̎wɌ肪 -#define FS_INVALIDPRM 0xFFFFFFF2 // ȃp[^ŃR[ -#define FS_INVALIDDRV 0xFFFFFFF1 // hCuwɌ肪 -#define FS_DELCURDIR 0xFFFFFFF0 // JgfBNg͍폜łȂ -#define FS_NOTIOCTRL 0xFFFFFFEF // IOCTRLłȂfoCX -#define FS_LASTFILE 0xFFFFFFEE // ȏt@C‚Ȃ -#define FS_CANTWRITE 0xFFFFFFED // w̃t@C͏݂łȂ -#define FS_DIRALREADY 0xFFFFFFEC // w̃fBNg͊ɓo^Ă -#define FS_CANTDELETE 0xFFFFFFEB // t@Ĉō폜łȂ -#define FS_CANTRENAME 0xFFFFFFEA // t@ĈŃl[łȂ -#define FS_DISKFULL 0xFFFFFFE9 // fBXNtŃt@CȂ -#define FS_DIRFULL 0xFFFFFFE8 // fBNgtŃt@CȂ -#define FS_CANTSEEK 0xFFFFFFE7 // ẅʒuɂ̓V[NłȂ -#define FS_SUPERVISOR 0xFFFFFFE6 // X[p[oCUԂŃX[poCUw肵 -#define FS_THREADNAME 0xFFFFFFE5 // Xbh݂ -#define FS_BUFWRITE 0xFFFFFFE4 // vZXԒʐM̃obt@݋֎~ -#define FS_BACKGROUND 0xFFFFFFE3 // obNOEhvZXNłȂ -#define FS_OUTOFLOCK 0xFFFFFFE0 // bN̈悪Ȃ -#define FS_LOCKED 0xFFFFFFDF // bNĂăANZXłȂ -#define FS_DRIVEOPENED 0xFFFFFFDE // w̃hCu̓nhI[vĂ -#define FS_LINKOVER 0xFFFFFFDD // V{bNNlXg16𒴂 -#define FS_FILEEXIST 0xFFFFFFB0 // t@C݂ +#define FS_INVALIDFUNC 0xFFFFFFFF // Executed an invalid function +#define FS_FILENOTFND 0xFFFFFFFE // The selected file can not be found +#define FS_DIRNOTFND 0xFFFFFFFD // The selected directory can not be found +#define FS_OVEROPENED 0xFFFFFFFC // There are too many files open +#define FS_CANTACCESS 0xFFFFFFFB // Can not access the direcory or volume +#define FS_NOTOPENED 0xFFFFFFFA // The selected handle is not opened +#define FS_INVALIDMEM 0xFFFFFFF9 // Memory management has been destroyed +#define FS_OUTOFMEM 0xFFFFFFF8 // Insufficient memory for execution +#define FS_INVALIDPTR 0xFFFFFFF7 // Selected an invalid memory management pointer +#define FS_INVALIDENV 0xFFFFFFF6 // Selected an invalid environment +#define FS_ILLEGALFMT 0xFFFFFFF5 // The exeucted file is in an invalid format +#define FS_ILLEGALMOD 0xFFFFFFF4 // Invalid open access mode +#define FS_INVALIDPATH 0xFFFFFFF3 // Mistake in selected file name +#define FS_INVALIDPRM 0xFFFFFFF2 // Called with an invalid parameter +#define FS_INVALIDDRV 0xFFFFFFF1 // Mistake in selected drive +#define FS_DELCURDIR 0xFFFFFFF0 // Unable to delete the current directory +#define FS_NOTIOCTRL 0xFFFFFFEF // Unable to use IOCTRL with the device +#define FS_LASTFILE 0xFFFFFFEE // Can not find any more files +#define FS_CANTWRITE 0xFFFFFFED // Selected file can not be written +#define FS_DIRALREADY 0xFFFFFFEC // Selected directory is already registered +#define FS_CANTDELETE 0xFFFFFFEB // Can not delete because of a file +#define FS_CANTRENAME 0xFFFFFFEA // Can not rename because of a file +#define FS_DISKFULL 0xFFFFFFE9 // Can not create a file because the disk is full +#define FS_DIRFULL 0xFFFFFFE8 // Can not create a file because the directory is full +#define FS_CANTSEEK 0xFFFFFFE7 // Can not seek in the selected location +#define FS_SUPERVISOR 0xFFFFFFE6 // Selected supervisor in supervisor mode +#define FS_THREADNAME 0xFFFFFFE5 // A thread with this name already exists +#define FS_BUFWRITE 0xFFFFFFE4 // Writing to inter-process communication buffers is disallowed +#define FS_BACKGROUND 0xFFFFFFE3 // Unable to start a background process +#define FS_OUTOFLOCK 0xFFFFFFE0 // Insufficient lock space +#define FS_LOCKED 0xFFFFFFDF // Can not access because it is locked +#define FS_DRIVEOPENED 0xFFFFFFDE // Selected drive has an open handler +#define FS_LINKOVER 0xFFFFFFDD // The symbolic link is nested over 16 times +#define FS_FILEEXIST 0xFFFFFFB0 // The file exists -#define FS_FATAL_MEDIAOFFLINE 0xFFFFFFA3 // fBAĂȂ -#define FS_FATAL_WRITEPROTECT 0xFFFFFFA2 // ݋֎~ᔽ -#define FS_FATAL_INVALIDCOMMAND 0xFFFFFFA1 // sȃR}hԍ -#define FS_FATAL_INVALIDUNIT 0xFFFFFFA0 // sȃjbgԍ +#define FS_FATAL_MEDIAOFFLINE 0xFFFFFFA3 // No media inserted +#define FS_FATAL_WRITEPROTECT 0xFFFFFFA2 // Write protected +#define FS_FATAL_INVALIDCOMMAND 0xFFFFFFA1 // Invalid command number +#define FS_FATAL_INVALIDUNIT 0xFFFFFFA0 // Invalid unit number -#define HUMAN68K_PATH_MAX 96 // Human68k̃pXő咷 +#define HUMAN68K_PATH_MAX 96 // Longest path allowed in Human68k //=========================================================================== // -/// Human68k O +/// Human68k name space // //=========================================================================== -/// t@Crbg +/// File attribute bit enum attribute_t { - AT_READONLY = 0x01, // ǂݍݐp - AT_HIDDEN = 0x02, // B - AT_SYSTEM = 0x04, // VXe - AT_VOLUME = 0x08, // {[x - AT_DIRECTORY= 0x10, // fBNg - AT_ARCHIVE = 0x20, // A[JCu - AT_ALL = 0xFF, // SĂ̑rbg1 + AT_READONLY = 0x01, // Read only attribute + AT_HIDDEN = 0x02, // Hidden attribute + AT_SYSTEM = 0x04, // System attribute + AT_VOLUME = 0x08, // Volume label attribute + AT_DIRECTORY = 0x10, // Directory attribute + AT_ARCHIVE = 0x20, // Archive attribute + AT_ALL = 0xFF, // All attribute bits are 1 }; -/// t@CI[v[h +/// File open modes enum open_t { - OP_READ = 0, // ǂݍ - OP_WRITE = 1, // - OP_FULL = 2, // ǂݏ - OP_MASK = 0x0F, // p}XN - OP_SHARE_NONE = 0x10, // L֎~ - OP_SHARE_READ = 0x20, // ǂݍ݋L - OP_SHARE_WRITE = 0x30, // ݋L - OP_SHARE_FULL = 0x40, // ǂݏL - OP_SHARE_MASK = 0x70, // Lp}XN - OP_SPECIAL = 0x100,// ANZX + OP_READ = 0, // Read + OP_WRITE = 1, // Write + OP_FULL = 2, // Read/Write + OP_MASK = 0x0F, // Decision mask + OP_SHARE_NONE = 0x10, // Sharing forbidden + OP_SHARE_READ = 0x20, // Read sharing + OP_SHARE_WRITE = 0x30, // Write sharing + OP_SHARE_FULL = 0x40, // Read/Write sharing + OP_SHARE_MASK = 0x70, // Sharing decision mask + OP_SPECIAL = 0x100, // Dictionary access }; -/// V[N +/// Seek types enum seek_t { - SK_BEGIN = 0, // t@C擪 - SK_CURRENT = 1, // ݈ʒu - SK_END = 2, // t@C + SK_BEGIN = 0, // From the beginning of a file + SK_CURRENT = 1, // From the current location + SK_END = 2, // From the end of the file }; -/// fBAoCg +/// Media byte enum media_t { - MEDIA_2DD_10 = 0xE0, // 2DD/10ZN^ - MEDIA_1D_9 = 0xE5, // 1D/9ZN^ - MEDIA_2D_9 = 0xE6, // 2D/9ZN^ - MEDIA_1D_8 = 0xE7, // 1D/8ZN^ - MEDIA_2D_8 = 0xE8, // 2D/8ZN^ - MEDIA_2HT = 0xEA, // 2HT - MEDIA_2HS = 0xEB, // 2HS - MEDIA_2HDE = 0xEC, // 2DDE - MEDIA_1DD_9 = 0xEE, // 1DD/9ZN^ - MEDIA_1DD_8 = 0xEF, // 1DD/8ZN^ - MEDIA_MANUAL = 0xF1, // [ghCu (蓮CWFNg) - MEDIA_REMOVABLE = 0xF2, // [ghCu ([ou) - MEDIA_REMOTE = 0xF3, // [ghCu - MEDIA_DAT = 0xF4, // SCSI-DAT - MEDIA_CDROM = 0xF5, // SCSI-CDROM - MEDIA_MO = 0xF6, // SCSI-MO - MEDIA_SCSI_HD = 0xF7, // SCSI-HD - MEDIA_SASI_HD = 0xF8, // SASI-HD - MEDIA_RAMDISK = 0xF9, // RAMfBXN - MEDIA_2HQ = 0xFA, // 2HQ - MEDIA_2DD_8 = 0xFB, // 2DD/8ZN^ - MEDIA_2DD_9 = 0xFC, // 2DD/9ZN^ - MEDIA_2HC = 0xFD, // 2HC - MEDIA_2HD = 0xFE, // 2HD + MEDIA_2DD_10 = 0xE0, // 2DD/10 sector + MEDIA_1D_9 = 0xE5, // 1D/9 sector + MEDIA_2D_9 = 0xE6, // 2D/9 sector + MEDIA_1D_8 = 0xE7, // 1D/8 sector + MEDIA_2D_8 = 0xE8, // 2D/8 sector + MEDIA_2HT = 0xEA, // 2HT + MEDIA_2HS = 0xEB, // 2HS + MEDIA_2HDE = 0xEC, // 2DDE + MEDIA_1DD_9 = 0xEE, // 1DD/9 sector + MEDIA_1DD_8 = 0xEF, // 1DD/8 sector + MEDIA_MANUAL = 0xF1, // Remote drive (manual eject) + MEDIA_REMOVABLE = 0xF2, // Remote drive (removable) + MEDIA_REMOTE = 0xF3, // Remote drive + MEDIA_DAT = 0xF4, // SCSI-DAT + MEDIA_CDROM = 0xF5, // SCSI-CDROM + MEDIA_MO = 0xF6, // SCSI-MO + MEDIA_SCSI_HD = 0xF7, // SCSI-HD + MEDIA_SASI_HD = 0xF8, // SASI-HD + MEDIA_RAMDISK = 0xF9, // RAM disk + MEDIA_2HQ = 0xFA, // 2HQ + MEDIA_2DD_8 = 0xFB, // 2DD/8 sector + MEDIA_2DD_9 = 0xFC, // 2DD/9 sector + MEDIA_2HC = 0xFD, // 2HC + MEDIA_2HD = 0xFE, // 2HD }; -/// namests\ +/// namests struct typedef struct { - BYTE wildcard; // ChJ[h - BYTE drive; // hCuԍ - BYTE path[65]; // pX(TufBNg+/) - BYTE name[8]; // t@C (PADDING 0x20) - BYTE ext[3]; // gq (PADDING 0x20) - BYTE add[10]; // t@Clj (PADDING 0x00) + BYTE wildcard; // Wildcard character length + BYTE drive; // Drive number + BYTE path[65]; // Path (subdirectory +/) + BYTE name[8]; // File name (PADDING 0x20) + BYTE ext[3]; // Extension (PADDING 0x20) + BYTE add[10]; // File name addition (PADDING 0x00) } namests_t; -/// files\ +/// files struct typedef struct { - BYTE fatr; // + 0 鑮 Ǎp + BYTE fatr; // + 0 search attribute; read-only BYTE pad1[3]; // padding -// BYTE drive; // + 1 hCuԍ Ǎp - DWORD sector; // + 2 fBNg̃ZN^ DOS _FILES擪AhXőp -// WORD cluster; // + 6 fBNg̃NX^ ڍוs (gp) - WORD offset; // + 8 fBNgGg p -// BYTE name[8]; // +10 Ɨpt@C Ǎp (gp) -// BYTE ext[3]; // +18 Ɨpgq Ǎp (gp) - BYTE attr; // +21 t@C p +// BYTE drive; // + 1 drive number; read-only + DWORD sector; // + 2 directory sector; DOS _FILES first address substitute +// WORD cluster; // + 6 directory cluster; details unknown (unused) + WORD offset; // + 8 directory entry; write-only +// BYTE name[8]; // +10 working file name; write-only (unused) +// BYTE ext[3]; // +18 working extension; write-only (unused) + BYTE attr; // +21 file attribute; write-only BYTE pad2; // padding - WORD time; // +22 ŏIύX p - WORD date; // +24 ŏIύX p - DWORD size; // +26 t@CTCY p - BYTE full[23]; // +30 tt@C p + WORD time; // +22 last change time of day; write-only + WORD date; // +24 last change date; write-only + DWORD size; // +26 file size; write-only + BYTE full[23]; // +30 full name; write-only BYTE pad3; // padding } files_t; /// FCB\ typedef struct { -// BYTE pad00[6]; // + 0`+ 5 (gp) - DWORD fileptr; // + 6`+ 9 t@C|C^ -// BYTE pad01[4]; // +10`+13 (gp) - WORD mode; // +14`+15 I[v[h -// BYTE pad02[16]; // +16`+31 (gp) -// DWORD zero; // +32`+35 I[v̂Ƃ0܂Ă (gp) -// BYTE name[8]; // +36`+43 t@C (PADDING 0x20) (gp) -// BYTE ext[3]; // +44`+46 gq (PADDING 0x20) (gp) - BYTE attr; // +47 t@C - BYTE pad; // padding -// BYTE add[10]; // +48`+57 t@Clj (PADDING 0x00) (gp) - WORD time; // +58`+59 ŏIύX - WORD date; // +60`+61 ŏIύX -// WORD cluster; // +62`+63 NX^ԍ (gp) - DWORD size; // +64`+67 t@CTCY -// BYTE pad03[28]; // +68`+95 FATLbV (gp) +// BYTE pad00[6]; // + 0~+ 5 (unused) + DWORD fileptr; // + 6~+ 9 file pointer +// BYTE pad01[4]; // +10~+13 (unused) + WORD mode; // +14~+15 open mode +// BYTE pad02[16]; // +16~+31 (unused) +// DWORD zero; // +32~+35 zeros are written when opened (unused) +// BYTE name[8]; // +36~+43 file name (PADDING 0x20) (unused) +// BYTE ext[3]; // +44~+46 extension (PADDING 0x20) (unused) + BYTE attr; // +47 file attribute +// BYTE add[10]; // +48~+57 file name addition (PADDING 0x00) (unused) + WORD time; // +58~+59 last change time of day + WORD date; // +60~+61 last change date +// WORD cluster; // +62~+63 cluster number (unused) + DWORD size; // +64~+67 file size +// BYTE pad03[28]; // +68~+95 FAT cache (unused) } fcb_t; -/// capacity\ +/// capacity struct typedef struct { - WORD freearea; // + 0 gp”\ȃNX^ - WORD clusters; // + 2 NX^ - WORD sectors; // + 4 NX^̃ZN^ - WORD bytes; // + 6 ZN^̃oCg + WORD freearea; // + 0 Number of available clusters + WORD clusters; // + 2 Total number of clusters + WORD sectors; // + 4 Number of sectors per cluster + WORD bytes; // + 6 Number of bytes per sector } capacity_t; -/// ctrldrive\ +/// ctrldrive struct typedef struct { - BYTE status; // +13 + BYTE status; // +13 status BYTE pad[3]; // padding } ctrldrive_t; -/// DPB\ +/// DPB struct typedef struct { - WORD sector_size; // + 0 1ZN^̃oCg - BYTE cluster_size; // + 2 1NX^̃ZN^-1 - BYTE shift; // + 3 NX^ZN^̃Vtg - WORD fat_sector; // + 4 FAT̐擪ZN^ԍ - BYTE fat_max; // + 6 FAT̈̌ - BYTE fat_size; // + 7 FAT̐߂ZN^(ʕ) - WORD file_max; // + 8 [gfBNgɓt@Ȑ - WORD data_sector; // +10 f[^̈̐擪ZN^ԍ - WORD cluster_max; // +12 NX^+1 - WORD root_sector; // +14 [gfBNg̐擪ZN^ԍ -// DWORD driverentry; // +16 foCXhCoւ̃|C^ (gp) - BYTE media; // +20 fBAʎq -// BYTE flag; // +21 DPBgptO (gp) - BYTE pad; // padding + WORD sector_size; // + 0 Number of bytes in one sector + BYTE cluster_size; // + 2 Number sectors in one cluster -1 + BYTE shift; // + 3 Number of clustersector shifts + WORD fat_sector; // + 4 FAT first sector number + BYTE fat_max; // + 6 FAT storage quantity + BYTE fat_size; // + 7 FAT controlled sector number (excluding duplicates) + WORD file_max; // + 8 Number of files in the root directory + WORD data_sector; // +10 First sector number of data storage + WORD cluster_max; // +12 Total number of clusters +1 + WORD root_sector; // +14 First sector number of root directory +// DWORD driverentry; // +16 Device driver pointer (unused) + BYTE media; // +20 Media identifier +// BYTE flag; // +21 Flag used by DPB (unused) + BYTE pad; // padding } dpb_t; -/// fBNgGg\ +/// Directory entry struct typedef struct { - BYTE name[8]; // + 0 t@C (PADDING 0x20) - BYTE ext[3]; // + 8 gq (PADDING 0x20) - BYTE attr; // +11 t@C - BYTE add[10]; // +12 t@Clj (PADDING 0x00) - WORD time; // +22 ŏIύX - WORD date; // +24 ŏIύX - WORD cluster; // +26 NX^ԍ - DWORD size; // +28 t@CTCY + BYTE name[8]; // + 0 File name (PADDING 0x20) + BYTE ext[3]; // + 8 Extension (PADDING 0x20) + BYTE attr; // +11 File attribute + BYTE add[10]; // +12 File name addition (PADDING 0x00) + WORD time; // +22 Last change time of day + WORD date; // +24 Last change date + WORD cluster; // +26 Cluster number + DWORD size; // +28 File size } dirent_t; -/// IOCTRLp[^p +/// IOCTRL parameter union typedef union { - BYTE buffer[8]; // oCgPʂł̃ANZX - DWORD param; // p[^(擪4oCg) - WORD media; // fBAoCg(擪2oCg) + BYTE buffer[8]; // Access in byte units + DWORD param; // Parameter (First 4 bytes) + WORD media; // Media byte (First 2 bytes) } ioctrl_t; -/// R}hC\ +/// Command line parameter struct typedef struct { - BYTE buf[256]; // R}hC + BYTE buf[256]; // Command line argument } argument_t; #endif // bridge_h diff --git a/src/x68k/RASDRV/RASDRV.S b/src/x68k/RASDRV/RASDRV.S index 6cb28b1e..6eb0a848 100644 --- a/src/x68k/RASDRV/RASDRV.S +++ b/src/x68k/RASDRV/RASDRV.S @@ -3,9 +3,9 @@ * SCSI Target Emulator RaSCSI (*^..^*) * for Raspberry Pi * -* Powered by XM6 TypeG Technorogy. +* Powered by XM6 TypeG Technology. * Copyright (C) 2016-2019 GIMONS -* [ zXgt@CVXe ubWhCo ] +* [ Host Filesystem Bridge Driver ] * * Based on * X68k Emulator Host Filesystem Driver version 0.27 @@ -20,19 +20,19 @@ .XREF _Init, _Process ;bridge.c -COMMAND EQU 2 R}hԍ -ERRLOW EQU 3 G[R[h8rbg -ERRHIGH EQU 4 G[R[h8rbg -MXUNIT EQU 13 jbg -DEVEND EQU 14 hCoIAhX -BDEVNO EQU 22 x[XhCuԍ +COMMAND EQU 2 Command number +ERRLOW EQU 3 Error code lower 8 bits +ERRHIGH EQU 4 Error code upper 8 bits +MXUNIT EQU 13 Number of units +DEVEND EQU 14 Driver exit address +BDEVNO EQU 22 Base drive number DDHEADER: - DC.L -1 +$00 N|C^ - DC.W $2040 +$04 foCX + DC.L -1 +$00 Link pointer + DC.W $2040 +$04 Device attribute DC.L DDSTRATEGY +$06 DC.L DDENTRY_FIRST +$0A - DC.B '*EMUHOST' +$0E foCX + DC.B '*EMUHOST' +$0E Device name DDREQUEST: DC.L 0 @@ -41,12 +41,12 @@ DDSTRATEGY: MOVE.L A5,DDREQUEST RTS -*foCXhCoGg +*Device driver entry DDENTRY: MOVE.L SP,(STACK_BUFF) LEA (DEF_STACK),SP MOVEM.L D1-D3/A0-A2,-(SP) - MOVEM.L DDREQUEST(PC),D0 D0.L: NGXgwb_AhX + MOVEM.L DDREQUEST(PC),D0 D0.L: Request header address MOVE.L D0,-(SP) BSR _Process ADDQ.L #4,SP @@ -56,67 +56,67 @@ DDENTRY: KEEP_HERE: -*݂̂̃foCXhCoGg +*First time only device driver entry DDENTRY_FIRST: MOVEM.L D1-D3/A0-A2/A5,-(SP) - MOVEA.L DDREQUEST(PC),A5 A5.L: NGXgwb_AhX -* CMPI.B #$40,COMMAND(A5) sHuman68kł̓`FbNsv -* BNE UNSUPPORTED G[: Ή + MOVEA.L DDREQUEST(PC),A5 A5.L: Request header address +* CMPI.B #$40,COMMAND(A5) No need to check when Human68k is running +* BNE UNSUPPORTED Error: Not supported -*^Cg\ +*Show title PEA MESSAGE_TITLE(PC) DOS _PRINT ADDQ.L #4,SP -*foCX`FbN +*Device check DEVICE_CHECK: BSR _Init TST.L D0 - BEQ NOTFOUND G[: foCX݂܂ + BEQ NOTFOUND Error: Device not found -*풓Jn +*Resident program start LEA DDENTRY(PC),A1 LEA DDHEADER+$0A(PC),A0 - MOVE.L A1,(A0) hCoGgݒ - MOVE.L #PROG_END,DEVEND(A5) 풓IAhXݒ + MOVE.L A1,(A0) Configure driver entry + MOVE.L #PROG_END,DEVEND(A5) Configure resident exit address - ST.B MXUNIT(A5) |[g̔̂ߕ̒lݒ肵Ă - JSR (A1) hCoGgs + ST.B MXUNIT(A5) Set negative number since port is considered unimplemented + JSR (A1) Execute driver entry - TST.B MXUNIT(A5) jbgmF - BLE NODRIVE G[: hCu܂ + TST.B MXUNIT(A5) Verify number of units + BLE NODRIVE Error: No drive MOVE.B BDEVNO(A5),D0 MOVE.B MXUNIT(A5),D1 LEA FIRSTDRIVE(PC),A0 - ADD.B D0,(A0) JnhCu - ADD.B D1,D0 IhCu + ADD.B D0,(A0) Overwrite start drive name + ADD.B D1,D0 Overwrite stop drive name ADD.B D0,LASTDRIVE-FIRSTDRIVE(A0) - PEA MESSAGE_DRIVENAME(PC) : hCuA: + PEA MESSAGE_DRIVENAME(PC) Normal: Drive A: DOS _PRINT - PEA MESSAGE_DRIVENAME2(PC) : Z: + PEA MESSAGE_DRIVENAME2(PC) Normal: to Z: SUBQ.B #2,D1 BCS @F DOS _PRINT @@ ADDQ.L #8,SP - PEA MESSAGE_DRIVENAME3(PC) : o^܂ + PEA MESSAGE_DRIVENAME3(PC) Normal: was registered BRA QUIT NOTFOUND: - PEA MESSAGE_NOTFOUND(PC) G[: foCX݂܂ + PEA MESSAGE_NOTFOUND(PC) Error: Device not found BRA ABORT UNSUPPORTED: - PEA MESSAGE_UNSUPPORTED(PC) G[: Ή + PEA MESSAGE_UNSUPPORTED(PC) Error: Not supported BRA ABORT NODRIVE: - PEA MESSAGE_NODRIVE(PC) G[: hCu܂ + PEA MESSAGE_NODRIVE(PC) Error: No drive ABORT: MOVE.B #$0D,ERRLOW(A5) @@ -139,41 +139,41 @@ SECRET: DATA -*G[bZ[W +*Error messages MESSAGE_NOTFOUND: - DC.B 'foCX݂܂',$0D,$0A,0 + DC.B 'Device not found',$0D,$0A,0 MESSAGE_UNSUPPORTED: - DC.B 'Ή',$0D,$0A,0 + DC.B 'Not supported',$0D,$0A,0 MESSAGE_NODRIVE: - DC.B 'hCu܂',$0D,$0A,0 + DC.B 'No drive',$0D,$0A,0 -*o^bZ[W +*Registration messages MESSAGE_DRIVENAME: - DC.B 'hCu' + DC.B 'Drive' FIRSTDRIVE: DC.B 'A:',0 MESSAGE_DRIVENAME2: - DC.B '' + DC.B ' to ' LASTDRIVE: DC.B '@:',0 MESSAGE_DRIVENAME3: - DC.B 'o^܂',$0D,$0A,0 + DC.B ' was registered',$0D,$0A,0 -*^Cg +*Title MESSAGE_TITLE: DC.B $0D,$0A MESSAGE_TITLE2: DC.B 'RaSCSI FileSystem Driver version 1.42',$0D,$0A,0 -*肪ƂI +*Thanks! CREDIT: DC.B 'Coded by GIMONS',$0D,$0A DC.B 'Special thanks to',$0D,$0A DC.B 9,'co',$0D,$0A - DC.B 9,'ohD',$0D,$0A - DC.B 9,'ԁKZ',$0D,$0A + DC.B 9,'PI.',$0D,$0A + DC.B 9,'Tachibana@Kuwashima Giken',$0D,$0A DC.B 0 BSS diff --git a/src/x68k/RASDRV/RASDRV.TXT b/src/x68k/RASDRV/RASDRV.TXT deleted file mode 100644 index f3404ca2..00000000 --- a/src/x68k/RASDRV/RASDRV.TXT +++ /dev/null @@ -1,29 +0,0 @@ -RaSCSI t@CVXehCo -RASDRV version 1.42 - -Tv - -@{vOX68000@œ삵ĂHuman68kRaSCSĨubWfoCX - ʂRaspberry Pĩt@CVXe𒼐ڑ삷邽߂̃foCXhCołB -@X68000G~[^łXM6yXM6ȂтTypeGWindrvXMƓ̋@\ -@܂B - - - -@pɂRaspberry PiRaSCSIŃubWfoCXLɂKv܂B - ႦSCSI ID6ɃubWfoCXLɂꍇrascsi -ID6 BRIDGEƂ - w肪KvłB - -@ɃfoCXhCoRASCSI.SYS@̊‹ɃRs[܂BƂCONFIG.SYS - 㔼Ɉȉ̂悤ɋLqĂB - - DEVICE = \SYS\RASDRV.SYS - -@NIvVw肵ȂꍇRaspberry Pĩ[gfBNgz}Eg -@܂BɃfBNgw肷邱Ƃł̃fBNg}Egł܂B - Ⴆ/home/pi/app/home/pi/dataƂfBNg}Egꍇ - - DEVICE = \SYS\RASDRV.SYS /home/pi/app /home/pi/data - - ƋLq邱Ƃœ‚̃fBNgʁX̃hCuƂă}Eg”\łB - ̑̃IvVƂWindrvXM̂̂w”\łB diff --git a/src/x68k/RASETHER/RASETHER.DOC b/src/x68k/RASETHER/RASETHER.DOC deleted file mode 100644 index 5e37f13c..00000000 --- a/src/x68k/RASETHER/RASETHER.DOC +++ /dev/null @@ -1,42 +0,0 @@ -RaSCSIp EthernethCo -RASETHER version 1.20 - -Based on - Neptune-X board driver for Human-68k(ESP-X) version 0.03 - Programed 1996-7 by Shi-MAD. - Special thanks to Niggle, FIRST, yamapu ... - - ̃vO - - Shi-MAD ̍ꂽ Ethernet p ISA oXubW "Neptune-X" pfo - CXhCo ether_ne.sys ether_ne.sys ver0.03 +M01 +1 +L12 - QlɂĊJRaSCSIp̃C[T[lbgʐMhCołB - - g - - {Iɂ̓IWĩhCoƓ悤ɃfoCXhCoƂCONFIG.SYS - őgݍʼnB - -@j - DEVICE = \SYS\RASETHER.SYS - -@ȉ̃IvVw”\łB - -tx [x͐] : gptrapw肵܂Bx͂OUw”\łB - Aw肵trapgpłꍇ́Atrap #0 - 󂢂Ă鏊TA‚trap𗘗p - ܂B - -n : APItrapgp܂B - - RASETHERŊgIvV͈ȉ̂ƂłB - -px [x͐] : pPbgM̃|[OԊuw肵܂1ftHgłB - 1̏ꍇ͖16msɃ|[O܂B2,38܂Ŏw肪”\ - łB‘邲Ƃ16msԊu傫Ȃ܂B - - -ix [x͐] : |[OɎgp銄荞݂̃^Cvw肵܂B - ftHg0MFPGPIP荞(V-DISP)𗘗p܂B - 1w肷Timer-A荞݂gp܂B - - - ̃hCoɂ‚ - IWiether_ne.sysyщłether_ne.sys ver0.03 +M01 +1 +L12 - pKɏ]܂B diff --git a/src/x68k/RASETHER/main.c b/src/x68k/RASETHER/main.c index a6b6bb4a..17cf4bce 100644 --- a/src/x68k/RASETHER/main.c +++ b/src/x68k/RASETHER/main.c @@ -3,9 +3,9 @@ // SCSI Target Emulator RaSCSI (*^..^*) // for Raspberry Pi // -// Powered by XM6 TypeG Technorogy. +// Powered by XM6 TypeG Technology. // Copyright (C) 2016-2017 GIMONS -// [ RaSCSI C[T[lbg C ] +// [ RaSCSI Ethernet Main ] // // Based on // Neptune-X board driver for Human-68k(ESP-X) version 0.03 @@ -25,7 +25,7 @@ int trap_no; int num_of_prt; struct prt PRT_LIST[NPRT]; -// }`LXgiΉj +// Multicast (not supported) #ifdef MULTICAST int num_of_multicast; struct eaddr multicast_array[NMULTICAST]; @@ -45,7 +45,7 @@ static int sprint_eaddr(unsigned char* dst, void* e) } /************************************************ - * TRAP ngp”\ׂ * + * Check if TRAP n can be used * ************************************************/ static int is_valid_trap(int trap_no) { @@ -55,10 +55,10 @@ static int is_valid_trap(int trap_no) return 0; } - // gpǂ`FbN + // Check if in use addr = (unsigned int)_dos_intvcg(TRAP_VECNO(trap_no)); - // AhX̍ŏʃoCgɃxN^ԍĂΖgp + // Unused if the uppermost byte of the process address contains the vector number if (addr & 0xff000000) { return -1; } @@ -67,13 +67,13 @@ static int is_valid_trap(int trap_no) } /************************************************ - * gpTRAP n * + * Search for unused TRAP n * ************************************************/ static int search_trap_no(int def) { int i; - // defgp”\Ȃ炻Ɍ + // If def is usable, choose that if (is_valid_trap(def)) { return def; } @@ -96,8 +96,8 @@ static void* trap_vector(int trap_no, void *func) } /************************************************ - ֐ine.sCjVCYŌĂт܂j * - ************************************************/ + * Init Function (call with ne.s initialize) * + ************************************************/ int Initialize(void) { unsigned char buff[128]; @@ -106,27 +106,27 @@ int Initialize(void) if (SearchRaSCSI()) { - Print("RaSCSI Ether Adapter ݂̑mFł܂ł\r\n"); + Print("Could not locate the RaSCSI Ethernet Adapter\r\n"); return -1; } if (InitList(NPRT) || InitRaSCSI()) { - Print("RaSCSI Ether Adapter Driver ̏Ɏs܂\r\n"); + Print("Failed to initialize the RaSCSI Ethernet Adapter Driver\r\n"); return -1; } memset(ðer_addr, 0x00, sizeof(ether_addr)); GetMacAddr(ðer_addr); - // gptrapԍ𒲂ׂiwԍDj + // Inspect unused trap number (prioritize specified number) if (trap_no >= 0) { trap_no = search_trap_no(trap_no); } if (trap_no >= 0) { - // traptbN + // Hook the trap trap_vector(trap_no, (void*)trap_entry); sprintf(buff, " API trap number:%d ", trap_no); } else { @@ -141,14 +141,14 @@ int Initialize(void) sprintf(buff, "MAC Addr:%s\r\n", buff2); Print(buff); - // |[OJn + // Start polling RegisterIntProcess(poll_interval); return 0; } /************************************************ - * vgRXg * + * Initialize Protocol List * ************************************************/ int InitList(int n) { @@ -171,7 +171,7 @@ int InitList(int n) } /************************************************ - * MnhivgRjlj * + * Add Receive Handler (protocol) * ************************************************/ int AddList(int type, int_handler handler) { @@ -184,7 +184,7 @@ int AddList(int type, int_handler handler) result = -1; - // overwrite if alreay exist + // overwrite if already exist p = &PRT_LIST[0]; for (i = 0; i < NPRT; i++, p++) { if ((p->type == type && p->malloc != (malloc_func)-1) @@ -214,7 +214,7 @@ int AddList(int type, int_handler handler) } /************************************************ - * MnhivgRj폜 * + * Delete Receive Handler (protocol) * ************************************************/ int DeleteList(int type) { @@ -238,7 +238,7 @@ int DeleteList(int type) } /************************************************ - * MnhivgRjT[` * + * Search Receive Handler (protocol) * ************************************************/ int_handler SearchList(int type) { @@ -260,7 +260,7 @@ int_handler SearchList(int type) /************************************************ - * MnhivgRjT[` * + * Search Receive Handler (protocol) * ************************************************/ int_handler SearchList2(int type, int n) { diff --git a/src/x68k/RASETHER/main.h b/src/x68k/RASETHER/main.h index fa481a06..68a41cee 100644 --- a/src/x68k/RASETHER/main.h +++ b/src/x68k/RASETHER/main.h @@ -3,9 +3,9 @@ // SCSI Target Emulator RaSCSI (*^..^*) // for Raspberry Pi // -// Powered by XM6 TypeG Technorogy. +// Powered by XM6 TypeG Technology. // Copyright (C) 2016-2017 GIMONS -// [ RaSCSI C[T[lbg C ] +// [ RaSCSI Ethernet Main ] // // Based on // Neptune-X board driver for Human-68k(ESP-X) version 0.03 @@ -25,7 +25,7 @@ // number of multicast address we can handle **EDIT this** #define NMULTICAST (64) -/* A܂}`LXgɂ͑ΉĂȂ */ +/* However, multicast is not yet supported */ struct eaddr { unsigned char eaddr [6]; @@ -41,7 +41,7 @@ struct prt { }; -// O[oϐ +// Global variables extern unsigned int scsiid; extern int trap_no; extern int num_of_prt; @@ -53,7 +53,7 @@ extern struct eaddr multicast_array [NMULTICAST]; #endif -// vg^Cv錾 +// Prototype declarations extern int Initialize (void); extern int InitList (int); extern int AddList (int, int_handler); @@ -69,7 +69,7 @@ extern void MakeMulticastTable (unsigned char*); #endif -// ne.s ֐ +// Function within ne.s extern void trap_entry (void); diff --git a/src/x68k/RASETHER/re.s b/src/x68k/RASETHER/re.s index 2eba98a8..1ff0c65b 100644 --- a/src/x68k/RASETHER/re.s +++ b/src/x68k/RASETHER/re.s @@ -3,9 +3,9 @@ ** SCSI Target Emulator RaSCSI (*^..^*) ** for Raspberry Pi ** -** Powered by XM6 TypeG Technorogy. +** Powered by XM6 TypeG Technology. ** Copyright (C) 2016-2017 GIMONS -** [ RaSCSI C[T[lbg hCo ] +** [ RaSCSI Ethernet Driver ] ** ** Based on ** Neptune-X board driver for Human-68k(ESP-X) version 0.03 @@ -23,21 +23,21 @@ * Global Symbols ---------------------- * * -* bp O錾 +* For C language: external declarations * .xref _Initialize, _AddList, _SearchList, _DeleteList ;main.c .xref _GetMacAddr, _SetMacAddr ;scsictl.c .xref _SendPacket, _SetPacketReception ;scsictl.c -* .xref _AddMulticastAddr, _DelMulticastAddr ; +* .xref _AddMulticastAddr, _DelMulticastAddr ;not implemented * -* bp Oϐ +* For C language: external functions * - .xref _num_of_prt ;main.c o^vgR - .xref _trap_no ;gptrapio[ - .xref _trans_counter ;scsictl.c M/MoCg - .xref _intr_type ;scsictl.c 荞ݎ - .xref _poll_interval ;scsictl.c |[OԊu + .xref _num_of_prt ;main.c Number of registered protocols + .xref _trap_no ;Used trap number + .xref _trans_counter ;scsictl.c Number of send/receive bytes + .xref _intr_type ;scsictl.c Type of interrupt + .xref _poll_interval ;scsictl.c Polling interval * Text Section -------------------------------- * @@ -47,21 +47,21 @@ * -* foCXwb_[ +* Device Header * device_header: - .dc.l -1 ;N|C^[ + .dc.l -1 ;link pointer .dc $8000 ;device att. - .dc.l strategy_entry ;stategy entry - .dc.l interupt_entry ;interupt entry + .dc.l strategy_entry ;strategy entry + .dc.l interupt_entry ;interrupt entry .dc.b '/dev/en0' ;device name .dc.b 'EthD' ;for etherlib.a - .dc.b 'RASC' ;driver name (̌ɃGg[u) + .dc.b 'RASC' ;driver name (put in entry later) -* 'RASC' superjsr_entry ̊Ԃɂ͉uȂ +* Don not put anything between 'RASC' and superjsr_entry * -* C[ThCo ֐ Gg[ ( for DOS _SUPERJSR ) +* Ether Driver Function Entry ( for DOS _SUPERJSR ) * in: d0: command number * a0: args * @@ -79,11 +79,11 @@ superjsr_entry: bsr do_command movem.l (sp)+,d1-d7/a1-a7 - rts ;ʂ̃^[ + rts ;normal return * -* C[ThCo ֐ Gg[ ( for trap #n ) +* Ether Driver Function Entry ( for trap #n ) * in: d0: command number * a0: args * @@ -101,51 +101,51 @@ _trap_entry:: bsr do_command movem.l (sp)+,d1-d7/a1-a7 - rte ;荞݃^[ + rte ;interrupt return * -* eӂ킯 +* Assign each command * do_command: moveq #FUNC_MIN,d1 cmp.l d0,d1 - bgt error ;d0<-2 Ȃ疢ΉR}hԍ + bgt error ;d0<-2 is an unsupported command number moveq #FUNC_MAX,d1 cmp.l d1,d0 - bgt error ;9) + .dc get_statistics-jumptable ;09 ... (Read out statistics ) FUNC_MAX: .equ ($-jumptable)/2-1 * -* R}h -2: MJE^̃AhXԂ +* Command -2: Return transfer counter address * return: address * get_cnt_addr: @@ -155,7 +155,7 @@ get_cnt_addr: * -* R}h -1: gptrapԍԂ +* Command -1: Return used trap number * return: trap number to use (-1:use SUPERJSR) * driver_entry: @@ -166,13 +166,13 @@ driver_entry: *mesff: .dc.b 'DriverEntry',13,10,0 * .text - move.l (_trap_no,pc),d0 ;trap_no ... main.c ϐ + move.l (_trap_no,pc),d0 ;trap_no ... main.c variable rts * -* R}h 00: hCo[̃o[WԂ -* return: version number (... ver1.00 Ȃ 100 Ԃ) +* Command 00: Return driver version +* return: version number (Ex... for ver1.00 return 100) * get_driver_version: * pea (mes00,pc) @@ -187,7 +187,7 @@ get_driver_version: * -* R}h 01: ݂ MAC AhX̎擾 +* Command 01: Get current MAC address * return: same as *dst * get_mac_addr: @@ -200,13 +200,13 @@ get_mac_addr: pea (a0) pea (a0) - bsr _GetMacAddr ;scsictl.c ֐ + bsr _GetMacAddr ;scsictl.c function addq.l #4,sp - move.l (sp)+,d0 ; a0 d0 ɂ̂܂ܕԂ + move.l (sp)+,d0 ;Return d0 for parameter a0 rts * -* R}h 02: EEPROM ɏꂽ MAC AhX̎擾 +* Command 02: Get MAC address written to EEPROM * return: same as *dst * get_prom_addr: @@ -219,13 +219,13 @@ get_prom_addr: pea (a0) pea (a0) - bsr _GetMacAddr ;scsictl.c ֐ + bsr _GetMacAddr ;scsictl.c function addq.l #4,sp - move.l (sp)+,d0 ; a0 d0 ɂ̂܂ܕԂ + move.l (sp)+,d0 ;Return d0 for parameter a0 rts * -* R}h 03: MACAhX̐ݒ +* Command 03: Set MAC address * return: 0 (if no errors) * set_mac_addr: @@ -237,13 +237,13 @@ set_mac_addr: * .text pea (a0) - bsr _SetMacAddr ;scsictl.c ֐ + bsr _SetMacAddr ;scsictl.c function addq.l #4,sp rts * -* R}h 04: pPbgM +* Command 04: Send packet * packet contents: * Distination MAC: 6 bytes * Source(own) MAC: 6 bytes @@ -256,10 +256,10 @@ send_packet: * DOS _PRINT * addq.l #4,sp - move.l (a0)+,d0 ;pPbgTCY - move.l (a0),-(sp) ;pPbgAhX + move.l (a0)+,d0 ;packet size + move.l (a0),-(sp) ;packet address move.l d0,-(sp) - bsr _SendPacket ;scsictl.c ֐ + bsr _SendPacket ;scsictl.c function addq.l #8,sp * move.l d0,-(sp) @@ -269,14 +269,14 @@ send_packet: * move.l (sp)+,d0 * .data *mes04: .dc.b 13,10,'SendPacket,13,10',0 -*mes04e:.dc.b 13,10,'SendPacket',13,10,0 +*mes04e:.dc.b 13,10,'SendPacket finished',13,10,0 * .text rts * -* R}h 05: M荞݃nhljEݒ +* Command 05: Set / add receive interrupt handler * type: 0x00000800 IP packet * 0x00000806 ARP packet * return: 0 (if no errors) @@ -289,28 +289,28 @@ set_int_addr: *mes05: .dc.b 'SetIntAddr',13,10,0 * .text - move.l (a0)+,d0 ;vgRԍ - move.l (a0),-(sp) ;nh֐̃AhX + move.l (a0)+,d0 ;protocol number + move.l (a0),-(sp) ;address to handler function move.l d0,-(sp) - bsr _AddList ;main.c ֐ + bsr _AddList ;main.c function addq.l #8,sp tst.l d0 - bmi set_int_addr_rts ;o^s + bmi set_int_addr_rts ;Registration failed - cmpi.l #1,(_num_of_prt) ;nhPȂ犄荞݋‚ + cmpi.l #1,(_num_of_prt) ;Permit interrupt if number of handlers is 1 bne set_int_addr_rts - pea (1) ;1=<> - bsr _SetPacketReception ;荞݋ ... scsictl.c + pea (1) ;1= + bsr _SetPacketReception ;interrupt permitted ... scsictl.c addq.l #4,sp -* moveq #0,d0 ;SetPacketReception() ŏ 0 Ԃ̂ŏȗ +* moveq #0,d0 ;SetPacketReception() always returns 0 so bypass set_int_addr_rts: rts * -* R}h 06: 荞݃nh̃AhX擾 +* Command 06: Get interrupt handler and address * return: interupt address * get_int_addr: @@ -328,7 +328,7 @@ get_int_addr: * -* R}h 07: 荞݃nh̍폜 +* Command 07: Delete interrupt handler * return: 0 (if no errors) * del_int_addr: @@ -340,24 +340,24 @@ del_int_addr: * .text pea (a0) - bsr _DeleteList ;main.c ֐ + bsr _DeleteList ;main.c function move.l d0,(sp)+ - bmi del_int_addr_ret ;폜s + bmi del_int_addr_ret ;Delete failed - tst.l (_num_of_prt) ;nh‚ȂȂΊ荞݂֎~ + tst.l (_num_of_prt) ;Forbid interrupts if handlers are gone bne del_int_addr_ret - clr.l -(sp) ;0=<֎~> - bsr _SetPacketReception ;荞݋֎~ ... scsictl.c + clr.l -(sp) ;0= + bsr _SetPacketReception ;Interrupt forbitten ... scsictl.c addq.l #4,sp -* moveq #0,d0 ;SetPacketReception() ŏ 0 Ԃ̂ŏȗ +* moveq #0,d0 ;SetPacketReception() always returns 0 so bypass del_int_addr_ret: rts * -* R}h 08: }`LXgAhX̐ݒ +* Command 08: Set multicast address * set_multicast_addr: * pea (mes08,pc) @@ -372,7 +372,7 @@ set_multicast_addr: * -* R}h 09: vǂݏo +* Command 09: Read out statistics * get_statistics: * pea (mes09,pc) @@ -386,7 +386,7 @@ get_statistics: rts * -* foCXhCoGg[ +* Device Driver Entry * strategy_entry: move.l a5,(request_buffer) @@ -394,7 +394,7 @@ strategy_entry: interupt_entry: - move.l sp,(stack_buff) ;ÕX^bNGAg + move.l sp,(stack_buff) ;Use own stack area lea (def_stack),sp ; movem.l d1-d7/a0-a5,-(sp) @@ -481,13 +481,13 @@ opt_i: bra opt_loop arg_end: - bsr _Initialize ;main.c ֐ - ;I/OAhXݒ - ;MACAhX擾 - ;vgRXg - ;SCSICTL - ;荞݃nhixN^ݒj - ;trapT[rXixN^ݒj + bsr _Initialize ;main.c function + ;Set I/O address + ;Get MAC address + ;Init protocol list + ;Init SCSICTL + ;Interrupt handler (set vector) + ;trap service (set vector) tst.l d0 bne errorret @@ -508,7 +508,7 @@ intret: move.b d0,(3,a5) movem.l (sp)+,d1-d7/a0-a5 - movea.l (stack_buff,pc),sp ;X^bN|C^ɂǂ + movea.l (stack_buff,pc),sp ;Restore stack pointer rts get_num: @@ -539,7 +539,7 @@ mestitle: .dc.b 'RaSCSI Ethernet Driver version 1.20 / Based on ether_ne.sys+M01L12',13,10 .dc.b 0 mesparam_err: - .dc.b 'p[^ُł',13,10,0 + .dc.b 'Invalid parameter',13,10,0 .even @@ -562,7 +562,7 @@ stack_buff_i: .quad * -* X^bNGA +* Stack area * .ds.b 1024*8 def_stack: diff --git a/src/x68k/RASETHER/scsictl.c b/src/x68k/RASETHER/scsictl.c index 8d30dc6d..9051710b 100644 --- a/src/x68k/RASETHER/scsictl.c +++ b/src/x68k/RASETHER/scsictl.c @@ -3,9 +3,9 @@ // SCSI Target Emulator RaSCSI (*^..^*) // for Raspberry Pi // -// Powered by XM6 TypeG Technorogy. +// Powered by XM6 TypeG Technology. // Copyright (C) 2016-2017 GIMONS -// [ RaSCSI C[T[lbg SCSI䕔 ] +// [ RaSCSI Ethernet SCSI Control Department ] // // Based on // Neptune-X board driver for Human-68k(ESP-X) version 0.03 @@ -47,24 +47,24 @@ typedef struct #define MFP_IERB 0xe88009 #define MFP_IMRB 0xe88015 -// asmsub.s ̃Tu[` +// Subroutine in asmsub.s extern void DI(); extern void EI(); -volatile short* iocsexec = (short*)0xa0e; // IOCSs[N -struct trans_counter trans_counter; // MJE^ -unsigned char rx_buff[2048]; // Mobt@ -int imr; // 荞݋ƒtO -int scsistop; // SCSI~tO -int intr_type; // 荞ݎ(0:V-DISP 1:TimerA) -int poll_interval; // |[OԊu(ݒ) -int poll_current; // |[OԊu() -int idle; // AChJE^ +volatile short* iocsexec = (short*)0xa0e; // Work when executin IOCS +struct trans_counter trans_counter; // Transfer counter +unsigned char rx_buff[2048]; // Receive buffer +int imr; // Interrupt permission flag +int scsistop; // SCSI stopped flag +int intr_type; // Interrupt type (0:V-DISP 1:TimerA) +int poll_interval; // Polling interval (configure) +int poll_current; // Polling interval (current) +int idle; // Idle counter #define POLLING_SLEEP 255 // 4-5s /************************************************ - * MACAhX擾ߔs * + * Execute command to get MAC address * ************************************************/ int SCSI_GETMACADDR(unsigned char *mac) { @@ -105,7 +105,7 @@ int SCSI_GETMACADDR(unsigned char *mac) } /************************************************ - * MACAhXݒ薽ߔs * + * Execute command to configure MAC address * ************************************************/ int SCSI_SETMACADDR(const unsigned char *mac) { @@ -144,7 +144,7 @@ int SCSI_SETMACADDR(const unsigned char *mac) } /************************************************ - * pPbgMTCY擾ߔs * + * Execute command to get received packet size* ************************************************/ int SCSI_GETPACKETLEN(int *len) { @@ -188,7 +188,7 @@ int SCSI_GETPACKETLEN(int *len) } /************************************************ - * pPbgMߔs * + * Execute receive packet command * ************************************************/ int SCSI_GETPACKETBUF(unsigned char *buf, int len) { @@ -229,7 +229,7 @@ int SCSI_GETPACKETBUF(unsigned char *buf, int len) } /************************************************ - * pPbgMߔs * + * Execute packet send command * ************************************************/ int SCSI_SENDPACKET(const unsigned char *buf, int len) { @@ -267,7 +267,7 @@ int SCSI_SENDPACKET(const unsigned char *buf, int len) } /************************************************ - * MACAhX擾 * + * Get MAC address * ************************************************/ int GetMacAddr(struct eaddr* buf) { @@ -279,7 +279,7 @@ int GetMacAddr(struct eaddr* buf) } /************************************************ - * MACAhXݒ * + * Set MAC address * ************************************************/ int SetMacAddr(const struct eaddr* data) { @@ -291,7 +291,7 @@ int SetMacAddr(const struct eaddr* data) } /************************************************ - * RaSCSI * + * Search RaSCSI * ************************************************/ int SearchRaSCSI() { @@ -299,7 +299,7 @@ int SearchRaSCSI() INQUIRYOPT_T inq; for (i = 0; i <= 7; i++) { - // BRIDGEfoCX + // Search for BRIDGE device if (S_INQUIRY(sizeof(INQUIRY_T) , i, (struct INQUIRY*)&inq) < 0) { continue; } @@ -308,7 +308,7 @@ int SearchRaSCSI() continue; } - // TAPԂ擾 + // Get TAP initialization status if (S_INQUIRY(sizeof(INQUIRYOPT_T) , i, (struct INQUIRY*)&inq) < 0) { continue; } @@ -317,7 +317,7 @@ int SearchRaSCSI() continue; } - // SCSI IDm + // Configure SCSI ID scsiid = i; return 0; } @@ -326,7 +326,7 @@ int SearchRaSCSI() } /************************************************ - * RaSCSI ֐ * + * Init RaSCSI method * ************************************************/ int InitRaSCSI(void) { @@ -343,7 +343,7 @@ int InitRaSCSI(void) } /************************************************ - * RaSCSI 荞ݏ ֐(|[O) * + * RaSCSI interrupt handler function (polling) * ************************************************/ void interrupt IntProcess(void) { @@ -353,78 +353,78 @@ void interrupt IntProcess(void) int_handler func; int i; - // V-DISP GPIP荞݂̓AChJE^Ő + // V-DISP GPIP interrupt idle count control if (intr_type == 0) { - // AChZ + // Increment idle idle++; - // ̏\ɓBĂȂȂXLbv + // Skip if not yet next scheduled processing if (idle < poll_current) { return; } - // AChJE^NA + // Clear idle counter idle = 0; } - // 荞݊Jn + // Start interrupt - // 荞݋‚̎ + // Only when interrupt is permitted if (imr == 0) { return; } - // IOCSsȂ΃XLbv + // Skip if executing IOCS if (*iocsexec != -1) { return; } - // M͊荞݋֎~ + // Interrupt forbidden if receiving data DI (); - // oXt[̎ + // Only in bus free phase phase = S_PHASE(); if (phase != 0) { - // I + // Exit goto ei_exit; } - // M + // Receive data if (SCSI_GETPACKETLEN(&len) == 0) { - // RaSCSI~ + // RaSCSI is stopped scsistop = 1; - // |[OԊu̍Đݒ(Q) + // Reset polling interval (sleep) UpdateIntProcess(POLLING_SLEEP); - // I + // Exit goto ei_exit; } - // RaSCSI͓쒆 + // RaSCSI is stopped if (scsistop) { scsistop = 0; - // |[OԊu̍Đݒ(}) + // Reset polling interval (hurry) UpdateIntProcess(poll_interval); } - // pPbg͓ĂȂ + // Packets did not arrive if (len == 0) { - // I + // Exit goto ei_exit; } - // Mobt@փpPbg] + // Tranfer packets to receive buffer memory if (SCSI_GETPACKETBUF(rx_buff, len) == 0) { - // s + // Fail goto ei_exit; } - // 荞݋ + // Interrupt permitted EI (); - // pPbg^CvŃf[^ + // Split data by packet type type = rx_buff[12] * 256 + rx_buff[13]; i = 0; while ((func = SearchList2(type, i))) { @@ -435,12 +435,12 @@ void interrupt IntProcess(void) return; ei_exit: - // 荞݋ + // Interrupt permitted EI (); } /************************************************ - * RaSCSI pPbgM ֐ (ne.s) * + * RaSCSI Send Packets Function (from ne.s) * ************************************************/ int SendPacket(int len, const unsigned char* data) { @@ -449,35 +449,35 @@ int SendPacket(int len, const unsigned char* data) } if (len > 1514) { // 6 + 6 + 2 + 1500 - return -1; // G[ + return -1; // Error } - // RaSCSI~̂悤ȂG[ + // If RaSCSI seems to be stopped, throw an error if (scsistop) { return -1; } - // M͊荞݋֎~ + // Interrupt is not permitted during sending DI (); - // MƑMtOAbv + // Send processing and raise send flag if (SCSI_SENDPACKET(data, len) == 0) { - // 荞݋ + // Interrupt permitted EI (); return -1; } - // 荞݋ + // Interrupt permitted EI (); - // M˗ς + // Finished requesting send trans_counter.send_byte += len; return 0; } /************************************************ - * RaSCSI 荞݋Asݒ ֐ * + * RaSCSI Interrupt Permission Setting Function* ************************************************/ int SetPacketReception(int i) { @@ -486,19 +486,19 @@ int SetPacketReception(int i) } /************************************************ - * RaSCSI 荞ݏo^ * + * RaSCSI Interrupt Processing Registration * ************************************************/ void RegisterIntProcess(int n) { volatile unsigned char *p; - // |[OԊu()̍XVƃAChJE^NA + // Update polling interval (current) and clear idle counter poll_current = n; idle = 0; if (intr_type == 0) { - // V-DISP GPIP荞݃xN^ - // 荞݂Lɂ + // Overwrite V-DISP GPIP interrupt vectors + // and enable interrupt B_INTVCS(0x46, (int)IntProcess); p = (unsigned char *)MFP_AEB; *p = *p | 0x10; @@ -507,28 +507,28 @@ void RegisterIntProcess(int n) p = (unsigned char *)MFP_IMRB; *p = *p | 0x40; } else if (intr_type == 1) { - // TimerA̓JEg[hݒ + // Set TimerA counter mode VDISPST(NULL, 0, 0); VDISPST(IntProcess, 0, poll_current); } } /************************************************ - * RaSCSI 荞ݏύX * + * RaSCSI Interrupt Processing Update * ************************************************/ void UpdateIntProcess(int n) { - // |[OԊu()ƓȂXVȂ + // Do not update if polling interval (current) is the same if (n == poll_current) { return; } - // |[OԊu()̍XVƃAChJE^NA + // Update polling interval (current) and clear idle counter poll_current = n; idle = 0; if (intr_type == 1) { - // TimerA͍ēo^Kv + // TimerA requires re-registering VDISPST(NULL, 0, 0); VDISPST(IntProcess, 0, poll_current); } diff --git a/src/x68k/RASETHER/scsictl.h b/src/x68k/RASETHER/scsictl.h index ae6bf477..a13ea178 100644 --- a/src/x68k/RASETHER/scsictl.h +++ b/src/x68k/RASETHER/scsictl.h @@ -3,9 +3,9 @@ // SCSI Target Emulator RaSCSI (*^..^*) // for Raspberry Pi // -// Powered by XM6 TypeG Technorogy. +// Powered by XM6 TypeG Technology. // Copyright (C) 2016-2017 GIMONS -// [ RaSCSI C[T[lbg SCSI䕔 ] +// [ RaSCSI Ethernet SCSI Control Department ] // // Based on // Neptune-X board driver for Human-68k(ESP-X) version 0.03 @@ -17,11 +17,11 @@ #ifndef scsictl_h #define scsictl_h -// O[oϐ +// Global variables extern int intr_type; extern int poll_interval; -// MJE^ +// Transfer counter struct trans_counter { unsigned int send_byte; unsigned int recv_byte; diff --git a/test/robot/Resources/rascsi_utils.resource b/test/robot/Resources/rascsi_utils.resource index 9e12261d..26d68627 100644 --- a/test/robot/Resources/rascsi_utils.resource +++ b/test/robot/Resources/rascsi_utils.resource @@ -122,3 +122,35 @@ Get checksum of ${filename} from ISO ${image_name} on the RaSCSI Host Execute Command rm -rf /tmp/${temp_dirname} sudo=True sudo_password=${Rascsi_Password} [Return] ${checksum} +The RaSCSI service is configured to use port ${port_number} + [Documentation] Modifies the rascsi.service systemd configuration file to use the specified port + ... instead of the default. This will erase any other options that have been previously + ... set in your rascsi.service file. + Execute command sed -i 's/^ExecStart=.*/ExecStart=\\\/usr\\\/local\\\/bin\\\/rascsi -p ${port_number}/' /etc/systemd/system/rascsi.service sudo=True sudo_password=${Rascsi_Password} + Execute command systemctl daemon-reload sudo=True sudo_password=${Rascsi_Password} + ${service_file}= Execute command cat /etc/systemd/system/rascsi.service + log ${service_file} + +The RaSCSI service is configured to use default port + [Documentation] Restore the rascsi.service systemd configuration file to use the default rascsi + ... launch command + Execute command sed -i 's/^ExecStart=.*/ExecStart=\\\/usr\\\/local\\\/bin\\\/rascsi/' /etc/systemd/system/rascsi.service sudo=True sudo_password=${Rascsi_Password} + Execute command systemctl daemon-reload sudo=True sudo_password=${Rascsi_Password} + ${service_file}= Execute command cat /etc/systemd/system/rascsi.service + log ${service_file} + +RASCTL should connect on port ${port_number} + ${stdout_out} ${stderr_out}= Execute Command rasctl -l -p ${port_number} return_stderr=True + log ${stdout_out} + log ${stderr_out} + Should Contain ${stdout_out} No images currently attached + +RASCTL should not connect on port ${port_number} + ${stdout_out} ${stderr_out}= Execute Command rasctl -l -p ${port_number} return_stderr=True + log ${stdout_out} + log ${stderr_out} + Should Contain ${stderr_out} Error: Can't connect to rascsi process on host + +Kill all rascsi processes + ${result}= Execute command killall -9 rascsi sudo=True sudo_password=${Rascsi_Password} + log ${result} \ No newline at end of file diff --git a/test/robot/rasctl_options.robot b/test/robot/rasctl_options.robot new file mode 100644 index 00000000..6df7bbe6 --- /dev/null +++ b/test/robot/rasctl_options.robot @@ -0,0 +1,33 @@ +*** Settings *** +Documentation Test that the RaSCSI can be started on a configurable TCP port. +Library OperatingSystem +Resource Resources/rascsi_utils.resource +Resource Resources/linux_services.resource + +Suite Setup Open Connection to Rascsi and Log In +Suite Teardown Close All Connections + +*** Test Cases *** +RASCTL Works on the Default Port + [Documentation] Check that RaSCSI starts with the default port + [Teardown] Run Keywords Kill all rascsi processes The Rascsi service is Restarted + When The RaSCSI service is configured to use default port + And The RaSCSI service is Restarted + Then rasctl should connect on port 6868 + And rasctl should not connect on port 9999 + +RASCTL Works on User Specified Port 9999 + [Documentation] Check that RaSCSI works with a user specified port "9999" + [Teardown] Run Keywords The RaSCSI service is configured to use default port The Rascsi service is Restarted + When The RaSCSI service is configured to use port 9999 + And The RaSCSI service is Restarted + Then rasctl should connect on port 9999 + And rasctl should not connect on port 6868 + +RASCTL Works on User Specified Port 6869 + [Documentation] Check that RaSCSI works with a user specified port DEFAULT + 1 + [Teardown] Run Keywords The RaSCSI service is configured to use default port The Rascsi service is Restarted + When The RaSCSI service is configured to use port 6869 + And The RaSCSI service is Restarted + Then rasctl should connect on port 6869 + And rasctl should not connect on port 6868