Merge tag 'v21.10.01'

October 2021 Release
This commit is contained in:
Tony Kuker 2021-10-29 23:41:37 -05:00
commit cf9a578c82
171 changed files with 16853 additions and 13650 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: akuker

View File

@ -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/*

4
.gitignore vendored
View File

@ -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/
*~

View File

@ -2,7 +2,8 @@ BSD 3-Clause License
Copyright (C) 2001-2006 (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

Binary file not shown.

View File

@ -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)
<a href="https://scan.coverity.com/projects/akuker-rascsi">
<img alt="Coverity Scan Build Status"
src="https://scan.coverity.com/projects/21656/badge.svg"/>
</a>
# 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)!

View File

@ -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_DT0PIN_SEL:それぞれSCSI信号のピン番号です。
□コンパイル方法
・実行ファイル(rascsi,rasctl)
gpiobus.hを修正
make clean
make
[EOF]

View File

@ -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]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

View File

@ -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: <https://www.github.com/akuker/RASCSI/wiki/>

View File

@ -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デバイス(ハードディスクMOCD-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:07
HD指定の場合(X68000 SASI機のHD指定互換)
rascsi [-HDn FILE] ...
n:015
ルート権限が必要ですので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(07)
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バイト単位であれば10M512Mの任意のファイルサイズがマウント可能です。
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]

View File

@ -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]

View File

@ -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: <https://www.github.com/akuker/RASCSI/wiki/>
Full documentation is available at:
<https://www.github.com/akuker/RASCSI/wiki/>
rascsi(1)
rascsi(1)

View File

@ -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: <https://www.github.com/akuker/RASCSI/wiki/>

View File

@ -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: <https://www.github.com/akuker/RASCSI/wiki/>
Full documentation is available at:
<https://www.github.com/akuker/RASCSI/wiki/>
rascsi(1)
rascsi(1)

35
doc/rasdump.1 Normal file
View File

@ -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: <https://www.github.com/akuker/RASCSI/wiki/>

41
doc/sasidump.1 Normal file
View File

@ -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: <https://www.github.com/akuker/RASCSI/wiki/>

View File

@ -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: <https://www.github.com/akuker/RASCSI/wiki/>

View File

@ -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: <https://www.github.com/akuker/RASCSI/wiki/>
Full documentation is available at:
<https://www.github.com/akuker/RASCSI/wiki/>
scsimon(1)
scsimon(1)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

View File

@ -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]

View File

@ -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]

View File

@ -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 </dev/null
}
# install all dependency packages for RaSCSI Service
# compile and install RaSCSI Service
function installRaScsi() {
installPackages
sudo apt-get update && sudo apt-get install --yes git libspdlog-dev
stopRaScsiScreen
stopRaScsi
cd ~/RASCSI/src/raspberrypi
make all CONNECT_TYPE=FULLSPEC
sudo make install CONNECT_TYPE=FULLSPEC
if [ -f /etc/systemd/system/rascsi.service ]; then
sudo cp /etc/systemd/system/rascsi.service /etc/systemd/system/rascsi.service.old
SYSTEMD_BACKUP=true
echo "Existing version of rascsi.service detected; Backing up to rascsi.service.old"
else
SYSTEMD_BACKUP=false
fi
sudoIsReady=$(sudo grep -c "rascsi" /etc/sudoers)
cd "$BASE/src/raspberrypi" || exit 1
if [ $sudoIsReady = "0" ]; then
( make clean && make all CONNECT_TYPE="${CONNECT_TYPE-FULLSPEC}" && sudo make install CONNECT_TYPE="${CONNECT_TYPE-FULLSPEC}" ) </dev/null
if [[ `sudo grep -c "rascsi" /etc/sudoers` -eq 0 ]]; then
sudo bash -c 'echo "
# Allow the web server to restart the rascsi service
www-data ALL=NOPASSWD: /bin/systemctl restart rascsi.service
@ -64,14 +102,123 @@ www-data ALL=NOPASSWD: /bin/systemctl stop rascsi.service
# Allow the web server to reboot the raspberry pi
www-data ALL=NOPASSWD: /sbin/shutdown, /sbin/reboot
" >> /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
if [ -f "$BASE/src/oled_monitor/rascsi_interface_pb2.py" ]; then
rm "$BASE/src/oled_monitor/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="$BASE/src/oled_monitor" rascsi_interface.proto
if [[ $(grep -c "^dtparam=i2c_arm=on" /boot/config.txt) -ge 1 ]]; then
echo "NOTE: I2C support seems to have been configured already."
REBOOT=0
else
sudo raspi-config nonint do_i2c 0 </dev/null
echo "Modified the Raspberry Pi boot configuration to enable I2C."
echo "A reboot will be required for the change to take effect."
REBOOT=1
fi
echo "Installing the monitor_rascsi.service configuration..."
sudo cp -f "$BASE/src/oled_monitor/monitor_rascsi.service" /etc/systemd/system/monitor_rascsi.service
sudo sed -i /^ExecStart=/d /etc/systemd/system/monitor_rascsi.service
sudo sed -i "8 i ExecStart=$BASE/src/oled_monitor/start.sh --rotation=$ROTATION" /etc/systemd/system/monitor_rascsi.service
sudo systemctl daemon-reload
sudo systemctl enable monitor_rascsi
if [ $REBOOT -eq 1 ]; then
echo ""
echo "The monitor_rascsi service will start on the next Pi boot."
echo "Press Enter to reboot or CTRL-C to exit"
read
echo "Rebooting..."
sleep 3
sudo reboot
fi
sudo systemctl start monitor_rascsi
}
function createImagesDir() {
if [ -d "$VIRTUAL_DRIVER_PATH" ]; then
echo "The $VIRTUAL_DRIVER_PATH directory already exists."
else
echo "The $VIRTUAL_DRIVER_PATH directory does not exist; creating..."
mkdir -p "$VIRTUAL_DRIVER_PATH"
chmod -R 775 "$VIRTUAL_DRIVER_PATH"
fi
if [ -d "$CFG_PATH" ]; then
echo "The $CFG_PATH directory already exists."
else
echo "The $CFG_PATH directory does not exist; creating..."
mkdir -p "$CFG_PATH"
chmod -R 775 "$CFG_PATH"
fi
}
function stopOldWebInterface() {
stopRaScsiWeb
APACHE_STATUS=$(sudo systemctl status apache2 &> /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 </dev/null
fi
if [ ! -x $HFDISK_BIN ]; then
# Clone, compile and install 'hfdisk', partition tool
git clone git://www.codesrc.com/git/hfdisk.git
cd hfdisk
cd hfdisk || exit 1
make
sudo cp hfdisk /usr/bin/hfdisk
fi
# Inject hfdisk commands to create Drive with correct partitions
(echo i; echo ; echo C; echo ; echo 32; echo "Driver_Partition"; echo "Apple_Driver"; echo C; echo ; echo ; echo "${volumeName}"; echo "Apple_HFS"; echo w; echo y; echo p;) | $HFDISK_BIN "$diskPath"
# https://www.codesrc.com/mediawiki/index.php/HFSFromScratch
# i initialize partition map
# continue with default first block
# C Create 1st partition with type specified next)
# continue with default
# 32 32 blocks (required for HFS+)
# Driver_Partition Partition Name
# Apple_Driver Partition Type (available types: Apple_Driver, Apple_Driver43, Apple_Free, Apple_HFS...)
# C Create 2nd partition with type specified next
# continue with default first block
# continue with default block size (rest of the disk)
# ${volumeName} Partition name provided by user
# Apple_HFS Partition Type
# w Write partition map to disk
# y Confirm partition table
# p Print partition map
(echo i; echo ; echo C; echo ; echo 32; echo "Driver_Partition"; echo "Apple_Driver"; echo C; echo ; echo ; echo "${volumeName}"; echo "Apple_HFS"; echo w; echo y; echo p;) | $HFDISK_BIN "$diskPath"
partitionOk=$?
if [ $partitionOk -eq 0 ]; then
if [ ! -f $LIDO_DRIVER ];then
echo "Lido driver couldn't be found. Make sure RASCSI is up-to-date with git pull"
if [ ! -f "$LIDO_DRIVER" ];then
echo "Lido driver couldn't be found. Make sure RaSCSI is up-to-date with git pull"
return 1
fi
# Burn Lido driver to the disk
dd if=$LIDO_DRIVER of="$diskPath" seek=64 count=32 bs=512 conv=notrunc
dd if="$LIDO_DRIVER" of="$diskPath" seek=64 count=32 bs=512 conv=notrunc
driverInstalled=$?
if [ $driverInstalled -eq 0 ]; then
@ -216,12 +381,12 @@ function createDrive() {
driveSize=$1
driveName=$2
mkdir -p $VIRTUAL_DRIVER_PATH
drivePath="${VIRTUAL_DRIVER_PATH}/${driveSize}MB.hda"
if [ ! -f $drivePath ]; then
mkdir -p "$VIRTUAL_DRIVER_PATH"
drivePath="${VIRTUAL_DRIVER_PATH}/${driveSize}MB.hds"
if [ ! -f "$drivePath" ]; then
echo "Creating a ${driveSize}MB Drive"
dd if=/dev/zero of=$drivePath bs=1M count=$driveSize
truncate --size "${driveSize}m" "$drivePath"
echo "Formatting drive with HFS"
formatDrive "$drivePath" "$driveName"
@ -231,74 +396,364 @@ function createDrive() {
fi
}
function setupWiredNetworking() {
echo "Setting up wired network..."
LAN_INTERFACE=eth0
echo "$LAN_INTERFACE will be configured for network forwarding with DHCP."
echo ""
echo "WARNING: If you continue, the IP address of your Pi may change upon reboot."
echo "Please make sure you will not lose access to the Pi system."
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 wired interfaces on this system:"
echo `ip -o addr show scope link | awk '{split($0, a); print $2}' | grep eth`
echo "Please type the wired interface you want to use and press Enter:"
read SELECTED
LAN_INTERFACE=$SELECTED
fi
if [ "$(grep -c "^denyinterfaces" /etc/dhcpcd.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
sudo sed -i /^denyinterfaces/d /etc/dhcpcd.conf
fi
sudo bash -c 'echo "denyinterfaces '$LAN_INTERFACE'" >> /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 </dev/null
fi
echo "Modified /etc/iptables/rules.v4"
echo "Configuration completed!"
echo ""
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 $WLAN_INTERFACE:$ROUTER_IP/$CIDR"
echo ""
echo "We need to reboot your Pi"
echo "Press Enter to reboot or CTRL-C to exit"
read REPLY
echo "Rebooting..."
sleep 3
sudo reboot
}
function reserveScsiIds() {
sudo systemctl stop rascsi
echo "WARNING: This will override any existing modifications to rascsi.service!"
echo "Please type the SCSI ID(s) that you want to reserve and press Enter:"
echo "The input should be numbers between 0 and 7 separated by commas, e.g. \"0,1,7\" for IDs 0, 1, and 7."
echo "Leave empty to make all IDs available."
read -r RESERVED_IDS
if [[ $RESERVED_IDS = "" ]]; then
sudo sed -i /^ExecStart=/d /etc/systemd/system/rascsi.service
sudo sed -i "8 i ExecStart=/usr/local/bin/rascsi" /etc/systemd/system/rascsi.service
else
sudo sed -i /^ExecStart=/d /etc/systemd/system/rascsi.service
sudo sed -i "8 i ExecStart=/usr/local/bin/rascsi -r $RESERVED_IDS" /etc/systemd/system/rascsi.service
fi
echo "Modified /etc/systemd/system/rascsi.service"
sudo systemctl daemon-reload
sudo systemctl start rascsi
}
function installNetatalk() {
NETATALK_VERSION="20200806"
AFP_SHARE_PATH="$HOME/afpshare"
echo "Cleaning up existing Netatalk installation, if it exists..."
sudo /etc/init.d/netatalk stop || true
sudo rm -rf /etc/default/netatalk.conf /etc/netatalk || true
if [ -f "$HOME/netatalk-classic-$NETATALK_VERSION" ]; then
echo "Deleting existing version of $HOME/netatalk-classic-$NETATALK_VERSION."
sudo rm -rf "$HOME/netatalk-classic-$NETATALK_VERSION"
fi
echo "Downloading netatalk-classic-$NETATALK_VERSION to $HOME"
cd $HOME || exit 1
wget "https://github.com/christopherkobayashi/netatalk-classic/archive/refs/tags/$NETATALK_VERSION.tar.gz" </dev/null
tar -xzvf $NETATALK_VERSION.tar.gz
cd "netatalk-classic-$NETATALK_VERSION" || exit 1
sed -i /^~/d ./config/AppleVolumes.default.tmpl
echo "/home/pi/afpshare \"Pi File Server\" adouble:v1 volcharset:ASCII" >> ./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 ) </dev/null
echo "Compiling and installing Netatalk..."
./bootstrap
./configure --enable-debian --enable-cups --sysconfdir=/etc --with-uams-path=/usr/lib/netatalk
( make && sudo make install ) </dev/null
if [ -d "$AFP_SHARE_PATH" ]; then
echo "The $AFP_SHARE_PATH directory already exists."
else
echo "The $AFP_SHARE_PATH directory does not exist; creating..."
mkdir -p "$AFP_SHARE_PATH"
chmod -R 2775 "$AFP_SHARE_PATH"
fi
if [[ `grep -c netatalk /etc/rc.local` -eq 0 ]]; then
sudo sed -i "/^exit 0/i sudo /etc/init.d/netatalk start" /etc/rc.local
echo "Modified /etc/rc.local"
fi
sudo /etc/init.d/netatalk start
if [[ `lsmod | grep -c appletalk` -eq 0 ]]; then
echo ""
echo "Your system may not have support for AppleTalk networking."
echo "Use TCP to connect to your AppleShare server via the IP address of the network interface that is connected to the rest of your network:"
echo `ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}'`
echo "See wiki for information on how to compile support for AppleTalk into your Linux kernel."
fi
echo ""
echo "Netatalk is now installed and configured to run on system boot."
echo "To start or stop the File Server manually, do:"
echo "sudo /etc/init.d/netatalk start"
echo "sudo /etc/init.d/netatalk stop"
echo ""
echo "Make sure that the user running Netatalk has a password of 8 chars or less. You may execute the 'passwd' command to change the password of the current user."
echo "For more information on configuring Netatalk and accessing AppleShare from your vintage Macs, see wiki:"
echo "https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing"
echo ""
}
function notifyBackup {
if $SYSTEMD_BACKUP; then
echo ""
echo "IMPORTANT: /etc/systemd/system/rascsi.service has been overwritten."
echo "A backup copy was saved as rascsi.service.old in the same directory."
echo "Please inspect the backup file and restore configurations that are important to your setup."
echo ""
fi
}
function runChoice() {
case $1 in
1)
echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE-FULLSPEC}) + Web interface"
stopOldWebInterface
updateRaScsiGit
createImagesDir
installPackages
installRaScsi
installRaScsiWebInterface
showRaScsiStatus
showRaScsiWebStatus
notifyBackup
echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE-FULLSPEC}) + Web interface - Complete!"
;;
2)
echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE-FULLSPEC})"
updateRaScsiGit
createImagesDir
installPackages
installRaScsi
showRaScsiStatus
notifyBackup
echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE-FULLSPEC}) - Complete!"
;;
3)
echo "Installing / Updating RaSCSI OLED Screen"
installRaScsiScreen
showRaScsiScreenStatus
echo "Installing / Updating RaSCSI OLED Screen - Complete!"
;;
4)
echo "Creating a 600MB drive"
createDrive600MB
echo "Creating a 600MB drive - Complete!"
;;
5)
echo "Creating a custom drive"
createDriveCustom
echo "Creating a custom drive - Complete!"
;;
6)
echo "Configuring wired network bridge"
showMacNetworkWired
setupWiredNetworking
echo "Configuring wired network bridge - Complete!"
;;
7)
echo "Configuring wifi network bridge"
showMacNetworkWireless
setupWirelessNetworking
echo "Configuring wifi network bridge - Complete!"
;;
8)
echo "Reserving SCSI IDs"
reserveScsiIds
showRaScsiWebStatus
echo "Reserving SCSI IDs - Complete!"
;;
9)
echo "Installing AppleShare File Server"
installNetatalk
echo "Installing AppleShare File Server - Complete!"
;;
-h|--help|h|help)
showMenu
;;
*)
echo "${1} is not a valid option, exiting..."
exit 1
esac
}
function readChoice() {
choice=-1
until [ $choice -ge "0" ] && [ $choice -le "9" ]; do
echo -n "Enter your choice (0-9) or CTRL-C to exit: "
read -r choice
done
runChoice "$choice"
}
function showMenu() {
echo ""
echo "Choose among the following options:"
echo "INSTALL"
echo " 0) install RaSCSI Service + web interface + 600MB Drive (recommended)"
echo " 1) install RaSCSI Service (initial)"
echo " 2) install RaSCSI Web interface"
echo "UPDATE"
echo " 3) update RaSCSI Service + web interface (recommended)"
echo " 4) update RaSCSI Service"
echo " 5) update RaSCSI Web interface"
echo "CREATE EMPTY DRIVE"
echo " 6) 600MB drive (recommended)"
echo " 7) custom drive size (up to 4000MB)"
choice=-1
until [ $choice -ge "0" ] && [ $choice -le "7" ]; do
echo -n "Enter your choice (0-7) or CTRL-C to exit: "
read -r choice
done
case $choice in
0)
echo "Installing RaSCSI Service + Web interface"
installRaScsi
installRaScsiWebInterface
createDrive600MB
showRaScsiStatus
;;
1)
echo "Installing RaSCSI Service"
installRaScsi
showRaScsiStatus
;;
2)
echo "Installing RaSCSI Web interface"
installRaScsiWebInterface
;;
3)
echo "Updating RaSCSI Service + Web interface"
updateRaScsi
updateRaScsiWebInterface
showRaScsiStatus
;;
4)
echo "Updating RaSCSI Service"
updateRaScsi
showRaScsiStatus
;;
5)
echo "Updating RaSCSI Web interface"
updateRaScsiWebInterface
;;
6)
echo "Creating a 600MB drive"
createDrive600MB
;;
7)
echo "Creating a custom drive"
createDriveCustom
;;
esac
echo "INSTALL/UPDATE RASCSI (${CONNECT_TYPE-FULLSPEC} version)"
echo " 1) install or update RaSCSI Service + Web Interface"
echo " 2) install or update RaSCSI Service"
echo " 3) install or update RaSCSI OLED Screen (requires hardware)"
echo "CREATE HFS FORMATTED (MAC) IMAGE WITH LIDO DRIVERS"
echo "** For the Mac Plus, it's better to create an image through the Web Interface **"
echo " 4) 600MB drive (suggested size)"
echo " 5) custom drive size (up to 4000MB)"
echo "NETWORK ASSISTANT"
echo " 6) configure network forwarding over Ethernet (DHCP)"
echo " 7) configure network forwarding over WiFi (static IP)"
echo "MISCELLANEOUS"
echo " 8) reserve SCSI IDs"
echo " 9) install AppleShare File Server (Netatalk)"
}
# parse arguments
while [ "$1" != "" ]; do
PARAM=$(echo "$1" | awk -F= '{print $1}')
VALUE=$(echo "$1" | awk -F= '{print $2}')
case $PARAM in
-c | --connect_type)
CONNECT_TYPE=$VALUE
;;
-r | --run_choice)
RUN_CHOICE=$VALUE
;;
*)
echo "ERROR: unknown parameter \"$PARAM\""
exit 1
;;
esac
case $VALUE in
FULLSPEC | STANDARD | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 )
;;
*)
echo "ERROR: unknown option \"$VALUE\""
exit 1
;;
esac
shift
done
showRaSCSILogo
initialChecks
showMenu
if [ -z "${RUN_CHOICE}" ]; then # RUN_CHOICE is unset, show menu
showMenu
readChoice
else
runChoice "$RUN_CHOICE"
fi

261
src/loopback_test/test.py Executable file
View File

@ -0,0 +1,261 @@
#!/usr/bin/env python3
# BSD 3-Clause License
#
# Copyright (c) 2021, akuker
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import RPi.GPIO as gpio
import time
pin_settle_delay = 0.01
err_count = 0
# Define constants for each of the SCSI signals, based upon their
# raspberry pi pin number (since we're using BOARD mode of RPi.GPIO)
scsi_d0_gpio = 19
scsi_d1_gpio = 23
scsi_d2_gpio = 32
scsi_d3_gpio = 33
scsi_d4_gpio = 8
scsi_d5_gpio = 10
scsi_d6_gpio = 36
scsi_d7_gpio = 11
scsi_dp_gpio = 12
scsi_atn_gpio = 35
scsi_rst_gpio = 38
scsi_ack_gpio = 40
scsi_req_gpio = 15
scsi_msg_gpio = 16
scsi_cd_gpio = 18
scsi_io_gpio = 22
scsi_bsy_gpio = 37
scsi_sel_gpio = 13
# Pin numbers of the direction controllers of the RaSCSI board
rascsi_ind_gpio = 31
rascsi_tad_gpio = 26
rascsi_dtd_gpio = 24
rascsi_none = -1
# Matrix showing all of the SCSI signals, along what signal they're looped back to.
# dir_ctrl indicates which direction control pin is associated with that output
gpio_map = [
{ 'gpio_num': scsi_d0_gpio, 'attached_to': scsi_ack_gpio, 'dir_ctrl': rascsi_dtd_gpio},
{ 'gpio_num': scsi_d1_gpio, 'attached_to': scsi_sel_gpio, 'dir_ctrl': rascsi_dtd_gpio},
{ 'gpio_num': scsi_d2_gpio, 'attached_to': scsi_atn_gpio, 'dir_ctrl': rascsi_dtd_gpio},
{ 'gpio_num': scsi_d3_gpio, 'attached_to': scsi_rst_gpio, 'dir_ctrl': rascsi_dtd_gpio},
{ 'gpio_num': scsi_d4_gpio, 'attached_to': scsi_cd_gpio, 'dir_ctrl': rascsi_dtd_gpio},
{ 'gpio_num': scsi_d5_gpio, 'attached_to': scsi_io_gpio, 'dir_ctrl': rascsi_dtd_gpio},
{ 'gpio_num': scsi_d6_gpio, 'attached_to': scsi_msg_gpio, 'dir_ctrl': rascsi_dtd_gpio},
{ 'gpio_num': scsi_d7_gpio, 'attached_to': scsi_req_gpio, 'dir_ctrl': rascsi_dtd_gpio},
{ 'gpio_num': scsi_dp_gpio, 'attached_to': scsi_bsy_gpio, 'dir_ctrl': rascsi_dtd_gpio},
{ 'gpio_num': scsi_atn_gpio, 'attached_to': scsi_d2_gpio, 'dir_ctrl': rascsi_ind_gpio},
{ 'gpio_num': scsi_rst_gpio, 'attached_to': scsi_d3_gpio, 'dir_ctrl': rascsi_ind_gpio},
{ 'gpio_num': scsi_ack_gpio, 'attached_to': scsi_d0_gpio, 'dir_ctrl': rascsi_ind_gpio},
{ 'gpio_num': scsi_req_gpio, 'attached_to': scsi_d7_gpio, 'dir_ctrl': rascsi_tad_gpio},
{ 'gpio_num': scsi_msg_gpio, 'attached_to': scsi_d6_gpio, 'dir_ctrl': rascsi_tad_gpio},
{ 'gpio_num': scsi_cd_gpio, 'attached_to': scsi_d4_gpio, 'dir_ctrl': rascsi_tad_gpio},
{ 'gpio_num': scsi_io_gpio, 'attached_to': scsi_d5_gpio, 'dir_ctrl': rascsi_tad_gpio},
{ 'gpio_num': scsi_bsy_gpio, 'attached_to': scsi_dp_gpio, 'dir_ctrl': rascsi_tad_gpio},
{ 'gpio_num': scsi_sel_gpio, 'attached_to': scsi_d1_gpio, 'dir_ctrl': rascsi_ind_gpio},
]
# List of all of the SCSI signals that is also a dictionary to their human readable name
scsi_signals = {
scsi_d0_gpio: 'D0',
scsi_d1_gpio: 'D1',
scsi_d2_gpio: 'D2',
scsi_d3_gpio: 'D3',
scsi_d4_gpio: 'D4',
scsi_d5_gpio: 'D5',
scsi_d6_gpio: 'D6',
scsi_d7_gpio: 'D7',
scsi_dp_gpio: 'DP',
scsi_atn_gpio: 'ATN',
scsi_rst_gpio: 'RST',
scsi_ack_gpio: 'ACK',
scsi_req_gpio: 'REQ',
scsi_msg_gpio: 'MSG',
scsi_cd_gpio: 'CD',
scsi_io_gpio: 'IO',
scsi_bsy_gpio: 'BSY',
scsi_sel_gpio: 'SEL'
}
# Debug function that just dumps the status of all of the scsi signals to the console
def print_all():
for cur_gpio in gpio_map:
print(cur_gpio['name']+"="+str(gpio.input(cur_gpio['gpio_num'])) + " ", end='', flush=True)
print("")
# Set transceivers IC1 and IC2 to OUTPUT
def set_dtd_out():
gpio.output(rascsi_dtd_gpio,gpio.LOW)
# Set transceivers IC1 and IC2 to INPUT
def set_dtd_in():
gpio.output(rascsi_dtd_gpio,gpio.HIGH)
# Set transceiver IC4 to OUTPUT
def set_ind_out():
gpio.output(rascsi_ind_gpio,gpio.HIGH)
# Set transceiver IC4 to INPUT
def set_ind_in():
gpio.output(rascsi_ind_gpio,gpio.LOW)
# Set transceiver IC3 to OUTPUT
def set_tad_out():
gpio.output(rascsi_tad_gpio,gpio.HIGH)
# Set transceiver IC3 to INPUT
def set_tad_in():
gpio.output(rascsi_tad_gpio,gpio.LOW)
# Set the specified transciever to an OUTPUT. All of the other transceivers
# will be set to inputs. If a non-existent direction gpio is specified, this
# will set all of the transceivers to inputs.
def set_output_channel(out_gpio):
if(out_gpio == rascsi_tad_gpio):
set_tad_out()
else:
set_tad_in()
if(out_gpio == rascsi_dtd_gpio):
set_dtd_out()
else:
set_dtd_in()
if(out_gpio == rascsi_ind_gpio):
set_ind_out()
else:
set_ind_in()
# Main test procedure. This will execute for each of the SCSI pins to make sure its connected
# properly.
def test_gpio_pin(gpio_rec):
global err_count
set_output_channel(gpio_rec['dir_ctrl'])
############################################
# set the test gpio low
gpio.output(gpio_rec['gpio_num'], gpio.LOW)
time.sleep(pin_settle_delay)
# loop through all of the gpios
for cur_gpio in scsi_signals:
# all of the gpios should be high except for the test gpio and the connected gpio
cur_val = gpio.input(cur_gpio)
if( cur_gpio == gpio_rec['gpio_num']):
if(cur_val != gpio.LOW):
print("Error: Test commanded GPIO " + scsi_signals[gpio_rec['gpio_num']] + " to be low, but it did not respond")
err_count = err_count+1
elif (cur_gpio == gpio_rec['attached_to']):
if(cur_val != gpio.LOW):
print("Error: GPIO " + scsi_signals[gpio_rec['gpio_num']] + " should drive " + scsi_signals[gpio_rec['attached_to']] + " low, but did not")
err_count = err_count+1
else:
if(cur_val != gpio.HIGH):
print("Error: GPIO " + scsi_signals[gpio_rec['gpio_num']] + " incorrectly pulled " + scsi_signals[cur_gpio] + " LOW, when it shouldn't have")
err_count = err_count+1
############################################
# set the transceivers to input
set_output_channel(rascsi_none)
time.sleep(pin_settle_delay)
# loop through all of the gpios
for cur_gpio in scsi_signals:
# all of the gpios should be high except for the test gpio
cur_val = gpio.input(cur_gpio)
if( cur_gpio == gpio_rec['gpio_num']):
if(cur_val != gpio.LOW):
print("Error: Test commanded GPIO " + scsi_signals[gpio_rec['gpio_num']] + " to be low, but it did not respond")
err_count = err_count+1
else:
if(cur_val != gpio.HIGH):
print("Error: GPIO " + scsi_signals[gpio_rec['gpio_num']] + " incorrectly pulled " + scsi_signals[cur_gpio] + " LOW, when it shouldn't have")
err_count = err_count+1
# Set the transceiver back to output
set_output_channel(gpio_rec['dir_ctrl'])
#############################################
# set the test gpio high
gpio.output(gpio_rec['gpio_num'], gpio.HIGH)
time.sleep(pin_settle_delay)
# loop through all of the gpios
for cur_gpio in scsi_signals:
# all of the gpios should be high
cur_val = gpio.input(cur_gpio)
if( cur_gpio == gpio_rec['gpio_num']):
if(cur_val != gpio.HIGH):
print("Error: Test commanded GPIO " + scsi_signals[gpio_rec['gpio_num']] + " to be high, but it did not respond")
err_count = err_count+1
else:
if(cur_val != gpio.HIGH):
print("Error: GPIO " + scsi_signals[gpio_rec['gpio_num']] + " incorrectly pulled " + scsi_signals[cur_gpio] + " LOW, when it shouldn't have")
err_count = err_count+1
# Initialize the GPIO library, set all of the gpios associated with SCSI signals to outputs and set
# all of the direction control gpios to outputs
def setup():
gpio.setmode(gpio.BOARD)
gpio.setwarnings(False)
for cur_gpio in gpio_map:
gpio.setup(cur_gpio['gpio_num'], gpio.OUT, initial=gpio.HIGH)
# Setup direction control
gpio.setup(rascsi_ind_gpio, gpio.OUT)
gpio.setup(rascsi_tad_gpio, gpio.OUT)
gpio.setup(rascsi_dtd_gpio, gpio.OUT)
# Main functions for running the actual test.
if __name__ == '__main__':
# setup the GPIOs
setup()
# Test each SCSI signal in the gpio_map
for cur_gpio in gpio_map:
test_gpio_pin(cur_gpio)
# Print the test results
if(err_count == 0):
print("-------- Test PASSED --------")
else:
print("!!!!!!!! Test FAILED !!!!!!!!")
print("Total errors: " + str(err_count))
gpio.cleanup()

View File

@ -0,0 +1,4 @@
type_writer.ttf
"Type Writer" TrueType font by Mandy Smith
Source: https://www.dafont.com/type-writer.font
Distributed under BSD 3-Clause by permission from author (see LICENSE for full text)

View File

@ -0,0 +1,17 @@
[Unit]
Description=Monitor RaSCSI service
After=network.target rascsi.service
[Service]
Type=simple
Restart=always
ExecStart=/home/pi/RASCSI/src/oled_monitor/start.sh
ExecStop=/bin/echo "Shutting down the OLED Monitor gracefully..."
ExecStop=/bin/pkill --signal 2 -f "python3 rascsi_oled_monitor.py"
ExecStop=/bin/sleep 2
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=RASCSIMON
[Install]
WantedBy=multi-user.target

View File

@ -4,7 +4,8 @@
# Updates to output rascsi status to an OLED display
# Copyright (C) 2020 Tony Kuker
# Author: Tony Kuker
# Developed for: https://www.amazon.com/MakerFocus-Display-SSD1306-3-3V-5V-Arduino/dp/B079BN2J8V
# Developed for:
# https://www.makerfocus.com/collections/oled/products/2pcs-i2c-oled-display-module-0-91-inch-i2c-ssd1306-oled-display-module-1
#
# All other code:
# Copyright (c) 2017 Adafruit Industries
@ -27,38 +28,86 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import time
import os
import sys
import datetime
import board
import busio
import adafruit_ssd1306
from time import sleep
from sys import argv, exit
from board import I2C
from adafruit_ssd1306 import SSD1306_I2C
from PIL import Image, ImageDraw, ImageFont
import subprocess
from os import path, getcwd
from collections import deque
from struct import pack, unpack
import signal
import socket
import rascsi_interface_pb2 as proto
class GracefulInterruptHandler(object):
def __init__(self, signals=(signal.SIGINT, signal.SIGTERM)):
self.signals = signals
self.original_handlers = {}
def __enter__(self):
self.interrupted = False
self.released = False
for sig in self.signals:
self.original_handlers[sig] = signal.getsignal(sig)
signal.signal(sig, self.handler)
return self
def handler(self, signum, frame):
self.release()
self.interrupted = True
def __exit__(self, type, value, tb):
self.release()
def release(self):
if self.released:
return False
for sig in self.signals:
signal.signal(sig, self.original_handlers[sig])
self.released = True
return True
WIDTH = 128
HEIGHT = 32 # Change to 64 if needed
BORDER = 5
# How long to delay between each update
delay_time_ms = 250
delay_time_ms = 1000
# Define the Reset Pin
oled_reset = None
# init i2c
i2c = board.I2C()
i2c = I2C()
# 128x32 display with hardware I2C:
oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C, reset=oled_reset)
oled = SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C, reset=oled_reset)
print ("Running with the following display:")
print (oled)
print ()
print ("Will update the OLED display every " + str(delay_time_ms) + "ms (approximately)")
# Attempt to read the first argument to the script; fall back to 2 (180 degrees)
if len(argv) > 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("<i", len(payload)))
s.send(payload)
# Receive the first 4 bytes to get the response header
response = s.recv(4)
if len(response) >= 4:
# Extracting the response header to get the length of the response message
response_length = unpack("<i", response)[0]
# Reading in chunks, to handle a case where the response message is very large
chunks = []
bytes_recvd = 0
while bytes_recvd < response_length:
chunk = s.recv(min(response_length - bytes_recvd, 2048))
if chunk == b'':
exit("Socket connection has dropped unexpectedly. "
"RaSCSI may have crashed."
)
chunks.append(chunk)
bytes_recvd = bytes_recvd + len(chunk)
response_message = b''.join(chunks)
return response_message
else:
exit("The response from RaSCSI did not contain a protobuf header. "
"RaSCSI may have crashed."
)
def formatted_output():
"""
Formats the strings to be displayed on the Screen
Returns a list of str output
"""
rascsi_list = device_list()
output = []
if len(rascsi_list):
for line in rascsi_list:
if line["device_type"] in ("SCCD", "SCRM", "SCMO"):
if len(line["file"]):
output.append(f"{line['id']} {line['device_type'][2:4]} {line['file']} {line['status']}")
else:
output.append(f"{line['id']} {line['device_type'][2:4]} {line['status']}")
elif line["device_type"] in ("SCDP"):
output.append(f"{line['id']} {line['device_type'][2:4]} {line['vendor']} {line['product']}")
elif line["device_type"] in ("SCBR"):
output.append(f"{line['id']} {line['device_type'][2:4]} {line['product']}")
else:
output.append(f"{line['id']} {line['device_type'][2:4]} {line['file']} {line['vendor']} {line['product']} {line['status']}")
else:
output.append("No image mounted!")
output.append(f"IP {ip} - {host}")
return output
def start_splash():
splash = Image.open(f"{cwd}/splash_start.bmp").convert("1")
draw.bitmap((0, 0), splash)
oled.image(splash)
oled.show()
time.sleep(1/delay_time_ms)
sleep(6)
def stop_splash():
draw.rectangle((0,0,WIDTH,HEIGHT), outline=0, fill=0)
splash = Image.open(f"{cwd}/splash_stop.bmp").convert("1")
draw.bitmap((0, 0), splash)
oled.image(splash)
oled.show()
cwd = getcwd()
start_splash()
ip, host = get_ip_and_host()
with GracefulInterruptHandler() as h:
while True:
ref_snapshot = formatted_output()
snapshot = ref_snapshot
output = deque(snapshot)
while snapshot == ref_snapshot:
# Draw a black filled box to clear the image.
draw.rectangle((0,0,WIDTH,HEIGHT), outline=0, fill=0)
y_pos = top
for line in output:
draw.text((x, y_pos), line, font=font, fill=255)
y_pos += 8
# Shift the index of the array by one to get a scrolling effect
if len(output) > 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...")

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

View File

@ -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}"

Binary file not shown.

View File

@ -12,3 +12,8 @@ sasidump
rasdump
obj
bin
/rascsi_interface.pb.cpp
/rascsi_interface.pb.h
.project
.cproject
.settings

View File

@ -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 @@
]
}
]
}
}

View File

@ -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 @@
}
}
]
}
}

View File

@ -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)/%

File diff suppressed because it is too large Load Diff

View File

@ -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
};

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,9 @@
//
//---------------------------------------------------------------------------
#pragma once
#include "controllers/sasidev_ctrl.h"
#include <map>
//===========================================================================
//
@ -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
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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 <unistd.h>
#include <arpa/inet.h>
#ifdef __linux__
#include <net/if.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
#endif
#include <zlib.h> // For crc32()
#include "os.h"
#include "xm6.h"
#include "../rascsi.h"
#include "ctapdriver.h"
#include "log.h"
#include "exceptions.h"
#include <sstream>
//---------------------------------------------------------------------------
//
// 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);
}

View File

@ -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 <pcap/pcap.h>
#include "filepath.h"
#include <vector>
#include <string>
#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<std::string> interfaces;
};
#endif // ctapdriver_h

View File

@ -0,0 +1,162 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
//---------------------------------------------------------------------------
#include <cassert>
#include <sstream>
#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;
}

View File

@ -0,0 +1,185 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include <map>
#include <string>
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<string, string> params;
// The default parameters
map<string, string> 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<string, string>&) { 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<string, string> GetParams() const { return params; }
const string GetParam(const string&);
void SetParams(const map<string, string>& params) { this->params = params; }
const map<string, string> GetDefaultParams() const { return default_params; }
void SetDefaultParams(const map<string, string>& 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"; }
};

View File

@ -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 <ifaddrs.h>
#include <set>
#include <map>
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<uint32_t>& DeviceFactory::GetSectorSizes(const string& type)
{
PbDeviceType t = UNDEFINED;
PbDeviceType_Parse(type, &t);
return sector_sizes[t];
}
const set<uint64_t> DeviceFactory::GetCapacities(PbDeviceType type)
{
set<uint64_t> keys;
for (const auto& geometry : geometries[type]) {
keys.insert(geometry.first);
}
return keys;
}
const list<string> DeviceFactory::GetNetworkInterfaces() const
{
list<string> 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;
}

View File

@ -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 <set>
#include <list>
#include <map>
#include <string>
#include "rascsi_interface.pb.h"
using namespace std;
using namespace rascsi_interface;
typedef pair<uint32_t, uint32_t> Geometry;
class Device;
class DeviceFactory
{
private:
DeviceFactory();
~DeviceFactory() {};
public:
static DeviceFactory& instance();
Device *CreateDevice(PbDeviceType, const string&);
PbDeviceType GetTypeForFile(const string&);
const set<uint32_t>& GetSectorSizes(PbDeviceType type) { return sector_sizes[type]; }
const set<uint32_t>& GetSectorSizes(const string&);
const set<uint64_t> GetCapacities(PbDeviceType);
const map<string, string>& GetDefaultParams(PbDeviceType type) { return default_params[type]; }
const list<string> GetNetworkInterfaces() const;
const map<string, PbDeviceType> GetExtensionMapping() const { return extension_mapping; }
private:
map<PbDeviceType, set<uint32_t>> sector_sizes;
// Optional mapping of drive capacities to drive geometries
map<PbDeviceType, map<uint64_t, Geometry>> geometries;
map<PbDeviceType, map<string, string>> default_params;
map<string, PbDeviceType> extension_mapping;
string GetExtension(const string&) const;
};

File diff suppressed because it is too large Load Diff

View File

@ -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 <string>
#include <set>
#include <map>
//---------------------------------------------------------------------------
//
// 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<uint32_t> 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<uint64_t, Geometry> 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<SCSIDEV::scsi_command, command_t*> 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<uint32_t> GetSectorSizes() const;
void SetSectorSizes(const set<uint32_t>&);
uint32_t GetConfiguredSectorSize() const;
bool SetConfiguredSectorSize(uint32_t);
void SetGeometries(const map<uint64_t, Geometry>&);
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);
};

View File

@ -0,0 +1,656 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (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;
}
}

View File

@ -0,0 +1,95 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (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
};

View File

@ -0,0 +1,43 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
//---------------------------------------------------------------------------
#include <map>
#include <string>
#include "file_support.h"
using namespace std;
map<string, id_set> 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();
}

View File

@ -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 <map>
#include <string>
#include "filepath.h"
using namespace std;
typedef pair<int, int> id_set;
class FileSupport
{
private:
Filepath diskpath;
// The list of image files in use and the IDs and LUNs using these files
static map<string, id_set> reserved_files;
public:
FileSupport() {};
virtual ~FileSupport() {};
void GetPath(Filepath& path) const { path = diskpath; }
void SetPath(const Filepath& path) { diskpath = path; }
static const map<string, id_set> GetReservedFiles(){ return reserved_files; }
static void SetReservedFiles(const map<string, id_set>& 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;
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -14,9 +14,10 @@
//
//---------------------------------------------------------------------------
#include "sasihd.h"
#include "xm6.h"
#include "fileio.h"
#include "exceptions.h"
#include <sstream>
#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;
}

View File

@ -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
};
void Reset();
void Open(const Filepath& path);
// Commands
int RequestSense(const DWORD *cdb, BYTE *buf) override;
int Inquiry(const DWORD *cdb, BYTE *buf) override;
};

View File

@ -0,0 +1,660 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2020 akuker
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) 2001-2006 (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 <sstream>
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<SCSIDEV::scsi_command>(ctrl->cmd[0]))) {
command_t *command = commands[static_cast<SCSIDEV::scsi_command>(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<string, string>& 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]);
}
}

View File

@ -0,0 +1,161 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2020 akuker
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) 2001-2006 (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 <map>
#include <string>
#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<SCSIDEV::scsi_command, command_t*> commands;
SASIDEV::ctrl_t *ctrl;
void AddCommand(SCSIDEV::scsi_command, const char*, void (SCSIDaynaPort::*)(SASIDEV *));
public:
SCSIDaynaPort();
~SCSIDaynaPort();
bool Init(const map<string, string>&) 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;

File diff suppressed because it is too large Load Diff

View File

@ -17,80 +17,96 @@
//---------------------------------------------------------------------------
#pragma once
#include "xm6.h"
#include "os.h"
#include "disk.h"
#include <string>
#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<SCSIDEV::scsi_command, command_t*> commands;
SASIDEV::ctrl_t *ctrl;
void AddCommand(SCSIDEV::scsi_command, const char*, void (SCSIBR::*)(SASIDEV *));
public:
SCSIBR();
~SCSIBR();
bool Init(const map<string, string>&) 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

File diff suppressed because it is too large Load Diff

View File

@ -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<SCSIDEV::scsi_command, command_t*> 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
};

View File

@ -14,8 +14,12 @@
//
//---------------------------------------------------------------------------
#include "scsihd.h"
#include "xm6.h"
#include "fileio.h"
#include "exceptions.h"
#include <sstream>
#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<length; i++)
{
printf("%02X ", buf[i]);
@ -262,7 +237,31 @@ BOOL FASTCALL SCSIHD::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;
}
//---------------------------------------------------------------------------
//
// Add Vendor special page to make drive Apple compatible
//
//---------------------------------------------------------------------------
int SCSIHD::AddVendorPage(int page, bool change, BYTE *buf)
{
ASSERT(buf);
// Page code 48 or 63
if (page != 0x30 && page != 0x3f) {
return 0;
}
// Set the message length
buf[0] = 0x30;
buf[1] = 0x1c;
// No changeable area
if (!change) {
memcpy(&buf[0xa], "APPLE COMPUTER, INC.", 20);
}
return 30;
}

View File

@ -19,20 +19,21 @@
#include "disk.h"
#include "filepath.h"
//===========================================================================
//
// SCSI Hard Disk
//
//===========================================================================
class SCSIHD : public Disk
class SCSIHD : public Disk, public FileSupport
{
public:
// Basic Functions
SCSIHD(); // Constructor
void FASTCALL Reset(); // Reset
BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); // Open
SCSIHD(bool);
virtual ~SCSIHD() {};
// 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 FinalizeSetup(const Filepath&, off_t);
void Reset();
virtual void Open(const Filepath&);
// Commands
virtual int Inquiry(const DWORD *cdb, BYTE *buf) override;
bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override;
// Add vendor special page
int AddVendorPage(int page, bool change, BYTE *buf) override;
};

View File

@ -1,93 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SCSI Hard Disk for Apple Macintosh ]
//
//---------------------------------------------------------------------------
#include "scsihd_apple.h"
//===========================================================================
//
// SCSI hard disk (Macintosh Apple genuine)
//
//===========================================================================
//---------------------------------------------------------------------------
//
// Constructor
//
//---------------------------------------------------------------------------
SCSIHD_APPLE::SCSIHD_APPLE() : SCSIHD()
{
}
//---------------------------------------------------------------------------
//
// INQUIRY
//
//---------------------------------------------------------------------------
int FASTCALL SCSIHD_APPLE::Inquiry(
const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor)
{
int size;
char vendor[32];
char product[32];
// Call the base class
size = SCSIHD::Inquiry(cdb, buf, major, minor);
// End if there is an error in the base class
if (size == 0) {
return 0;
}
// Vendor name
sprintf(vendor, " SEAGATE");
memcpy(&buf[8], vendor, strlen(vendor));
// Product name
sprintf(product, " ST225N");
memcpy(&buf[16], product, strlen(product));
return size;
}
//---------------------------------------------------------------------------
//
// Add Vendor special page
//
//---------------------------------------------------------------------------
int FASTCALL SCSIHD_APPLE::AddVendor(int page, BOOL change, BYTE *buf)
{
ASSERT(this);
ASSERT(buf);
// Page code 48
if ((page != 0x30) && (page != 0x3f)) {
return 0;
}
// Set the message length
buf[0] = 0x30;
buf[1] = 0x1c;
// No changeable area
if (change) {
return 30;
}
// APPLE COMPUTER, INC.
memcpy(&buf[0xa], "APPLE COMPUTER, INC.", 20);
return 30;
}

View File

@ -1,35 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SCSI Hard Disk for Apple Macintosh ]
//
//---------------------------------------------------------------------------
#pragma once
#include "scsihd.h"
//===========================================================================
//
// SCSI Hard Disk(Genuine Apple Macintosh)
//
//===========================================================================
class SCSIHD_APPLE : public SCSIHD
{
public:
// Basic Functions
SCSIHD_APPLE(); // Constructor
// commands
int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
// Internal processing
int FASTCALL AddVendor(int page, BOOL change, BYTE *buf); // Add vendor special page
};

View File

@ -16,32 +16,19 @@
#include "scsihd_nec.h"
#include "fileio.h"
#include "exceptions.h"
//===========================================================================
//
// SCSI hard disk (PC-9801-55 NEC genuine /Anex86/T98Next)
//
//===========================================================================
//---------------------------------------------------------------------------
//
// Constructor
//
//---------------------------------------------------------------------------
SCSIHD_NEC::SCSIHD_NEC() : SCSIHD()
SCSIHD_NEC::SCSIHD_NEC() : SCSIHD(false)
{
// ワーク初期化
// Work initialization
cylinders = 0;
heads = 0;
sectors = 0;
sectorsize = 0;
imgoffset = 0;
imgsize = 0;
}
//---------------------------------------------------------------------------
//
// リトルエンディアンと想定したワードを取り出す
// Extract words that are supposed to be little endian
//
//---------------------------------------------------------------------------
static inline WORD getWordLE(const BYTE *b)
@ -51,225 +38,166 @@ static inline WORD getWordLE(const BYTE *b)
//---------------------------------------------------------------------------
//
// リトルエンディアンと想定したロングワードを取り出す
// Extract longwords assumed to be little endian
//
//---------------------------------------------------------------------------
static inline DWORD getDwordLE(const BYTE *b)
{
return ((DWORD)(b[3]) << 24) | ((DWORD)(b[2]) << 16) |
((DWORD)(b[1]) << 8) | b[0];
return ((DWORD)(b[3]) << 24) | ((DWORD)(b[2]) << 16) | ((DWORD)(b[1]) << 8) | b[0];
}
//---------------------------------------------------------------------------
//
// オープン
//
//---------------------------------------------------------------------------
BOOL FASTCALL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
void SCSIHD_NEC::Open(const Filepath& path)
{
Fileio fio;
off64_t size;
BYTE hdr[512];
LPCTSTR ext;
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 hard disk file");
}
// Get file size
size = fio.GetFileSize();
off_t size = fio.GetFileSize();
// ヘッダー読み込み
if (size >= (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;
}

View File

@ -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
};
// Geometry data
int cylinders;
int heads;
int sectors;
};

View File

@ -15,8 +15,11 @@
//---------------------------------------------------------------------------
#include "scsimo.h"
#include "xm6.h"
#include "../rascsi.h"
#include "fileio.h"
#include "exceptions.h"
#include <sstream>
//===========================================================================
//
@ -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);

View File

@ -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
};
int AddVendor(int page, BOOL change, BYTE *buf); // Add vendor special page
};

View File

@ -0,0 +1,49 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
// Various exceptions
//
//---------------------------------------------------------------------------
#pragma once
#include <exception>
#include <string>
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() {}
};

View File

@ -4,159 +4,114 @@
//
// Copyright (C) 2001-2006 (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

View File

@ -4,7 +4,7 @@
//
// Copyright (C) 2001-2005 (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

View File

@ -4,64 +4,43 @@
//
// Copyright (C) 2001-2006 (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];

View File

@ -4,80 +4,49 @@
//
// Copyright (C) 2001-2006 (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

View File

@ -12,12 +12,13 @@
//
//---------------------------------------------------------------------------
#include <sys/mman.h>
#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;

View File

@ -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:

3
src/raspberrypi/launch_sudo.sh Executable file
View File

@ -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 "$@"

View File

@ -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

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <assert.h>
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <cstdarg>
#include <cstring>
#include <csignal>
#include <cassert>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <utime.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <string.h>
#include <sched.h>
#include <pthread.h>
#include <iconv.h>
@ -55,24 +53,16 @@
#include <sys/stat.h>
#include <sys/time.h>
#ifndef BAREMETAL
#include <poll.h>
#include <dirent.h>
#include <sys/mman.h>
#include <pwd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <linux/gpio.h>
#else
#include <machine/endian.h>
#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 <linux/gpio.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,155 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
//---------------------------------------------------------------------------
#include <unistd.h>
#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());
}

View File

@ -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 <sstream>
#include <string>
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&);
}

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -0,0 +1,347 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
//---------------------------------------------------------------------------
#include <unistd.h>
#include <sys/sendfile.h>
#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 <sstream>
#include <string>
#include <filesystem>
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);
}

View File

@ -0,0 +1,38 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include "rascsi_interface.pb.h"
#include <string>
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;
};

View File

@ -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<string, PbDeviceType> 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<string, string> 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<string, string> 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<string, string> 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<string, string> 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;
}

View File

@ -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 <sstream>
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<const FileSupport *>(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<const Disk*>(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<const FileSupport *>(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<int>& 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<Device *>& 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<Device *>& devices,
int unit_count)
{
set<id_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<Device *>& devices, const set<int>& 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;
}

View File

@ -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 <vector>
#include <string>
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<int>&);
void GetDevices(PbServerInfo&, const vector<Device *>&);
void GetDevicesInfo(PbResult&, const PbCommand&, const vector<Device *>&, int);
PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&);
PbVersionInfo *GetVersionInfo(PbResult&);
PbServerInfo *GetServerInfo(PbResult&, const vector<Device *>&, const set<int>&, const string&);
PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&);
PbMappingInfo *GetMappingInfo(PbResult&);
PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&);
private:
DeviceFactory *device_factory;
const RascsiImage *rascsi_image;
vector<string> log_levels;
PbDeviceProperties *GetDeviceProperties(const Device *);
void GetDevice(const Device *, PbDevice *);
void GetAllDeviceTypeProperties(PbDeviceTypesInfo&);
void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType);
void GetAvailableImages(PbResult& result, PbServerInfo&);
};

View File

@ -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;
}

View File

@ -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();

View File

@ -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.

Some files were not shown because too many files have changed in this diff Show More