mirror of
https://github.com/akuker/RASCSI.git
synced 2024-12-27 01:31:42 +00:00
Merge tag 'v21.10.01'
October 2021 Release
This commit is contained in:
commit
cf9a578c82
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: akuker
|
57
.github/workflows/rpi_image_creation.yml
vendored
57
.github/workflows/rpi_image_creation.yml
vendored
@ -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
4
.gitignore
vendored
@ -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/
|
||||
*~
|
||||
|
3
LICENSE
3
LICENSE
@ -2,7 +2,8 @@ BSD 3-Clause License
|
||||
|
||||
Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
Copyright (C) 2014-2020 GIMONS
|
||||
Copyright (c) 2020, akuker
|
||||
Copyright (c) 2020-2021 akuker
|
||||
Copyright (c) RaSCSI project contributors (github.com/akuker/rascsi)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
Binary file not shown.
@ -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)!
|
||||
|
||||
|
@ -1,116 +0,0 @@
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
SCSI Target Emulator RaSCSI for Raspberry Pi
|
||||
|
||||
Powered by XM6 TypeG Technology.
|
||||
Copyright (C) 2016-2020 GIMONS
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
□変換基板の必要性について
|
||||
SCSIはTTLレベルで5Vを220Ωと330Ωで分圧(パッシブターミネータの場合)する
|
||||
ことで各信号線に3V弱の電圧がかかった状態が定常状態(信号的にはネゲート)に
|
||||
なっています。
|
||||
|
||||
イニシエータ側もしくはターゲット側が信号をアサートする(=0V)にしようと
|
||||
すると両端のターミネータから合わせて5000÷220×2=45mAの電流が流れることに
|
||||
なります(X68000のSCSIコントローラであるMB89352のデータシートを見ればシンク
|
||||
電流としてIol48mAとなっています)。
|
||||
|
||||
RPIのGPIOはこのような大きなシンク電流は吸収できません。電気的に安全な接続
|
||||
を行うためには汎用ロジックIC等で変換基板を作る必要があります。汎用ロジック
|
||||
ICで48mAものシンク電流に耐えるのは74LS06とか07といったオープンコレクタで
|
||||
ハイパワータイプのものを使用します。
|
||||
|
||||
作者は74HC541×3,74HC126×1,74HC04×1で基本的なSCSIの方向制御を行い更に
|
||||
74LS07×3を使ってバスをドライブする回路を組んでみたところ問題なく動作する
|
||||
ことを確認しました。
|
||||
|
||||
他にも74LS641の派生版である74LS641-1を使用すると回路はシンプルに構成できる
|
||||
でしょう。ノーマル品と違ってシンク電流が48mA対応なので74LS07を使用する必要
|
||||
はありません。しかし入手性はそれほど良くありません。
|
||||
|
||||
□変換基板の回路図案
|
||||
同じフォルダに回路図案を入れています。
|
||||
|
||||
・target.png
|
||||
SCSIのターゲットモードを使用するための変換基板回路図です。基本機能である
|
||||
HDDやMOのエミュレーションを行うのであればこの回路図相当の物を作れば良い
|
||||
でしょう。使用するGPIOピンも最も少ない構成になります。
|
||||
|
||||
ピンアサインを変更しなければRaSCSIのstandardディレクトリに含まれる
|
||||
バイナリを使用することが可能です。
|
||||
|
||||
・fullspec.png
|
||||
SCSIのターゲットモード、イニシエータモードを利用できる変換基板回路図です。
|
||||
全ての74LS641-1の方向制御をRaSCSIから行いますのでGPIOピンを三つ余分に使用
|
||||
してしまいます。
|
||||
|
||||
ピンアサインのカスタマイズで、PIN_TAD,PIN_IND,PIN_DTDにそれぞれ標準
|
||||
では6,7,8を設定してコンパイルする必要があります。ピンアサインの
|
||||
カスタマイズを参照してください。
|
||||
|
||||
□既存のものを手に入れる方法
|
||||
最近では主にTwitter界隈を通じてRaSCSI用の変換基板を作成していただいて
|
||||
いる方々がいらっしゃいます。直ぐに見つかると思いますのでここでの紹介
|
||||
は省略します。時期は未定ですが公式版の有償頒布を計画しています。
|
||||
|
||||
□ピンアサインのカスタマイズ
|
||||
GPIOの信号制御論理やピンアサインはgpiobus.hに定義があります。
|
||||
|
||||
カスタマイズ例としてgpiobus.hに下記の二つの変換基板用定義例を用意しました。
|
||||
配布物の中にはコンパイル済みバイナリも含まれています。
|
||||
|
||||
・あいぼむ版
|
||||
・GAMERnium版
|
||||
|
||||
□カスタマイズ方法
|
||||
・RaSCSI起動時のメッセージです。
|
||||
CONNECT_DESC
|
||||
|
||||
・信号制御モードを選択します。
|
||||
SIGNAL_CONTROL_MODE
|
||||
|
||||
0:SCSI論理仕様
|
||||
直結またはHPに公開した74LS641-1等を使用する変換基板
|
||||
アーサート:0V
|
||||
ネゲート :オープンコレクタ出力(バスから切り離す)
|
||||
|
||||
1:負論理仕様(負論理->SCSI論理への変換基板を使用する場合)
|
||||
現時点でこの仕様による変換基板は存在しません
|
||||
アーサート:0V -> (CONVERT) -> 0V
|
||||
ネゲート :3.3V -> (CONVERT) -> オープンコレクタ出力
|
||||
|
||||
2:正論理仕様(正論理->SCSI論理への変換基板を使用する場合)
|
||||
RaSCSI Adapter Rev.C @132sync等
|
||||
|
||||
アーサート:3.3V -> (CONVERT) -> 0V
|
||||
ネゲート :0V -> (CONVERT) -> オープンコレクタ出力
|
||||
|
||||
・制御信号ピンアサイン
|
||||
PIN_ACT:SCSIコマンドを処理中の状態を示す信号のピン番号。
|
||||
PIN_ENB:起動から終了の間の有効信号を示す信号のピン番号。
|
||||
PIN_TAD:ターゲット信号(BSY,IO,CD,MSG,REG)の入出力方向を示す信号のピン番号。
|
||||
PIN_IND:イニシーエータ信号(SEL,ATN,RST,ACK)の入出力方向を示す信号のピン番号。
|
||||
PIN_DTD:データ信号(DT0...DT7,DP)の入出力方向を示す信号のピン番号。
|
||||
|
||||
・制御信号出力論理
|
||||
0V:FALSE 3.3V:TRUEで指定します。
|
||||
|
||||
ACT_ON:PIN_ACT信号の論理です。
|
||||
ENB_ON:PIN_ENB信号の論理です。
|
||||
TAD_IN:PIN_TAD入力方向時の論理です。
|
||||
IND_IN:PIN_ENB入力方向時の論理です。
|
||||
DTD_IN:PIN_ENB入力方向時の論理です。
|
||||
|
||||
・SCSI信号ピンアサイン
|
||||
PIN_DT0~PIN_SEL:それぞれSCSI信号のピン番号です。
|
||||
|
||||
□コンパイル方法
|
||||
|
||||
・実行ファイル(rascsi,rasctl)
|
||||
gpiobus.hを修正
|
||||
make clean
|
||||
make
|
||||
|
||||
[EOF]
|
@ -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]
|
BIN
doc/fullspec.png
BIN
doc/fullspec.png
Binary file not shown.
Before Width: | Height: | Size: 73 KiB |
Binary file not shown.
Before Width: | Height: | Size: 335 KiB |
77
doc/rascsi.1
77
doc/rascsi.1
@ -3,19 +3,28 @@
|
||||
rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
|
||||
.SH SYNOPSIS
|
||||
.B rascsi
|
||||
[\fB\-IDn\fR \fIfile\fR]
|
||||
[\fB\-HDn\fR \fIfile\fR]...
|
||||
[\fB\-F\f® \fIFOLDER\fR]
|
||||
[\fB\-L\f® \fILOG_LEVEL\fR]
|
||||
[\fB\-h\fR]
|
||||
[\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR]
|
||||
[\fB\-p\f® \fIPORT\fR]
|
||||
[\fB\-r\fR \fIRESERVED_IDS\fR]
|
||||
[\fB\-n\fR \fITYPE\fR]
|
||||
[\fB\-v\fR]
|
||||
[\fB\-IDn:[u]\fR \fIFILE\fR]
|
||||
[\fB\-HDn[:u]\fR \fIFILE\fR]...
|
||||
.SH DESCRIPTION
|
||||
.B rascsi
|
||||
Emulates SCSI devices using the Raspberry Pi GPIO pins.
|
||||
.PP
|
||||
In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) devices can be specified.
|
||||
The number (n) after the ID or HD idnetifier specifies the ID number for that device.
|
||||
For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer).Note that SASI is considered rare and only used on very early Sharp X68000 computers.
|
||||
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) or SASI (-HDn[:u]) devices can be specified.
|
||||
The number (n) after the ID or HD identifier specifies the ID number for that device. The optional number (u) specifies the LUN (logical unit) for that device. The default LUN is 0.
|
||||
For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer). The LUN is limited from 0-31. Note that SASI is considered rare and only used on very early Sharp X68000 computers.
|
||||
.PP
|
||||
RaSCSI will determin the type of device based upon the file extension of the FILE argument.
|
||||
RaSCSI will determine the type of device based upon the file extension of the FILE argument.
|
||||
hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used with X68000)
|
||||
hds: SCSI Hard Disk image (XM6 SCSI HD image - typically only used with X68000)
|
||||
hds: SCSI Hard Disk image (generic, non-removable)
|
||||
hdr: SCSI Hard Disk image (generic, removable)
|
||||
hdn: SCSI Hard Disk image (NEC GENUINE)
|
||||
hdi: SCSI Hard Disk image (Anex86 HD image)
|
||||
nhd: SCSI Hard Disk image (T98Next HD image)
|
||||
@ -26,37 +35,69 @@ RaSCSI will determin the type of device based upon the file extension of the FIL
|
||||
For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command:
|
||||
sudo rascsi -ID0 /path/to/drive/hdimage.hda
|
||||
|
||||
Once RaSCSI starts, it will open a socket (port 6868) to allow external management commands.
|
||||
If another process is using port 6868, RaSCSI will terminate, since it is likely another instance of RaSCSI.
|
||||
Once RaSCSI starts, it will open a socket (default port is 6868) to allow external management commands.
|
||||
If another process is using the rascsi port, RaSCSI will terminate, since it is likely another instance of RaSCSI.
|
||||
Once RaSCSI has initialized, the rasctl utility can be used to send commands.
|
||||
|
||||
To quit RaSCSI, press Control + C. If it is running in the background, you can kill it using an INT signal.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-ID\fIn " " \fIFILE
|
||||
n is the SCSI ID number (0-7)
|
||||
.BR \-b\fI " " \fIBLOCK_SIZE
|
||||
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
|
||||
.TP
|
||||
.BR \-F\fI " " \fIFOLDER
|
||||
The default folder for image files. For files in this folder no absolute path needs to be specified. The initial default folder is '~/images'.
|
||||
.TP
|
||||
.BR \-L\fI " " \fILOG_LEVEL
|
||||
The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'.
|
||||
.TP
|
||||
.BR \-h\fI " " \fI
|
||||
Show a help page.
|
||||
.TP
|
||||
.BR \-n\fI " " \fIVENDOR:PRODUCT:REVISION
|
||||
Set the vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed.
|
||||
.TP
|
||||
.BR \-p\fI " " \fIPORT
|
||||
The rascsi server port, default is 6868.
|
||||
.TP
|
||||
.BR \-r\fI " " \fIRESERVED_IDS
|
||||
Comma-separated list of IDs to reserve.
|
||||
.BR \-p\fI " " \fITYPE
|
||||
The optional case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP). If no type is specified for devices that support an image file, rascsi tries to derive the type from the file extension.
|
||||
.TP
|
||||
.BR \-v\fI " " \fI
|
||||
Display the rascsi version.
|
||||
.TP
|
||||
.BR \-ID\fIn[:u] " " \fIFILE
|
||||
n is the SCSI ID number (0-7). u (0-31) is the optional LUN (logical unit). The default LUN is 0.
|
||||
.IP
|
||||
FILE is the name of the image file to attach to that ID.
|
||||
FILE is the name of the image file to use for the SCSI device. For devices that do not support an image file (SCBR, SCDP) a dummy name must be provided.
|
||||
.TP
|
||||
.BR \-HD\fIn " " \fIFILE
|
||||
n is the SASI ID number (0-15)
|
||||
.BR \-HD\fIn[:u] " " \fIFILE
|
||||
n is the SASI ID number (0-15). The effective SASI ID is calculated as n/2, the effective SASI LUN is calculated is the remainder of n/2. Alternatively the n:u syntax can be used, where ns is the SASI ID (0-7) and u the LUN (0-1).
|
||||
.IP
|
||||
FILE is the name of the image file to attach to that ID.
|
||||
FILE is the name of the image file to use for the SASI device.
|
||||
.IP
|
||||
Note: SASI usage is rare, and is typically limited to early Sharp X68000 systems.
|
||||
Note: SASI usage is rare, and is typically limited to early Unix workstations and Sharp X68000 systems.
|
||||
|
||||
.SH EXAMPLES
|
||||
Launch RaSCSI with no emulated drives attached:
|
||||
rascsi
|
||||
|
||||
Launch RaSCSI with an Apple hard drive image as ID0 and a CD-ROM as ID 2
|
||||
Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID 2
|
||||
rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso
|
||||
|
||||
Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw device file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter as ID 6:
|
||||
rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp DUMMY_FILENAME
|
||||
|
||||
To create an empty, 100MB HD image, use the following command:
|
||||
dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800
|
||||
|
||||
In case the fallocate command is available a much faster alternative to the dd command is:
|
||||
fallocate -l 104857600 /path/to/newimage.hda
|
||||
|
||||
.SH SEE ALSO
|
||||
rasctl(1), scsimon(1)
|
||||
rasctl(1), scsimon(1), rasdump(1), sasidump(1)
|
||||
|
||||
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
|
||||
|
335
doc/rascsi.txt
335
doc/rascsi.txt
@ -1,335 +0,0 @@
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
SCSI Target Emulator RaSCSI (*^..^*)
|
||||
for Raspberry Pi
|
||||
|
||||
Powered by XM6 TypeG Technology.
|
||||
Copyright (C) 2016-2020 GIMONS
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
□RaSCSIとは
|
||||
RaSCSIはRaspberry Piで動作するSCSIデバイス(ハードディスク,MO,CD-ROM)を
|
||||
仮想的に再現するエミュレータです。SCSIを採用したSHARPのX68000で使用する
|
||||
ことを目的として開発しました。RaSCSIを導入したRaspberry PiをX68000のSCSI
|
||||
コネクタに接続するだけで物理的なSCSIデバイスとして認識されます。
|
||||
|
||||
X68000以外にもSCSIを採用したFM TOWNSやPC98等のレトロPCでも使用できるかも
|
||||
しれません。作者はFM TOWNSとPC9821Ceで動作するところまでは確認しています。
|
||||
|
||||
RaSCSIはSCSIデバイスをエミュレートするソフトウェアに加えてRaspberry Piの
|
||||
GPIOコネクタをSCSIコネクタに変換する機構の総称を指します。
|
||||
|
||||
□動作環境
|
||||
(1)Raspberry Pi
|
||||
Raspberry Pi Zero
|
||||
Raspberry Pi Zero W
|
||||
Raspberry Pi Zero WH
|
||||
Raspberry Pi 2 Model B
|
||||
Raspberry Pi 3 Model B(推奨)
|
||||
Raspberry Pi 3 Model A+
|
||||
Raspberry Pi 3 Model B+
|
||||
Raspberry Pi 4 Model B
|
||||
|
||||
Zero/Zero W/Zero WHでは性能的に少し不安定かもしれません。
|
||||
|
||||
3 Model A+/3 Model B+/4 Model Bは高性能ですが熱の影響でCPUクロックが
|
||||
変動することがありますので対策が必要でしょう。
|
||||
|
||||
(2)OS
|
||||
RASPBIAN BUSTERで開発およびテストしています。
|
||||
RaSCSIはSCSI信号をGPIOを利用して制御しているので可能な限り低レイテンシー
|
||||
の状態で使用する必要があります。したがってCUIモードで利用することを推奨
|
||||
します。
|
||||
|
||||
□SCSIコネクタとの接続方法
|
||||
状況が複雑になってきましたのでRaSCSIのホームページ上で情報提供しています。
|
||||
このドキュメントの最後にある公式ホームページを参考にして下さい。
|
||||
|
||||
□配布物
|
||||
配布するアーカイブには実行ファイル、ドキュメント、ソースコードのそれぞれが
|
||||
ディレクトリで分かれて含まれています。
|
||||
|
||||
bin/ ・・・ 実行ファイル
|
||||
raspberrypi/ ・・・ RPI用のプログラム
|
||||
rascsi.tar.gz ・・・ 実行ファイルをtar+gzipしたもの。
|
||||
|
||||
x68k/ ・・・ X68000用の専用ドライバ
|
||||
RASDRIVER.XDF・・・ 二つのドライバを含むフロッピーイメージ
|
||||
RASDRIVER.HDS・・・ 二つのドライバを含むSCSI HDイメージ
|
||||
RASDRIVER.HDF・・・ 二つのドライバを含むSASI HDイメージ
|
||||
|
||||
doc/ ・・・ ドキュメント
|
||||
rascsi.txt ・・・ 当ドキュメント
|
||||
x68k.txt ・・・ X68000固有機能の説明
|
||||
converter.txt ・・・ 変換基板の説明
|
||||
pinassign.png ・・・ ピンアサイン図
|
||||
target.png ・・・ 変換基板回路図案(ターゲットモード)
|
||||
fullspec.png ・・・ 変換基板回路図案(フルスペック)
|
||||
|
||||
src/ ・・・ ソースコード
|
||||
raspberrypi/ ・・・ RPI用のプログラムソース一式
|
||||
x68k/ ・・・ X68000用のプログラム一式
|
||||
|
||||
|
||||
RPIで使用するプログラムはrascsi.tar.gzですのでRPI上に転送してから解凍して
|
||||
下さい。パーミッション等を維持するためにRPI上で解凍することを推奨します。
|
||||
|
||||
rascsi.tar.gzにはstandard,fullspec,aibom,gamerniumのディレクトリが含まれ
|
||||
ています。
|
||||
|
||||
直結ケーブルや直結基板を使用する場合はstandardディレクトリの実行ファイル
|
||||
を使用して下さい。
|
||||
|
||||
同様にフルスペック版と説明された変換基板の場合はfullspecのディレクトリの
|
||||
ものを使用します(直結でも動くと思います)。
|
||||
|
||||
aibom,gamerniumディレクトリのものは"あいぼむ版","GAMERnium版"の変換基板を
|
||||
使用する時のものです。
|
||||
|
||||
X68000用のドライバはRASDRIVER.XDFもしくはRASDRIVER.HDSの中に次の二つが含ま
|
||||
れています。
|
||||
RASDRV.SYS ・・・ ホストドライブドライバ
|
||||
RASETHER.SYS ・・・ イーサネットドライバ
|
||||
|
||||
□RASCI本体の使用方法(rascsi)
|
||||
|
||||
ID指定の場合
|
||||
rascsi [-IDn FILE] ...
|
||||
n:0~7
|
||||
|
||||
HD指定の場合(X68000 SASI機のHD指定互換)
|
||||
rascsi [-HDn FILE] ...
|
||||
n:0~15
|
||||
|
||||
ルート権限が必要ですのでsudo等で起動する必要があります。
|
||||
オプションに-hを付けると簡単なHELPが表示されます
|
||||
|
||||
Usage: rascsi [-IDn FILE] ...
|
||||
|
||||
n is SCSI identification number(0-7).
|
||||
FILE is disk image file.
|
||||
|
||||
Usage: rascsi [-HDn FILE] ...
|
||||
|
||||
n is X68000 SASI HD number(0-15).
|
||||
FILE is disk image file.
|
||||
|
||||
Image type is detected based on file extension.
|
||||
hdf : SASI HD image(XM6 SASI HD image)
|
||||
hds : SCSI HD image(XM6 SCSI HD image)
|
||||
hdn : SCSI HD image(NEC GENUINE)
|
||||
hdi : SCSI HD image(Anex86 HD image)
|
||||
nhd : SCSI HD image(T98Next HD image)
|
||||
hda : SCSI HD image(APPLE GENUINE)
|
||||
mos : SCSI MO image(XM6 SCSI MO image)
|
||||
iso : SCSI CD image(ISO 9660 image)
|
||||
|
||||
引数では-IDnもしくは-HDnとFILEの一組で一つのSCSI(SASI)デバイスを指定できます。
|
||||
-IDの後ろの番号はSCSI(SASI) IDです。IDは0-7を指定できますが通常レトロPC本体
|
||||
がイニシエータとしてID7等を使用していると思います。その場合は0-6を指定する
|
||||
ことになります。
|
||||
|
||||
FILEは仮想ディスクイメージのファイルパスです。イメージファイル名には拡張子
|
||||
が必要です。拡張子によってHD,MO,CDの種別を判定しています。
|
||||
|
||||
例)SCSI ID0にHDIMAGE0.HDS,ID1にHDIMAGE1.HDSを指定して起動する場合、
|
||||
sudo rascsi -ID0 HDIMAGE0.HDS -ID1 HDIMAGE1.HDS
|
||||
|
||||
終了する場合はCTRL+Cで停止します。
|
||||
バックグラウンドで起動した場合にはkillコマンド該当プロセスにINTシグナルか
|
||||
HUPシグナルを送ることで終了します。
|
||||
|
||||
rascsiは起動後にソケット(6868ポート)を開いて外部からの管理コマンドを受け
|
||||
付ける状態になります。したがって既に別プロセスとしてrascsiが起動している
|
||||
場合はエラーメッセージとともに起動を中断します。
|
||||
|
||||
□管理ツールの使用方法(rasctl)
|
||||
バージョン1.10からrasctlという管理ツールを提供します。これはrascsiプロセス
|
||||
がバックグラウンドで起動(6868ポートで接続待ちの状態)している場合にディスク
|
||||
操作のコマンドを発行することが可能となります。コマンドラインは下記の通り。
|
||||
|
||||
rasctl -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE]
|
||||
|
||||
ID : SCSI ID(0~7)
|
||||
UNIT : ユニット番号(0または1)
|
||||
CMD : 操作コマンド
|
||||
attach : ディスクを取り付ける
|
||||
detach : ディスクを取り外す
|
||||
insert : メディアを挿入する(MOまたはCDのみ)
|
||||
eject : メディアを取り出す(MOまたはCDのみ)
|
||||
protect : メディアを書き込み禁止にする(MOのみ)
|
||||
TYPE : ディスク種別
|
||||
hd : ハードディスク(SASI/SCSI)
|
||||
mo : MO(光磁気ディスク)
|
||||
cd : CDROM(CDROMドライブ)
|
||||
bridge : ブリッジデバイス
|
||||
FILE : ディスクイメージファイルのパス
|
||||
|
||||
IDは必須です。UNITは省略時は0です(SCSIの場合は0を基本とします)。
|
||||
CMDは省略時はattachと解釈します。TYPEはコマンドがattachの場合にはFILEの拡張子
|
||||
から自動判定します。FILEはTYPEを明示的に指定している場合は拡張子が異なっても
|
||||
構いません。基本的CMD,TYPEの解釈は大文字小文字を無視します。最初の1文字でのみ
|
||||
判定しています。
|
||||
|
||||
コマンド例
|
||||
rasctl -i 0 -f HDIMAGE0.HDS
|
||||
|
||||
の場合はSCSI IDは0。CMDはデフォルトでattachでありTYPEは拡張子HDSから判断
|
||||
するのでhdと推測することになりrascsi起動時のオプション指定と同等です。
|
||||
|
||||
現在の状態を確認するにために-lオプションのみを指定するとデバイス一覧が表示
|
||||
されます。コマンドラインは下記の通り。
|
||||
|
||||
rasctl -l
|
||||
|
||||
rasctl自体の起動にはルート権限は必要ありません。
|
||||
|
||||
□ディスクダンプツールの使用方法(rasdump)
|
||||
直結もしくは直結基板、またはフルスペック基板向けのサンプルプログラムです。
|
||||
|
||||
名前の通りSCSI HDDやMOのイメージをダンプ(オプションでリストア)します。
|
||||
自身のIDはBIDで指定して下さい。省略時は7を使用します。
|
||||
|
||||
rasdump -i ID [-b BID] -f FILE [-r]
|
||||
ID : ターゲットデバイスのSCSI ID
|
||||
BID : RaSCSI自身のSCSI ID
|
||||
FILE : ダンプファイル名
|
||||
-r : リストアモード
|
||||
|
||||
サンプルなので必要最低限の処理しか実装していませんので改造するなりして
|
||||
ご使用下さい。
|
||||
|
||||
□SASI専用ディスクダンプツールの使用方法(sasidump)
|
||||
rasdumpをベースにSASI専用に作成したダンプツールです。
|
||||
SASI HDイメージをダンプ(オプションでリストア)します。
|
||||
|
||||
sasidump -i ID [-u UT] [-b BSIZE] -c COUNT -f FILE [-r]
|
||||
ID : ターゲットデバイスのSASI ID
|
||||
UT : ターゲットデバイスのUNIT ID
|
||||
BSIZE: ブロックサイズ(デフォルトは512)
|
||||
COUNT: ブロック数
|
||||
FILE : ダンプファイル名
|
||||
-r : リストアモード
|
||||
|
||||
□ソースから実行ファイルをコンパイルする場合
|
||||
スタンダード版
|
||||
make CONNECT_TYPE=STANDARD
|
||||
|
||||
フルスペック版
|
||||
make CONNECT_TYPE=FULLSPEC
|
||||
|
||||
あいぼむ版
|
||||
make CONNECT_TYPE=AIBOM
|
||||
|
||||
GAMERnium版
|
||||
make CONNECT_TYPE=GAMERNIUM
|
||||
|
||||
□サポートするディスクイメージ
|
||||
(1)SCSI ハードディスク
|
||||
HDSファイル形式 (拡張子HDS/HDN/HDI/NHD/HDA)
|
||||
ファイルサイズは10MB以上4095MB以下の範囲で任意のサイズ(但し512バイト単位)
|
||||
|
||||
拡張子が"HDN"の場合はNEC純正55ボード(PC-9801-55)向けの純正ハードディスク
|
||||
エミュレーションを行います。INQUIRYで返却される情報やMODE SENSEのサイズに
|
||||
に違いがあります。
|
||||
|
||||
拡張子が"HDI","NHD"の場合はそれぞれPC98エミュレータであるAnex86及びT98Next
|
||||
のSCSIハードディスクイメージを使用するものです。HDNの時と同様に一部の情報
|
||||
がNEC用に変換されます。
|
||||
|
||||
拡張子が"HDA"の場合はAPPLE純正ハードディスクエミュレーションを行います。
|
||||
INQUIRY及びMODE SENSEで返却される情報に違いがあります。
|
||||
|
||||
(2)SASI ハードディスク
|
||||
HDFファイル形式 (拡張子HDF)
|
||||
ファイルサイズは10441728バイト、20748288バイト、41496576バイトのいずれか
|
||||
(それぞれ10MBドライブ、20MBドライブ、40MBドライブに対応)を推奨します。
|
||||
256バイト単位であれば10M~512Mの任意のファイルサイズがマウント可能です。
|
||||
|
||||
Version1.46から22437888バイトのイメージはMZ-2500/MZ-2800 MZ-1F23専用の
|
||||
20MBイメージとして認識します(ブロックサイズが1024という特殊イメージ)。
|
||||
|
||||
(3)SCSI 光磁気(MO)ディスク
|
||||
MOSファイル形式 (拡張子MOS)
|
||||
ファイルサイズは次の4種類のいずれか:
|
||||
128MBタイプ (127398912バイト)
|
||||
230MBタイプ (228518400バイト)
|
||||
540MBタイプ (533248000バイト)
|
||||
640MBタイプ (635600896バイト)
|
||||
128MB,230MB,540MBは512バイト/セクタ、640MBは2048バイト/セクタになります。
|
||||
|
||||
(4)SCSI CD-ROMディスク
|
||||
ISOファイル形式 (拡張子ISO、ISO9660ベタイメージ)
|
||||
モード1(2048バイト/セクタ)で、データのみ格納されたファイルとRAW形式で記録
|
||||
されたファイルの両方に対応しています。
|
||||
|
||||
□ディスクイメージの作成
|
||||
RaSCSI自体がX68000エミュレータであるXM6 TypeGの派生物です。従ってディスク
|
||||
イメージの作成はXM6 TypeGの「ツール」メニューから行うことを前提としています。
|
||||
もちろん先に説明した仕様に従えばdd等で空のイメージファイルを作成することも
|
||||
可能です。
|
||||
|
||||
例)100MBのHDSイメージ(空ファイル)を作る場合
|
||||
|
||||
dd if=/dev/zero of=HARDDISK.HDS bs=512 count=204800
|
||||
|
||||
□動作実績
|
||||
作者の開発環境であるX68000 PRO(内蔵SASI/純正SCSIボード)、X68030 内蔵SCSI、
|
||||
XVI Compact 内蔵SCSIで動作確認しています。Mach-2でも動作しました。
|
||||
|
||||
他にも初代X68000,ACE,EXPERT,XVI,PRO2,SUPER等で動作報告がありましたので、
|
||||
X68000シリーズはほぼ問題ないでしょう。
|
||||
|
||||
その他のレトロPCではFM TOWNSシリーズ、Apple Macintosh、MSX(MEGA-SCSI利用)
|
||||
で動作報告があります。PC98シリーズは動作したという報告も多数ありますが、
|
||||
SCSIボードによっては全く動作しないという報告もあります。
|
||||
|
||||
□活用方法
|
||||
XM6等のX68000エミュレータを使用している場合はエミュレータで環境構築したHDD
|
||||
イメージをFTP等でRaspberry Piに転送することでX68000の実機に接続できます。
|
||||
またその逆も然りで実機上に存在するファイルを格納したHDDイメージをPCにFTP等
|
||||
で転送することでエミュレータで活用することができます。
|
||||
|
||||
□ライセンス
|
||||
RaSCSIはあるがまま"AS IS"で配布されるソフトウェアです。
|
||||
|
||||
つまり使用者が受けたあらゆる損害に対して一切責任を持ちません。またソフト
|
||||
ウェアに不備もしくは不具合があったとしてもそれを修正する責任もありません。
|
||||
|
||||
RaSCSIを利用することでRaspberry PiやレトロPCが故障するリスクがあります。
|
||||
あくまで自己責任でチャレンジしてください。
|
||||
|
||||
XM6 TypeG同様に実験成果公開という性質上私のHP以外での配布を認めておりません。
|
||||
|
||||
XM6のライセンス条項を継承していますので雑誌/書籍での紹介事前の許諾が必要です。
|
||||
そもそも2019年にもなってSCSIに反応するのは限られた人だけだと思います。
|
||||
|
||||
□変換基板の頒布について
|
||||
変換基板を有償で頒布する場合は下記の条件に従う限り作者に許諾を得る必要は
|
||||
ありません。重要なことは基板を購入したユーザーに十分な情報を提供することと
|
||||
心得て下さい。
|
||||
|
||||
1.頒布価格
|
||||
基板製作費 + パーツ費用 + 運送費 +(社会通念上一般的な)手数料。
|
||||
|
||||
2.回路図
|
||||
購入者に回路図を提供して下さい。基板頒布と同時もしくは別途ホームページから
|
||||
のダウンロード等手段は自由です。
|
||||
|
||||
3.動作検証
|
||||
X68000実機環境の動作検証は必須とします。実機が手に入らない場合はX68000
|
||||
ユーザーの方に検証の協力をお願いしてもよいでしょう。動作検証は起動確認以外
|
||||
に書き込みや負荷テストをお願いします。検証結果は使用した環境やと共に公開
|
||||
して下さい。
|
||||
|
||||
□変換基板(公式版)について
|
||||
BOOTHで2019年3月以降配布しています(数に限りがありますので不定期です)。
|
||||
|
||||
□公開ホームページ
|
||||
http://retropc.net/gimons/rascsi/
|
||||
|
||||
□連絡先
|
||||
twitter https://twitter.com/kugimoto0715
|
||||
e-mail gimons.developer.works@gmail.com
|
||||
|
||||
[EOF]
|
@ -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]
|
@ -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)
|
||||
|
148
doc/rasctl.1
148
doc/rasctl.1
@ -3,12 +3,33 @@
|
||||
rasctl \- Sends management commands to the rascsi process
|
||||
.SH SYNOPSIS
|
||||
.B rasctl
|
||||
\fB\-e\fR |
|
||||
\fB\-l\fR |
|
||||
\fB\-i\fR \fIID\fR
|
||||
[\fB\-u\fR \fIUNIT\fR]
|
||||
\fB\-m\fR |
|
||||
\fB\-s\fR |
|
||||
\fB\-v\fR |
|
||||
\fB\-D\fR |
|
||||
\fB\-I\fR |
|
||||
\fB\-L\fR |
|
||||
\fB\-O\fR |
|
||||
\fB\-T\fR |
|
||||
\fB\-V\fR |
|
||||
\fB\-X\fR |
|
||||
[\fB\-C\fR \fIFILENAME:FILESIZE\fR]
|
||||
[\fB\-E\fR \fIFILENAME\fR]
|
||||
[\fB\-F\fR \fIIMAGE_FOLDER\fR]
|
||||
[\fB\-R\fR \fICURRENT_NAME:NEW_NAME\fR]
|
||||
[\fB\-c\fR \fICMD\fR]
|
||||
[\fB\-f\fR \fIFILE|PARAM\fR]
|
||||
[\fB\-g\fR \fILOG_LEVEL\fR]
|
||||
[\fB\-h\fR \fIHOST\fR]
|
||||
[\fB\-i\fR \fIID\fR
|
||||
[\fB\-n\fR \fINAME\fR]
|
||||
[\fB\-p\fR \fIPORT\fR]
|
||||
[\fB\-r\fR \fIRESERVED_IDS\fR]
|
||||
[\fB\-t\fR \fITYPE\fR]
|
||||
[\fB\-f\fR \fIFILE\fR]
|
||||
[\fB\-u\fR \fIUNIT\fR]
|
||||
[\fB\-x\fR \fICURRENT_NAME:NEW_NAME\fR]
|
||||
.SH DESCRIPTION
|
||||
.B rasctl
|
||||
Sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices.
|
||||
@ -17,55 +38,128 @@ Either the -i or -l option should be specified at one time. Not both.
|
||||
|
||||
You do NOT need root privileges to use rasctl.
|
||||
|
||||
Note: The command and type arguments are case insensitive. Only the first letter of the command/type are evaluated by the tool.
|
||||
Note: The command and type arguments are case insensitive. Only the first letter of the command/type is evaluated by the tool.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-C\fI " "\fIFILENAME:FILESIZE
|
||||
Create an image file in the default image folder with the specified name and size in bytes.
|
||||
.TP
|
||||
.BR \-D\fI
|
||||
Detach all devices.
|
||||
.TP
|
||||
.BR \-E\fI " " \fIFILENAME
|
||||
Display information on an image file.
|
||||
.TP
|
||||
.BR \-F\fI " "\fIIMAGE_FOLDER
|
||||
Set the default image folder.
|
||||
.TP
|
||||
.BR \-I\fI
|
||||
Gets the list of reserved device IDs.
|
||||
.TP
|
||||
.BR \-L\fI " "\fILOG_LEVEL
|
||||
Set the rascsi log level (trace, debug, info, warn, err, critical, off).
|
||||
.TP
|
||||
.BR \-h\fI " " \fIHOST
|
||||
The rascsi host to connect to, default is 'localhost'.
|
||||
.TP
|
||||
.BR \-e\fI
|
||||
List all images files in the default image folder.
|
||||
.TP
|
||||
.BR \-N\fI
|
||||
Lists all available network interfaces provided that they are up.
|
||||
.TP
|
||||
.BR \-O\fI
|
||||
Display the available rascsi server log levels and the current log level.
|
||||
.TP
|
||||
.BR \-l\fI
|
||||
List all of the devices that are currently being emulated by RaSCSI, as well as their current status.
|
||||
.TP
|
||||
.BR \-m\fI
|
||||
List all file extensions recognized by RaSCSI and the device types they map to.
|
||||
.TP
|
||||
.BR \-R\fI " "\fICURRENT_NAME:NEW_NAME
|
||||
Rename an image file in the default image folder.
|
||||
.TP
|
||||
.BR \-p\fI " " \fIPORT
|
||||
The rascsi port to connect to, default is 6868.
|
||||
.TP
|
||||
.BR \-r\fI " " \fIRESERVED_IDS
|
||||
Comma-separated list of IDs to reserve.
|
||||
.TP
|
||||
.BR \-s\fI
|
||||
Display server-side settings like available images or supported device types.
|
||||
.TP
|
||||
.BR \-T\fI
|
||||
Display all device types and their properties.
|
||||
.TP
|
||||
.BR \-v\fI " " \fI
|
||||
Display the rascsi server version.
|
||||
.TP
|
||||
.BR \-V\fI " " \fI
|
||||
Display the rasctl version.
|
||||
.TP
|
||||
.BR \-X\fI " " \fI
|
||||
Shut down the rascsi process.
|
||||
.TP
|
||||
.BR \-d\fI " "\fIFILENAME
|
||||
Delete an image file in the default image folder.
|
||||
.TP
|
||||
.BR \-x\fI " "\fICURRENT_NAME:NEW_NAME
|
||||
Copy an image file in the default image folder.
|
||||
.TP
|
||||
.BR \-i\fI " " \fIID
|
||||
ID is the SCSI ID that you want to control. (0-7)
|
||||
.TP
|
||||
.BR \-u\fI " " \fIUNIT
|
||||
Unit number (0 or 1). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common)
|
||||
.TP
|
||||
.BR \-c\fI " " \fICMD
|
||||
Command is the operation being requested. options are:
|
||||
attach: attach disk
|
||||
detach: detach disk
|
||||
insert: insert media (Magneto-Optical and CD only)
|
||||
eject: eject media (Magneto-Optical and CD only)
|
||||
protect: Write protect the media (Magneto-Optical only)
|
||||
Command is the operation being requested. Options are:
|
||||
a(ttach): Attach disk
|
||||
d(etach): Detach disk
|
||||
i(nsert): Insert media (removable media devices only)
|
||||
e(ject): Eject media (removable media devices only)
|
||||
p(rotect): Write protect the medium (not for CD-ROMs, which are always read-only)
|
||||
u(nprotect): Remove write protection from the medium (not for CD-ROMs, which are always read-only)
|
||||
s(how): Display device information
|
||||
.IP
|
||||
When the command is omited, rasctl will default to the 'attach' command
|
||||
eject, protect and unprotect are idempotent.
|
||||
.TP
|
||||
.BR \-b\fI " " \fIBLOCK_SIZE
|
||||
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
|
||||
.TP
|
||||
.BR \-f\fI " " \fIFILE|PARAM
|
||||
Device-specific: Either a path to a disk image file, or a parameter for a non-disk device. See the rascsi(1) man page for permitted file types.
|
||||
.TP
|
||||
.BR \-t\fI " " \fITYPE
|
||||
Specifies the type of disk. If this disagrees with the file extension of the specified image, the TYPE argument is ignored. Available drive types are:
|
||||
hd: Hard disk (SCSI or SASI)
|
||||
mo: Magneto-Optical disk)
|
||||
Specifies the device type. This type overrides the type derived from the file extension of the specified image. See the rascsi(1) man page for the available device types. For some types there are shortcuts (only the first letter is required):
|
||||
hd: SCSI hard disk drive
|
||||
rm: SCSI removable media drive
|
||||
cd: CD-ROM
|
||||
bridge: Bridge device (This is only applicable to the Sharp X68000)
|
||||
mo: Magneto-Optical disk
|
||||
bridge: Bridge device (Only applicable to the Sharp X68000)
|
||||
daynaport: DaynaPORT network adapter
|
||||
.TP
|
||||
.BR \-f\fI " " \fIFILE
|
||||
Path to the disk image file. See the rascsi(1) man page for allowable file types.
|
||||
.BR \-n\fI " " \fIVENDOR:PRODUCT:REVISION
|
||||
The vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed.
|
||||
.TP
|
||||
.BR \-u\fI " " \fIUNIT
|
||||
Unit number (0-31). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common)
|
||||
|
||||
.SH EXAMPLES
|
||||
Show a listing of all of the SCSI devices and their current status
|
||||
Show a listing of all of the SCSI devices and their current status.
|
||||
rasctl -l
|
||||
|
||||
|
||||
Example output:
|
||||
+----+----+------+-------------------------------------
|
||||
| ID | UN | TYPE | DEVICE STATUS
|
||||
+----+----+------+-------------------------------------
|
||||
| 0 | 1 | SCHD | /home/pi/harddisk.hda
|
||||
+----+----+------+-------------------------------------
|
||||
+----+-----+------+-------------------------------------
|
||||
| ID | LUN | TYPE | IMAGE FILE
|
||||
+----+-----+------+-------------------------------------
|
||||
| 0 | 1 | SCHD | /home/pi/harddisk.hda
|
||||
+----+-----+------+-------------------------------------
|
||||
|
||||
Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image "HDIIMAGE0.HDS".
|
||||
rasctl -i 0 -f HDIIMAGE0.HDS
|
||||
|
||||
.SH SEE ALSO
|
||||
rascsi(1) scsimon(1)
|
||||
rascsi(1), scsimon(1), rasdump(1), sasidump(1)
|
||||
|
||||
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
|
||||
|
@ -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
35
doc/rasdump.1
Normal 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
41
doc/sasidump.1
Normal 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/>
|
@ -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/>
|
||||
|
@ -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)
|
||||
|
BIN
doc/target.png
BIN
doc/target.png
Binary file not shown.
Before Width: | Height: | Size: 72 KiB |
114
doc/x68k.txt
114
doc/x68k.txt
@ -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]
|
114
doc/x68k_en.txt
114
doc/x68k_en.txt
@ -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]
|
729
easyinstall.sh
729
easyinstall.sh
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# BSD 3-Clause License
|
||||
# Author @sonique6784
|
||||
@ -20,11 +20,43 @@ logo="""
|
||||
echo -e $logo
|
||||
}
|
||||
|
||||
VIRTUAL_DRIVER_PATH=/home/pi/images
|
||||
function showMacNetworkWired(){
|
||||
logo="""
|
||||
.-~-.-~~~-.~-.\n
|
||||
╔═══════╗ .( )\n
|
||||
║|¯¯¯¯¯|║ / \`.\n
|
||||
║|_____|║>--------------<~ . )\n
|
||||
║ . __ ║ ( :'-'\n
|
||||
╚╦═════╦╝ ~-.________.:'\n
|
||||
¯¯¯¯¯¯¯\n
|
||||
"""
|
||||
echo -e $logo
|
||||
}
|
||||
|
||||
function showMacNetworkWireless(){
|
||||
logo="""
|
||||
.-~-.-~~~-.~-.\n
|
||||
╔═══════╗ .( .( )\n
|
||||
║|¯¯¯¯¯|║ .( .( / \`.\n
|
||||
║|_____|║ .o o ~ . )\n
|
||||
║ . __ ║ '( '( ( :'-'\n
|
||||
╚╦═════╦╝ '( ~-.________.:'\n
|
||||
¯¯¯¯¯¯¯\n
|
||||
"""
|
||||
echo -e $logo
|
||||
}
|
||||
|
||||
BASE="$HOME/RASCSI"
|
||||
VIRTUAL_DRIVER_PATH="$HOME/images"
|
||||
CFG_PATH="$HOME/.config/rascsi"
|
||||
WEBINSTDIR="$BASE/src/web"
|
||||
HFS_FORMAT=/usr/bin/hformat
|
||||
HFDISK_BIN=/usr/bin/hfdisk
|
||||
LIDO_DRIVER=~/RASCSI/lido-driver.img
|
||||
LIDO_DRIVER=$BASE/lido-driver.img
|
||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
GIT_REMOTE=${GIT_REMOTE:-origin}
|
||||
|
||||
set -e
|
||||
|
||||
function initialChecks() {
|
||||
currentUser=$(whoami)
|
||||
@ -33,30 +65,36 @@ function initialChecks() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d ~/RASCSI ]; then
|
||||
echo "You must checkout RASCSI repo into /home/pi/RASCSI"
|
||||
if [ ! -d "$BASE" ]; then
|
||||
echo "You must checkout RASCSI repo into $BASE"
|
||||
echo "$ git clone git@github.com:akuker/RASCSI.git"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
# install all dependency packages for RaSCSI Service
|
||||
function installPackages() {
|
||||
sudo apt-get update && sudo apt install git libspdlog-dev genisoimage python3 python3-venv nginx bridge-utils -y
|
||||
sudo apt-get update && sudo apt-get install git libspdlog-dev libpcap-dev genisoimage python3 python3-venv nginx libpcap-dev protobuf-compiler bridge-utils python3-dev libev-dev libevdev2 -y </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
261
src/loopback_test/test.py
Executable 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()
|
4
src/oled_monitor/fonts.README
Normal file
4
src/oled_monitor/fonts.README
Normal 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)
|
17
src/oled_monitor/monitor_rascsi.service
Normal file
17
src/oled_monitor/monitor_rascsi.service
Normal 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
|
@ -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...")
|
||||
|
@ -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
|
||||
|
BIN
src/oled_monitor/splash_start.bmp
Normal file
BIN
src/oled_monitor/splash_start.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 574 B |
BIN
src/oled_monitor/splash_stop.bmp
Normal file
BIN
src/oled_monitor/splash_stop.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 574 B |
@ -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}"
|
||||
|
BIN
src/oled_monitor/type_writer.ttf
Normal file
BIN
src/oled_monitor/type_writer.ttf
Normal file
Binary file not shown.
5
src/raspberrypi/.gitignore
vendored
5
src/raspberrypi/.gitignore
vendored
@ -12,3 +12,8 @@ sasidump
|
||||
rasdump
|
||||
obj
|
||||
bin
|
||||
/rascsi_interface.pb.cpp
|
||||
/rascsi_interface.pb.h
|
||||
.project
|
||||
.cproject
|
||||
.settings
|
||||
|
6
src/raspberrypi/.vscode/launch.json
vendored
6
src/raspberrypi/.vscode/launch.json
vendored
@ -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 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
4
src/raspberrypi/.vscode/tasks.json
vendored
4
src/raspberrypi/.vscode/tasks.json
vendored
@ -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 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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
@ -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
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
162
src/raspberrypi/devices/device.cpp
Normal file
162
src/raspberrypi/devices/device.cpp
Normal 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;
|
||||
}
|
185
src/raspberrypi/devices/device.h
Normal file
185
src/raspberrypi/devices/device.h
Normal 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"; }
|
||||
};
|
274
src/raspberrypi/devices/device_factory.cpp
Normal file
274
src/raspberrypi/devices/device_factory.cpp
Normal 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;
|
||||
}
|
58
src/raspberrypi/devices/device_factory.h
Normal file
58
src/raspberrypi/devices/device_factory.h
Normal 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
@ -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);
|
||||
};
|
||||
|
656
src/raspberrypi/devices/disk_track_cache.cpp
Normal file
656
src/raspberrypi/devices/disk_track_cache.cpp
Normal file
@ -0,0 +1,656 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// X68000 EMULATOR "XM6"
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
//
|
||||
// XM6i
|
||||
// Copyright (C) 2010-2015 isaki@NetBSD.org
|
||||
// Copyright (C) 2010 Y.Sugahara
|
||||
//
|
||||
// Imported sava's Anex86/T98Next image and MO format support patch.
|
||||
// Imported NetBSD support and some optimisation patch by Rin Okuyama.
|
||||
// Comments translated to english by akuker.
|
||||
//
|
||||
// [ DiskTrack and DiskCache ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "log.h"
|
||||
#include "fileio.h"
|
||||
#include "disk_track_cache.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Disk Track
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
DiskTrack::DiskTrack()
|
||||
{
|
||||
// Initialization of internal information
|
||||
dt.track = 0;
|
||||
dt.size = 0;
|
||||
dt.sectors = 0;
|
||||
dt.raw = FALSE;
|
||||
dt.init = FALSE;
|
||||
dt.changed = FALSE;
|
||||
dt.length = 0;
|
||||
dt.buffer = NULL;
|
||||
dt.maplen = 0;
|
||||
dt.changemap = NULL;
|
||||
dt.imgoffset = 0;
|
||||
}
|
||||
|
||||
DiskTrack::~DiskTrack()
|
||||
{
|
||||
// Release memory, but do not save automatically
|
||||
if (dt.buffer) {
|
||||
free(dt.buffer);
|
||||
dt.buffer = NULL;
|
||||
}
|
||||
if (dt.changemap) {
|
||||
free(dt.changemap);
|
||||
dt.changemap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Initialization
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void DiskTrack::Init(int track, int size, int sectors, BOOL raw, off_t imgoff)
|
||||
{
|
||||
ASSERT(track >= 0);
|
||||
ASSERT((sectors > 0) && (sectors <= 0x100));
|
||||
ASSERT(imgoff >= 0);
|
||||
|
||||
// Set Parameters
|
||||
dt.track = track;
|
||||
dt.size = size;
|
||||
dt.sectors = sectors;
|
||||
dt.raw = raw;
|
||||
|
||||
// Not initialized (needs to be loaded)
|
||||
dt.init = FALSE;
|
||||
|
||||
// Not Changed
|
||||
dt.changed = FALSE;
|
||||
|
||||
// Offset to actual data
|
||||
dt.imgoffset = imgoff;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Load
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskTrack::Load(const Filepath& path)
|
||||
{
|
||||
Fileio fio;
|
||||
|
||||
// Not needed if already loaded
|
||||
if (dt.init) {
|
||||
ASSERT(dt.buffer);
|
||||
ASSERT(dt.changemap);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate offset (previous tracks are considered to hold 256 sectors)
|
||||
off_t offset = ((off_t)dt.track << 8);
|
||||
if (dt.raw) {
|
||||
ASSERT(dt.size == 11);
|
||||
offset *= 0x930;
|
||||
offset += 0x10;
|
||||
} else {
|
||||
offset <<= dt.size;
|
||||
}
|
||||
|
||||
// Add offset to real image
|
||||
offset += dt.imgoffset;
|
||||
|
||||
// Calculate length (data size of this track)
|
||||
int length = dt.sectors << dt.size;
|
||||
|
||||
// Allocate buffer memory
|
||||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||
|
||||
if (dt.buffer == NULL) {
|
||||
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
|
||||
LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__);
|
||||
}
|
||||
dt.length = length;
|
||||
}
|
||||
|
||||
if (!dt.buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reallocate if the buffer length is different
|
||||
if (dt.length != (DWORD)length) {
|
||||
free(dt.buffer);
|
||||
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
|
||||
LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__);
|
||||
}
|
||||
dt.length = length;
|
||||
}
|
||||
|
||||
// Reserve change map memory
|
||||
if (dt.changemap == NULL) {
|
||||
dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL));
|
||||
dt.maplen = dt.sectors;
|
||||
}
|
||||
|
||||
if (!dt.changemap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reallocate if the buffer length is different
|
||||
if (dt.maplen != (DWORD)dt.sectors) {
|
||||
free(dt.changemap);
|
||||
dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL));
|
||||
dt.maplen = dt.sectors;
|
||||
}
|
||||
|
||||
// Clear changemap
|
||||
memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL));
|
||||
|
||||
// Read from File
|
||||
if (!fio.OpenDIO(path, Fileio::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
if (dt.raw) {
|
||||
// Split Reading
|
||||
for (int i = 0; i < dt.sectors; i++) {
|
||||
// Seek
|
||||
if (!fio.Seek(offset)) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read
|
||||
if (!fio.Read(&dt.buffer[i << dt.size], 1 << dt.size)) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Next offset
|
||||
offset += 0x930;
|
||||
}
|
||||
} else {
|
||||
// Continuous reading
|
||||
if (!fio.Seek(offset)) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
if (!fio.Read(dt.buffer, length)) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
fio.Close();
|
||||
|
||||
// Set a flag and end normally
|
||||
dt.init = TRUE;
|
||||
dt.changed = FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Save
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskTrack::Save(const Filepath& path)
|
||||
{
|
||||
// Not needed if not initialized
|
||||
if (!dt.init) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not needed unless changed
|
||||
if (!dt.changed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Need to write
|
||||
ASSERT(dt.buffer);
|
||||
ASSERT(dt.changemap);
|
||||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||
|
||||
// Writing in RAW mode is not allowed
|
||||
ASSERT(!dt.raw);
|
||||
|
||||
// Calculate offset (previous tracks are considered to hold 256 sectors)
|
||||
off_t offset = ((off_t)dt.track << 8);
|
||||
offset <<= dt.size;
|
||||
|
||||
// Add offset to real image
|
||||
offset += dt.imgoffset;
|
||||
|
||||
// Calculate length per sector
|
||||
int length = 1 << dt.size;
|
||||
|
||||
// Open file
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadWrite)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Partial write loop
|
||||
int total;
|
||||
for (int i = 0; i < dt.sectors;) {
|
||||
// If changed
|
||||
if (dt.changemap[i]) {
|
||||
// Initialize write size
|
||||
total = 0;
|
||||
|
||||
// Seek
|
||||
if (!fio.Seek(offset + ((off_t)i << dt.size))) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Consectutive sector length
|
||||
int j;
|
||||
for (j = i; j < dt.sectors; j++) {
|
||||
// end when interrupted
|
||||
if (!dt.changemap[j]) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Add one sector
|
||||
total += length;
|
||||
}
|
||||
|
||||
// Write
|
||||
if (!fio.Write(&dt.buffer[i << dt.size], total)) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// To unmodified sector
|
||||
i = j;
|
||||
} else {
|
||||
// Next Sector
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Close
|
||||
fio.Close();
|
||||
|
||||
// Drop the change flag and exit
|
||||
memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL));
|
||||
dt.changed = FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Read Sector
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskTrack::Read(BYTE *buf, int sec) const
|
||||
{
|
||||
ASSERT(buf);
|
||||
ASSERT((sec >= 0) & (sec < 0x100));
|
||||
|
||||
LOGTRACE("%s reading sector: %d", __PRETTY_FUNCTION__,sec);
|
||||
// Error if not initialized
|
||||
if (!dt.init) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// // Error if the number of sectors exceeds the valid number
|
||||
if (sec >= dt.sectors) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy
|
||||
ASSERT(dt.buffer);
|
||||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||
memcpy(buf, &dt.buffer[(off_t)sec << dt.size], (off_t)1 << dt.size);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Write Sector
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskTrack::Write(const BYTE *buf, int sec)
|
||||
{
|
||||
ASSERT(buf);
|
||||
ASSERT((sec >= 0) & (sec < 0x100));
|
||||
ASSERT(!dt.raw);
|
||||
|
||||
// Error if not initialized
|
||||
if (!dt.init) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// // Error if the number of sectors exceeds the valid number
|
||||
if (sec >= dt.sectors) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate offset and length
|
||||
int offset = sec << dt.size;
|
||||
int length = 1 << dt.size;
|
||||
|
||||
// Compare
|
||||
ASSERT(dt.buffer);
|
||||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||
if (memcmp(buf, &dt.buffer[offset], length) == 0) {
|
||||
// Exit normally since it's attempting to write the same thing
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy, change
|
||||
memcpy(&dt.buffer[offset], buf, length);
|
||||
dt.changemap[sec] = TRUE;
|
||||
dt.changed = TRUE;
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Disk Cache
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DiskCache::DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff)
|
||||
{
|
||||
ASSERT(blocks > 0);
|
||||
ASSERT(imgoff >= 0);
|
||||
|
||||
// Cache work
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
cache[i].disktrk = NULL;
|
||||
cache[i].serial = 0;
|
||||
}
|
||||
|
||||
// Other
|
||||
serial = 0;
|
||||
sec_path = path;
|
||||
sec_size = size;
|
||||
sec_blocks = blocks;
|
||||
cd_raw = FALSE;
|
||||
imgoffset = imgoff;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DiskCache::~DiskCache()
|
||||
{
|
||||
// Clear the track
|
||||
Clear();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// RAW Mode Setting
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void DiskCache::SetRawMode(BOOL raw)
|
||||
{
|
||||
// Configuration
|
||||
cd_raw = raw;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Save
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskCache::Save()
|
||||
{
|
||||
// Save track
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
// Is it a valid track?
|
||||
if (cache[i].disktrk) {
|
||||
// Save
|
||||
if (!cache[i].disktrk->Save(sec_path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Get disk cache information
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskCache::GetCache(int index, int& track, DWORD& aserial) const
|
||||
{
|
||||
ASSERT((index >= 0) && (index < CacheMax));
|
||||
|
||||
// false if unused
|
||||
if (!cache[index].disktrk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set track and serial
|
||||
track = cache[index].disktrk->GetTrack();
|
||||
aserial = cache[index].serial;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Clear
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void DiskCache::Clear()
|
||||
{
|
||||
// Free the cache
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
if (cache[i].disktrk) {
|
||||
delete cache[i].disktrk;
|
||||
cache[i].disktrk = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Sector Read
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskCache::Read(BYTE *buf, int block)
|
||||
{
|
||||
ASSERT(sec_size != 0);
|
||||
|
||||
// Update first
|
||||
Update();
|
||||
|
||||
// Calculate track (fixed to 256 sectors/track)
|
||||
int track = block >> 8;
|
||||
|
||||
// Get the track data
|
||||
DiskTrack *disktrk = Assign(track);
|
||||
if (!disktrk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the track data to the cache
|
||||
return disktrk->Read(buf, (BYTE)block);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Sector write
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskCache::Write(const BYTE *buf, int block)
|
||||
{
|
||||
ASSERT(sec_size != 0);
|
||||
|
||||
// Update first
|
||||
Update();
|
||||
|
||||
// Calculate track (fixed to 256 sectors/track)
|
||||
int track = block >> 8;
|
||||
|
||||
// Get that track data
|
||||
DiskTrack *disktrk = Assign(track);
|
||||
if (!disktrk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the data to the cache
|
||||
return disktrk->Write(buf, (BYTE)block);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Track Assignment
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DiskTrack* DiskCache::Assign(int track)
|
||||
{
|
||||
ASSERT(sec_size != 0);
|
||||
ASSERT(track >= 0);
|
||||
|
||||
// First, check if it is already assigned
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
if (cache[i].disktrk) {
|
||||
if (cache[i].disktrk->GetTrack() == track) {
|
||||
// Track match
|
||||
cache[i].serial = serial;
|
||||
return cache[i].disktrk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next, check for empty
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
if (!cache[i].disktrk) {
|
||||
// Try loading
|
||||
if (Load(i, track)) {
|
||||
// Success loading
|
||||
cache[i].serial = serial;
|
||||
return cache[i].disktrk;
|
||||
}
|
||||
|
||||
// Load failed
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, find the youngest serial number and delete it
|
||||
|
||||
// Set index 0 as candidate c
|
||||
DWORD s = cache[0].serial;
|
||||
int c = 0;
|
||||
|
||||
// Compare candidate with serial and update to smaller one
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
ASSERT(cache[i].disktrk);
|
||||
|
||||
// Compare and update the existing serial
|
||||
if (cache[i].serial < s) {
|
||||
s = cache[i].serial;
|
||||
c = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Save this track
|
||||
if (!cache[c].disktrk->Save(sec_path)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Delete this track
|
||||
DiskTrack *disktrk = cache[c].disktrk;
|
||||
cache[c].disktrk = NULL;
|
||||
|
||||
// Load
|
||||
if (Load(c, track, disktrk)) {
|
||||
// Successful loading
|
||||
cache[c].serial = serial;
|
||||
return cache[c].disktrk;
|
||||
}
|
||||
|
||||
// Load failed
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Load cache
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskCache::Load(int index, int track, DiskTrack *disktrk)
|
||||
{
|
||||
ASSERT((index >= 0) && (index < CacheMax));
|
||||
ASSERT(track >= 0);
|
||||
ASSERT(!cache[index].disktrk);
|
||||
|
||||
// Get the number of sectors on this track
|
||||
int sectors = sec_blocks - (track << 8);
|
||||
ASSERT(sectors > 0);
|
||||
if (sectors > 0x100) {
|
||||
sectors = 0x100;
|
||||
}
|
||||
|
||||
// Create a disk track
|
||||
if (disktrk == NULL) {
|
||||
disktrk = new DiskTrack();
|
||||
}
|
||||
|
||||
// Initialize disk track
|
||||
disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset);
|
||||
|
||||
// Try loading
|
||||
if (!disktrk->Load(sec_path)) {
|
||||
// Failure
|
||||
delete disktrk;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocation successful, work set
|
||||
cache[index].disktrk = disktrk;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Update serial number
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void DiskCache::Update()
|
||||
{
|
||||
// Update and do nothing except 0
|
||||
serial++;
|
||||
if (serial != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear serial of all caches (loop in 32bit)
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
cache[i].serial = 0;
|
||||
}
|
||||
}
|
||||
|
95
src/raspberrypi/devices/disk_track_cache.h
Normal file
95
src/raspberrypi/devices/disk_track_cache.h
Normal file
@ -0,0 +1,95 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// X68000 EMULATOR "XM6"
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
//
|
||||
// XM6i
|
||||
// Copyright (C) 2010-2015 isaki@NetBSD.org
|
||||
//
|
||||
// Imported sava's Anex86/T98Next image and MO format support patch.
|
||||
// Comments translated to english by akuker.
|
||||
//
|
||||
// [ DiskTrack and DiskCache ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../rascsi.h"
|
||||
#include "filepath.h"
|
||||
|
||||
// Number of tracks to cache
|
||||
#define CacheMax 16
|
||||
|
||||
class DiskTrack
|
||||
{
|
||||
private:
|
||||
struct {
|
||||
int track; // Track Number
|
||||
int size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
|
||||
int sectors; // Number of sectors(<0x100)
|
||||
DWORD length; // Data buffer length
|
||||
BYTE *buffer; // Data buffer
|
||||
BOOL init; // Is it initilized?
|
||||
BOOL changed; // Changed flag
|
||||
DWORD maplen; // Changed map length
|
||||
BOOL *changemap; // Changed map
|
||||
BOOL raw; // RAW mode flag
|
||||
off_t imgoffset; // Offset to actual data
|
||||
} dt;
|
||||
|
||||
public:
|
||||
DiskTrack();
|
||||
~DiskTrack();
|
||||
|
||||
void Init(int track, int size, int sectors, BOOL raw = FALSE, off_t imgoff = 0);
|
||||
bool Load(const Filepath& path);
|
||||
bool Save(const Filepath& path);
|
||||
|
||||
// Read / Write
|
||||
bool Read(BYTE *buf, int sec) const; // Sector Read
|
||||
bool Write(const BYTE *buf, int sec); // Sector Write
|
||||
|
||||
int GetTrack() const { return dt.track; } // Get track
|
||||
};
|
||||
|
||||
class DiskCache
|
||||
{
|
||||
public:
|
||||
// Internal data definition
|
||||
typedef struct {
|
||||
DiskTrack *disktrk; // Disk Track
|
||||
DWORD serial; // Serial
|
||||
} cache_t;
|
||||
|
||||
public:
|
||||
DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff = 0);
|
||||
~DiskCache();
|
||||
|
||||
void SetRawMode(BOOL raw); // CD-ROM raw mode setting
|
||||
|
||||
// Access
|
||||
bool Save(); // Save and release all
|
||||
bool Read(BYTE *buf, int block); // Sector Read
|
||||
bool Write(const BYTE *buf, int block); // Sector Write
|
||||
bool GetCache(int index, int& track, DWORD& serial) const; // Get cache information
|
||||
|
||||
private:
|
||||
// Internal Management
|
||||
void Clear(); // Clear all tracks
|
||||
DiskTrack* Assign(int track); // Load track
|
||||
bool Load(int index, int track, DiskTrack *disktrk = NULL); // Load track
|
||||
void Update(); // Update serial number
|
||||
|
||||
// Internal data
|
||||
cache_t cache[CacheMax]; // Cache management
|
||||
DWORD serial; // Last serial number
|
||||
Filepath sec_path; // Path
|
||||
int sec_size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
|
||||
int sec_blocks; // Blocks per sector
|
||||
BOOL cd_raw; // CD-ROM RAW mode
|
||||
off_t imgoffset; // Offset to actual data
|
||||
};
|
||||
|
43
src/raspberrypi/devices/file_support.cpp
Normal file
43
src/raspberrypi/devices/file_support.cpp
Normal 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();
|
||||
}
|
46
src/raspberrypi/devices/file_support.h
Normal file
46
src/raspberrypi/devices/file_support.h
Normal 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;
|
||||
};
|
48
src/raspberrypi/devices/interfaces/scsi_block_commands.h
Normal file
48
src/raspberrypi/devices/interfaces/scsi_block_commands.h
Normal 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;
|
||||
};
|
25
src/raspberrypi/devices/interfaces/scsi_mmc_commands.h
Normal file
25
src/raspberrypi/devices/interfaces/scsi_mmc_commands.h
Normal 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;
|
||||
};
|
34
src/raspberrypi/devices/interfaces/scsi_primary_commands.h
Normal file
34
src/raspberrypi/devices/interfaces/scsi_primary_commands.h
Normal 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;
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
660
src/raspberrypi/devices/scsi_daynaport.cpp
Normal file
660
src/raspberrypi/devices/scsi_daynaport.cpp
Normal 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 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ Emulation of the DaynaPort SCSI Link Ethernet interface ]
|
||||
//
|
||||
// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's
|
||||
// Tiny SCSI Emulator
|
||||
// - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT
|
||||
// - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator
|
||||
//
|
||||
// Additional documentation and clarification is available at the
|
||||
// following link:
|
||||
// - https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link
|
||||
//
|
||||
// This does NOT include the file system functionality that is present
|
||||
// in the Sharp X68000 host bridge.
|
||||
//
|
||||
// Note: This requires a DaynaPort SCSI Link driver.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "scsi_daynaport.h"
|
||||
#include <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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
161
src/raspberrypi/devices/scsi_daynaport.h
Normal file
161
src/raspberrypi/devices/scsi_daynaport.h
Normal 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 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ Emulation of the DaynaPort SCSI Link Ethernet interface ]
|
||||
//
|
||||
// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's
|
||||
// Tiny SCSI Emulator
|
||||
// - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT
|
||||
// - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator
|
||||
//
|
||||
// Special thanks to @PotatoFi for loaning me his Farallon EtherMac for
|
||||
// this development. (Farallon's EtherMac is a re-branded DaynaPort
|
||||
// SCSI/Link-T).
|
||||
//
|
||||
// This does NOT include the file system functionality that is present
|
||||
// in the Sharp X68000 host bridge.
|
||||
//
|
||||
// Note: This requires the DaynaPort SCSI Link driver.
|
||||
//---------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "os.h"
|
||||
#include "disk.h"
|
||||
#include "ctapdriver.h"
|
||||
#include <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
@ -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
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -1,93 +0,0 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(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;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(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
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
};
|
||||
|
49
src/raspberrypi/exceptions.h
Normal file
49
src/raspberrypi/exceptions.h
Normal 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() {}
|
||||
};
|
@ -4,159 +4,114 @@
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2010-2020 GIMONS
|
||||
// [ ファイルI/O(RaSCSI用サブセット) ]
|
||||
// [ File I/O (Subset for RaSCSI) ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "filepath.h"
|
||||
#include "fileio.h"
|
||||
#include "rascsi.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ファイルI/O
|
||||
// File I/O
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
#ifndef BAREMETAL
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// コンストラクタ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Fileio::Fileio()
|
||||
{
|
||||
// ワーク初期化
|
||||
// Initialize work
|
||||
handle = -1;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// デストラクタ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Fileio::~Fileio()
|
||||
{
|
||||
ASSERT(handle == -1);
|
||||
|
||||
// Releaseでの安全策
|
||||
// Safety measure for Release
|
||||
Close();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ロード
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Load(const Filepath& path, void *buffer, int size)
|
||||
BOOL Fileio::Load(const Filepath& path, void *buffer, int size)
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(buffer);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(handle < 0);
|
||||
|
||||
// オープン
|
||||
if (!Open(path, ReadOnly)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// 読み込み
|
||||
if (!Read(buffer, size)) {
|
||||
Close();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// クローズ
|
||||
Close();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// セーブ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Save(const Filepath& path, void *buffer, int size)
|
||||
BOOL Fileio::Save(const Filepath& path, void *buffer, int size)
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(buffer);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(handle < 0);
|
||||
|
||||
// オープン
|
||||
if (!Open(path, WriteOnly)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// 書き込み
|
||||
if (!Write(buffer, size)) {
|
||||
Close();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// クローズ
|
||||
Close();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO)
|
||||
BOOL Fileio::Open(const char *fname, OpenMode mode, BOOL directIO)
|
||||
{
|
||||
mode_t omode;
|
||||
|
||||
ASSERT(this);
|
||||
ASSERT(fname);
|
||||
ASSERT(handle < 0);
|
||||
|
||||
// ヌル文字列からの読み込みは必ず失敗させる
|
||||
// Always fail a read from a null array
|
||||
if (fname[0] == _T('\0')) {
|
||||
handle = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// デフォルトモード
|
||||
// Default mode
|
||||
omode = directIO ? O_DIRECT : 0;
|
||||
|
||||
// モード別
|
||||
switch (mode) {
|
||||
// 読み込みのみ
|
||||
case ReadOnly:
|
||||
handle = open(fname, O_RDONLY | omode);
|
||||
break;
|
||||
|
||||
// 書き込みのみ
|
||||
case WriteOnly:
|
||||
handle = open(fname, O_CREAT | O_WRONLY | O_TRUNC | omode, 0666);
|
||||
break;
|
||||
|
||||
// 読み書き両方
|
||||
case ReadWrite:
|
||||
// CD-ROMからの読み込みはRWが成功してしまう
|
||||
// Make sure RW does not succeed when reading from CD-ROM
|
||||
if (access(fname, 0x06) != 0) {
|
||||
return FALSE;
|
||||
}
|
||||
handle = open(fname, O_RDWR | omode);
|
||||
break;
|
||||
|
||||
// アペンド
|
||||
case Append:
|
||||
handle = open(fname, O_CREAT | O_WRONLY | O_APPEND | omode, 0666);
|
||||
break;
|
||||
|
||||
// それ以外
|
||||
default:
|
||||
ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
// 結果評価
|
||||
// Evaluate results
|
||||
if (handle == -1) {
|
||||
return FALSE;
|
||||
}
|
||||
@ -165,75 +120,44 @@ BOOL FASTCALL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Open(LPCTSTR fname, OpenMode mode)
|
||||
BOOL Fileio::Open(const char *fname, OpenMode mode)
|
||||
{
|
||||
ASSERT(this);
|
||||
|
||||
return Open(fname, mode, FALSE);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Open(const Filepath& path, OpenMode mode)
|
||||
BOOL Fileio::Open(const Filepath& path, OpenMode mode)
|
||||
{
|
||||
ASSERT(this);
|
||||
|
||||
return Open(path.GetPath(), mode);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::OpenDIO(LPCTSTR fname, OpenMode mode)
|
||||
BOOL Fileio::OpenDIO(const char *fname, OpenMode mode)
|
||||
{
|
||||
ASSERT(this);
|
||||
|
||||
// O_DIRECT付きでオープン
|
||||
// Open with included O_DIRECT
|
||||
if (!Open(fname, mode, TRUE)) {
|
||||
// 通常モードリトライ(tmpfs等)
|
||||
// Normal mode retry (tmpfs etc.)
|
||||
return Open(fname, mode, FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::OpenDIO(const Filepath& path, OpenMode mode)
|
||||
BOOL Fileio::OpenDIO(const Filepath& path, OpenMode mode)
|
||||
{
|
||||
ASSERT(this);
|
||||
|
||||
return OpenDIO(path.GetPath(), mode);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 読み込み
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Read(void *buffer, int size)
|
||||
BOOL Fileio::Read(void *buffer, int size)
|
||||
{
|
||||
int count;
|
||||
|
||||
ASSERT(this);
|
||||
ASSERT(buffer);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(handle >= 0);
|
||||
|
||||
// 読み込み
|
||||
count = read(handle, buffer, size);
|
||||
if (count != size) {
|
||||
return FALSE;
|
||||
@ -242,21 +166,14 @@ BOOL FASTCALL Fileio::Read(void *buffer, int size)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 書き込み
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Write(const void *buffer, int size)
|
||||
BOOL Fileio::Write(const void *buffer, int size)
|
||||
{
|
||||
int count;
|
||||
|
||||
ASSERT(this);
|
||||
ASSERT(buffer);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(handle >= 0);
|
||||
|
||||
// 書き込み
|
||||
count = write(handle, buffer, size);
|
||||
if (count != size) {
|
||||
return FALSE;
|
||||
@ -265,18 +182,12 @@ BOOL FASTCALL Fileio::Write(const void *buffer, int size)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// シーク
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Seek(off64_t offset, BOOL relative)
|
||||
BOOL Fileio::Seek(off_t offset, BOOL relative)
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(handle >= 0);
|
||||
ASSERT(offset >= 0);
|
||||
|
||||
// 相対シークならオフセットに現在値を追加
|
||||
// Add current position in case of relative seek
|
||||
if (relative) {
|
||||
offset += GetFilePos();
|
||||
}
|
||||
@ -288,330 +199,42 @@ BOOL FASTCALL Fileio::Seek(off64_t offset, BOOL relative)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ファイルサイズ取得
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
off64_t FASTCALL Fileio::GetFileSize()
|
||||
off_t Fileio::GetFileSize()
|
||||
{
|
||||
off64_t cur;
|
||||
off64_t end;
|
||||
off_t cur;
|
||||
off_t end;
|
||||
|
||||
ASSERT(this);
|
||||
ASSERT(handle >= 0);
|
||||
|
||||
// ファイル位置を64bitで取得
|
||||
// Get file position in 64bit
|
||||
cur = GetFilePos();
|
||||
|
||||
// ファイルサイズを64bitで取得
|
||||
// Get file size in64bitで
|
||||
end = lseek(handle, 0, SEEK_END);
|
||||
|
||||
// 位置を元に戻す
|
||||
// Return to start position
|
||||
Seek(cur);
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ファイル位置取得
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
off64_t FASTCALL Fileio::GetFilePos() const
|
||||
off_t Fileio::GetFilePos() const
|
||||
{
|
||||
off64_t pos;
|
||||
off_t pos;
|
||||
|
||||
ASSERT(this);
|
||||
ASSERT(handle >= 0);
|
||||
|
||||
// ファイル位置を64bitで取得
|
||||
// Get file position in 64bit
|
||||
pos = lseek(handle, 0, SEEK_CUR);
|
||||
return pos;
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// クローズ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void FASTCALL Fileio::Close()
|
||||
void Fileio::Close()
|
||||
{
|
||||
ASSERT(this);
|
||||
|
||||
if (handle != -1) {
|
||||
close(handle);
|
||||
handle = -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// コンストラクタ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Fileio::Fileio()
|
||||
{
|
||||
// ワーク初期化
|
||||
handle.obj.fs = 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// デストラクタ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Fileio::~Fileio()
|
||||
{
|
||||
ASSERT(!handle.obj.fs);
|
||||
|
||||
// Releaseでの安全策
|
||||
Close();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ロード
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Load(const Filepath& path, void *buffer, int size)
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(buffer);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(!handle.obj.fs);
|
||||
|
||||
// オープン
|
||||
if (!Open(path, ReadOnly)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// 読み込み
|
||||
if (!Read(buffer, size)) {
|
||||
Close();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// クローズ
|
||||
Close();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// セーブ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Save(const Filepath& path, void *buffer, int size)
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(buffer);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(!handle.obj.fs);
|
||||
|
||||
// オープン
|
||||
if (!Open(path, WriteOnly)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// 書き込み
|
||||
if (!Write(buffer, size)) {
|
||||
Close();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// クローズ
|
||||
Close();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Open(LPCTSTR fname, OpenMode mode)
|
||||
{
|
||||
FRESULT fr;
|
||||
Filepath fpath;
|
||||
ASSERT(this);
|
||||
ASSERT(fname);
|
||||
ASSERT(!handle.obj.fs);
|
||||
|
||||
// ヌル文字列からの読み込みは必ず失敗させる
|
||||
if (fname[0] == _T('\0')) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// モード別
|
||||
switch (mode) {
|
||||
// 読み込みのみ
|
||||
case ReadOnly:
|
||||
fr = f_open(&handle, fname, FA_READ);
|
||||
break;
|
||||
|
||||
// 書き込みのみ
|
||||
case WriteOnly:
|
||||
fr = f_open(&handle, fname, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
break;
|
||||
|
||||
// 読み書き両方
|
||||
case ReadWrite:
|
||||
fr = f_open(&handle, fname, FA_READ | FA_WRITE);
|
||||
break;
|
||||
|
||||
// アペンド
|
||||
case Append:
|
||||
fr = f_open(&handle, fname, FA_OPEN_APPEND | FA_WRITE);
|
||||
break;
|
||||
|
||||
// それ以外
|
||||
default:
|
||||
fr = FR_NO_PATH;
|
||||
ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
// 結果評価
|
||||
if (fr != FR_OK) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// オープン成功
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Open(const Filepath& path, OpenMode mode)
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(!handle.obj.fs);
|
||||
|
||||
return Open(path.GetPath(), mode);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 読み込み
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Read(void *buffer, int size)
|
||||
{
|
||||
FRESULT fr;
|
||||
UINT count;
|
||||
|
||||
ASSERT(this);
|
||||
ASSERT(buffer);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(handle.obj.fs);
|
||||
|
||||
// 読み込み
|
||||
fr = f_read(&handle, buffer, size, &count);
|
||||
if (fr != FR_OK || count != (unsigned int)size) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 書き込み
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Write(const void *buffer, int size)
|
||||
{
|
||||
FRESULT fr;
|
||||
UINT count;
|
||||
|
||||
ASSERT(this);
|
||||
ASSERT(buffer);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(handle.obj.fs);
|
||||
|
||||
// 書き込み
|
||||
fr = f_write(&handle, buffer, size, &count);
|
||||
if (fr != FR_OK || count != (unsigned int)size) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// シーク
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Fileio::Seek(off64_t offset, BOOL relative)
|
||||
{
|
||||
FRESULT fr;
|
||||
|
||||
ASSERT(this);
|
||||
ASSERT(offset >= 0);
|
||||
ASSERT(handle.obj.fs);
|
||||
|
||||
// 相対シークならオフセットに現在値を追加
|
||||
if (relative) {
|
||||
offset += f_tell(&handle);
|
||||
}
|
||||
|
||||
fr = f_lseek(&handle, offset);
|
||||
if (fr != FR_OK) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (f_tell(&handle) != (DWORD)offset) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ファイルサイズ取得
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
off64_t FASTCALL Fileio::GetFileSize()
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(handle.obj.fs);
|
||||
|
||||
return f_size(&handle);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ファイル位置取得
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
off64_t FASTCALL Fileio::GetFilePos() const
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(handle.obj.fs);
|
||||
|
||||
return f_tell(&handle);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// クローズ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void FASTCALL Fileio::Close()
|
||||
{
|
||||
ASSERT(this);
|
||||
|
||||
if (handle.obj.fs) {
|
||||
f_close(&handle);
|
||||
}
|
||||
}
|
||||
#endif //BAREMETAL
|
@ -4,7 +4,7 @@
|
||||
//
|
||||
// Copyright (C) 2001-2005 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2013-2020 GIMONS
|
||||
// [ ファイルI/O(RaSCSI用サブセット) ]
|
||||
// [ File I/O (Subset for RaSCSI) ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@ -13,13 +13,9 @@
|
||||
|
||||
#include "filepath.h"
|
||||
|
||||
#ifdef BAREMETAL
|
||||
#include "ff.h"
|
||||
#endif // BAREMETAL
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// マクロ(Load,Save用)
|
||||
// Macros (for Load, Save)
|
||||
//
|
||||
//===========================================================================
|
||||
#define PROP_IMPORT(f, p) \
|
||||
@ -34,67 +30,40 @@
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ファイルI/O
|
||||
// File I/O
|
||||
//
|
||||
//===========================================================================
|
||||
class Fileio
|
||||
{
|
||||
public:
|
||||
enum OpenMode {
|
||||
ReadOnly, // 読み込みのみ
|
||||
WriteOnly, // 書き込みのみ
|
||||
ReadWrite, // 読み書き両方
|
||||
Append // アペンド
|
||||
ReadOnly,
|
||||
WriteOnly,
|
||||
ReadWrite
|
||||
};
|
||||
|
||||
public:
|
||||
Fileio();
|
||||
// コンストラクタ
|
||||
virtual ~Fileio();
|
||||
// デストラクタ
|
||||
BOOL FASTCALL Load(const Filepath& path, void *buffer, int size);
|
||||
// ROM,RAMロード
|
||||
BOOL FASTCALL Save(const Filepath& path, void *buffer, int size);
|
||||
// RAMセーブ
|
||||
BOOL Load(const Filepath& path, void *buffer, int size); // Load ROM, RAM
|
||||
BOOL Save(const Filepath& path, void *buffer, int size); // Save RAM
|
||||
|
||||
BOOL FASTCALL Open(LPCTSTR fname, OpenMode mode);
|
||||
// オープン
|
||||
BOOL FASTCALL Open(const Filepath& path, OpenMode mode);
|
||||
// オープン
|
||||
#ifndef BAREMETAL
|
||||
BOOL FASTCALL OpenDIO(LPCTSTR fname, OpenMode mode);
|
||||
// オープン
|
||||
BOOL FASTCALL OpenDIO(const Filepath& path, OpenMode mode);
|
||||
// オープン
|
||||
#endif // BAREMETAL
|
||||
BOOL FASTCALL Seek(off64_t offset, BOOL relative = FALSE);
|
||||
// シーク
|
||||
BOOL FASTCALL Read(void *buffer, int size);
|
||||
// 読み込み
|
||||
BOOL FASTCALL Write(const void *buffer, int size);
|
||||
// 書き込み
|
||||
off64_t FASTCALL GetFileSize();
|
||||
// ファイルサイズ取得
|
||||
off64_t FASTCALL GetFilePos() const;
|
||||
// ファイル位置取得
|
||||
void FASTCALL Close();
|
||||
// クローズ
|
||||
#ifndef BAREMETAL
|
||||
BOOL FASTCALL IsValid() const { return (BOOL)(handle != -1); }
|
||||
#else
|
||||
BOOL FASTCALL IsValid() const { return (BOOL)(handle.obj.fs != 0); }
|
||||
#endif // BAREMETAL
|
||||
// 有効チェック
|
||||
BOOL Open(const char *fname, OpenMode mode);
|
||||
BOOL Open(const Filepath& path, OpenMode mode);
|
||||
BOOL OpenDIO(const char *fname, OpenMode mode);
|
||||
BOOL OpenDIO(const Filepath& path, OpenMode mode);
|
||||
BOOL Seek(off_t offset, BOOL relative = FALSE);
|
||||
BOOL Read(void *buffer, int size);
|
||||
BOOL Write(const void *buffer, int size);
|
||||
off_t GetFileSize();
|
||||
off_t GetFilePos() const;
|
||||
void Close();
|
||||
BOOL IsValid() const { return (BOOL)(handle != -1); }
|
||||
|
||||
private:
|
||||
#ifndef BAREMETAL
|
||||
BOOL FASTCALL Open(LPCTSTR fname, OpenMode mode, BOOL directIO);
|
||||
// オープン
|
||||
BOOL Open(const char *fname, OpenMode mode, BOOL directIO);
|
||||
|
||||
int handle; // ファイルハンドル
|
||||
#else
|
||||
FIL handle; // ファイルハンドル
|
||||
#endif // BAREMETAL
|
||||
int handle; // File handle
|
||||
};
|
||||
|
||||
#endif // fileio_h
|
||||
|
@ -4,64 +4,43 @@
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2012-2020 GIMONS
|
||||
// [ ファイルパス(サブセット) ]
|
||||
// [ File path (subset) ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "filepath.h"
|
||||
#include "fileio.h"
|
||||
#include "rascsi.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ファイルパス
|
||||
// File path
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// コンストラクタ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Filepath::Filepath()
|
||||
{
|
||||
// クリア
|
||||
// Clear
|
||||
Clear();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// デストラクタ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Filepath::~Filepath()
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 代入演算子
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Filepath& Filepath::operator=(const Filepath& path)
|
||||
{
|
||||
// パス設定(内部でSplitされる)
|
||||
// Set path (split internally)
|
||||
SetPath(path.GetPath());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// クリア
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void FASTCALL Filepath::Clear()
|
||||
void Filepath::Clear()
|
||||
{
|
||||
ASSERT(this);
|
||||
|
||||
// パスおよび各部分をクリア
|
||||
// Clear the path and each part
|
||||
m_szPath[0] = _T('\0');
|
||||
m_szDir[0] = _T('\0');
|
||||
m_szFile[0] = _T('\0');
|
||||
@ -70,124 +49,41 @@ void FASTCALL Filepath::Clear()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ファイル設定(ユーザ) MBCS用
|
||||
// File settings (user) for MBCS
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void FASTCALL Filepath::SetPath(LPCSTR path)
|
||||
void Filepath::SetPath(const char *path)
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(path);
|
||||
ASSERT(strlen(path) < _MAX_PATH);
|
||||
|
||||
// パス名コピー
|
||||
strcpy(m_szPath, (LPTSTR)path);
|
||||
// Copy pathname
|
||||
strcpy(m_szPath, (char *)path);
|
||||
|
||||
// 分離
|
||||
// Split
|
||||
Split();
|
||||
}
|
||||
|
||||
#ifdef BAREMETAL
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 互換関数(dirname) 結果は直ぐにコピーせよ
|
||||
// Split paths
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
static char dirtmp[2];
|
||||
char* dirname(char *path)
|
||||
void Filepath::Split()
|
||||
{
|
||||
char *p;
|
||||
if( path == NULL || *path == '\0' ) {
|
||||
dirtmp[0] = '.';
|
||||
dirtmp[1] = '\0';
|
||||
return dirtmp;
|
||||
}
|
||||
|
||||
p = path + strlen(path) - 1;
|
||||
while( *p == '/' ) {
|
||||
if( p == path )
|
||||
return path;
|
||||
*p-- = '\0';
|
||||
}
|
||||
|
||||
while( p >= path && *p != '/' ) {
|
||||
p--;
|
||||
}
|
||||
|
||||
if (p < path) {
|
||||
dirtmp[0] = '.';
|
||||
dirtmp[1] = '\0';
|
||||
return dirtmp;
|
||||
}
|
||||
|
||||
if (p == path) {
|
||||
dirtmp[0] = '/';
|
||||
dirtmp[1] = '\0';
|
||||
return dirtmp;
|
||||
}
|
||||
|
||||
*p = 0;
|
||||
return path;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 互換関数(basename) 結果は直ぐにコピーせよ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
static char basetmp[2];
|
||||
char* basename(char *path)
|
||||
{
|
||||
char *p;
|
||||
if( path == NULL || *path == '\0' ) {
|
||||
basetmp[0] = '/';
|
||||
basetmp[1] = '\0';
|
||||
return basetmp;
|
||||
}
|
||||
|
||||
p = path + strlen(path) - 1;
|
||||
while( *p == '/' ) {
|
||||
if( p == path ) {
|
||||
return path;
|
||||
}
|
||||
*p-- = '\0';
|
||||
}
|
||||
|
||||
while( p >= path && *p != '/' ) {
|
||||
p--;
|
||||
}
|
||||
|
||||
return p + 1;
|
||||
}
|
||||
#endif // BAREMETAL
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// パス分離
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void FASTCALL Filepath::Split()
|
||||
{
|
||||
LPTSTR pDir;
|
||||
LPTSTR pDirName;
|
||||
LPTSTR pBase;
|
||||
LPTSTR pBaseName;
|
||||
LPTSTR pExtName;
|
||||
|
||||
ASSERT(this);
|
||||
|
||||
// パーツを初期化
|
||||
// Initialize the parts
|
||||
m_szDir[0] = _T('\0');
|
||||
m_szFile[0] = _T('\0');
|
||||
m_szExt[0] = _T('\0');
|
||||
|
||||
// 分離
|
||||
pDir = strdup(m_szPath);
|
||||
pDirName = dirname(pDir);
|
||||
pBase = strdup(m_szPath);
|
||||
pBaseName = basename(pBase);
|
||||
pExtName = strrchr(pBaseName, '.');
|
||||
// Split
|
||||
char *pDir = strdup(m_szPath);
|
||||
char *pDirName = dirname(pDir);
|
||||
char *pBase = strdup(m_szPath);
|
||||
char *pBaseName = basename(pBase);
|
||||
char *pExtName = strrchr(pBaseName, '.');
|
||||
|
||||
// 転送
|
||||
// Transmit
|
||||
if (pDirName) {
|
||||
strcpy(m_szDir, pDirName);
|
||||
strcat(m_szDir, "/");
|
||||
@ -201,118 +97,36 @@ void FASTCALL Filepath::Split()
|
||||
strcpy(m_szFile, pBaseName);
|
||||
}
|
||||
|
||||
// 解放
|
||||
// Release
|
||||
free(pDir);
|
||||
free(pBase);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// パス合成
|
||||
// File name + extension acquisition
|
||||
// The returned pointer is temporary. Copy immediately.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void FASTCALL Filepath::Make()
|
||||
const char *Filepath::GetFileExt() const
|
||||
{
|
||||
ASSERT(this);
|
||||
|
||||
// パス初期化
|
||||
m_szPath[0] = _T('\0');
|
||||
|
||||
// 合成
|
||||
strncat(m_szPath, m_szDir, ARRAY_SIZE(m_szPath) - strlen(m_szPath));
|
||||
strncat(m_szPath, m_szFile, ARRAY_SIZE(m_szPath) - strlen(m_szPath));
|
||||
strncat(m_szPath, m_szExt, ARRAY_SIZE(m_szPath) - strlen(m_szPath));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// クリアされているか
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Filepath::IsClear() const
|
||||
{
|
||||
// Clear()の逆
|
||||
if ((m_szPath[0] == _T('\0')) &&
|
||||
(m_szDir[0] == _T('\0')) &&
|
||||
(m_szFile[0] == _T('\0')) &&
|
||||
(m_szExt[0] == _T('\0'))) {
|
||||
// 確かに、クリアされている
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// クリアされていない
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ショート名取得
|
||||
// ※返されるポインタは一時的なもの。すぐコピーすること
|
||||
// ※FDIDiskのdisk.nameとの関係で、文字列は最大59文字+終端とすること
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
const char* FASTCALL Filepath::GetShort() const
|
||||
{
|
||||
char szFile[_MAX_FNAME];
|
||||
char szExt[_MAX_EXT];
|
||||
|
||||
ASSERT(this);
|
||||
|
||||
// TCHAR文字列からchar文字列へ変換
|
||||
strcpy(szFile, m_szFile);
|
||||
strcpy(szExt, m_szExt);
|
||||
|
||||
// 固定バッファへ合成
|
||||
strcpy(ShortName, szFile);
|
||||
strcat(ShortName, szExt);
|
||||
|
||||
// strlenで調べたとき、最大59になるように細工
|
||||
ShortName[59] = '\0';
|
||||
|
||||
// const charとして返す
|
||||
return (const char*)ShortName;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ファイル名+拡張子取得
|
||||
// ※返されるポインタは一時的なもの。すぐコピーすること
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
LPCTSTR FASTCALL Filepath::GetFileExt() const
|
||||
{
|
||||
ASSERT(this);
|
||||
|
||||
// 固定バッファへ合成
|
||||
// Merge into static buffer
|
||||
strcpy(FileExt, m_szExt);
|
||||
|
||||
// LPCTSTRとして返す
|
||||
return (LPCTSTR)FileExt;
|
||||
// Return as LPCTSTR
|
||||
return (const char *)FileExt;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// パス比較
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Filepath::CmpPath(const Filepath& path) const
|
||||
BOOL Filepath::Save(Fileio *fio, int /*ver*/)
|
||||
{
|
||||
// パスが完全一致していればTRUE
|
||||
if (strcmp(path.GetPath(), GetPath()) == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
ASSERT(fio);
|
||||
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// セーブ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Filepath::Save(Fileio *fio, int /*ver*/)
|
||||
BOOL Filepath::Load(Fileio *fio, int /*ver*/)
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(fio);
|
||||
|
||||
return TRUE;
|
||||
@ -320,27 +134,7 @@ BOOL FASTCALL Filepath::Save(Fileio *fio, int /*ver*/)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ロード
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL FASTCALL Filepath::Load(Fileio *fio, int /*ver*/)
|
||||
{
|
||||
ASSERT(this);
|
||||
ASSERT(fio);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ショート名
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
char Filepath::ShortName[_MAX_FNAME + _MAX_DIR];
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ファイル名+拡張子
|
||||
// Filename and extension
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
TCHAR Filepath::FileExt[_MAX_FNAME + _MAX_DIR];
|
||||
|
@ -4,80 +4,49 @@
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2012-2020 GIMONS
|
||||
// [ ファイルパス(サブセット) ]
|
||||
// [ File path (subset) ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#if !defined(filepath_h)
|
||||
#define filepath_h
|
||||
#pragma once
|
||||
|
||||
#include "os.h"
|
||||
|
||||
class Fileio;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 定数定義
|
||||
// Constant definition
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define FILEPATH_MAX _MAX_PATH
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ファイルパス
|
||||
// ※代入演算子を用意すること
|
||||
// File path
|
||||
// Assignment operators are prepared here
|
||||
//
|
||||
//===========================================================================
|
||||
class Filepath
|
||||
{
|
||||
public:
|
||||
Filepath();
|
||||
// コンストラクタ
|
||||
virtual ~Filepath();
|
||||
// デストラクタ
|
||||
Filepath& operator=(const Filepath& path);
|
||||
// 代入
|
||||
|
||||
void FASTCALL Clear();
|
||||
// クリア
|
||||
void FASTCALL SetPath(LPCSTR path);
|
||||
// ファイル設定(ユーザ) MBCS用
|
||||
BOOL FASTCALL IsClear() const;
|
||||
// クリアされているか
|
||||
LPCTSTR FASTCALL GetPath() const { return m_szPath; }
|
||||
// パス名取得
|
||||
const char* FASTCALL GetShort() const;
|
||||
// ショート名取得(const char*)
|
||||
LPCTSTR FASTCALL GetFileExt() const;
|
||||
// ショート名取得(LPCTSTR)
|
||||
BOOL FASTCALL CmpPath(const Filepath& path) const;
|
||||
// パス比較
|
||||
|
||||
BOOL FASTCALL Save(Fileio *fio, int ver);
|
||||
// セーブ
|
||||
BOOL FASTCALL Load(Fileio *fio, int ver);
|
||||
// ロード
|
||||
void Clear();
|
||||
void SetPath(const char *path); // File settings (user) for MBCS
|
||||
const char *GetPath() const { return m_szPath; } // Get path name
|
||||
const char *GetFileExt() const; // Get short name (LPCTSTR)
|
||||
BOOL Save(Fileio *fio, int ver);
|
||||
BOOL Load(Fileio *fio, int ver);
|
||||
|
||||
private:
|
||||
void FASTCALL Split();
|
||||
// パス分割
|
||||
void FASTCALL Make();
|
||||
// パス合成
|
||||
void FASTCALL SetCurDir();
|
||||
// カレントディレクトリ設定
|
||||
TCHAR m_szPath[_MAX_PATH];
|
||||
// ファイルパス
|
||||
TCHAR m_szDrive[_MAX_DRIVE];
|
||||
// ドライブ
|
||||
TCHAR m_szDir[_MAX_DIR];
|
||||
// ディレクトリ
|
||||
TCHAR m_szFile[_MAX_FNAME];
|
||||
// ファイル
|
||||
TCHAR m_szExt[_MAX_EXT];
|
||||
// 拡張子
|
||||
void Split(); // Split the path
|
||||
TCHAR m_szPath[_MAX_PATH]; // File path
|
||||
TCHAR m_szDir[_MAX_DIR]; // Directory
|
||||
TCHAR m_szFile[_MAX_FNAME]; // File
|
||||
TCHAR m_szExt[_MAX_EXT]; // Extension
|
||||
|
||||
static char ShortName[_MAX_FNAME + _MAX_DIR];
|
||||
// ショート名(char)
|
||||
static TCHAR FileExt[_MAX_FNAME + _MAX_DIR];
|
||||
// ショート名(TCHAR)
|
||||
static TCHAR FileExt[_MAX_FNAME + _MAX_DIR]; // Short name (TCHAR)
|
||||
};
|
||||
|
||||
#endif // filepath_h
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
3
src/raspberrypi/launch_sudo.sh
Executable 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 "$@"
|
@ -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
|
||||
|
@ -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
|
||||
|
7
src/raspberrypi/os_integration/dhcpcd.conf.patch
Normal file
7
src/raspberrypi/os_integration/dhcpcd.conf.patch
Normal 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
|
@ -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
|
||||
|
13
src/raspberrypi/os_integration/rascsi_bridge
Normal file
13
src/raspberrypi/os_integration/rascsi_bridge
Normal 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
|
||||
|
155
src/raspberrypi/protobuf_util.cpp
Normal file
155
src/raspberrypi/protobuf_util.cpp
Normal 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());
|
||||
}
|
34
src/raspberrypi/protobuf_util.h
Normal file
34
src/raspberrypi/protobuf_util.h
Normal 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&);
|
||||
}
|
@ -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
@ -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
|
347
src/raspberrypi/rascsi_image.cpp
Normal file
347
src/raspberrypi/rascsi_image.cpp
Normal 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);
|
||||
}
|
38
src/raspberrypi/rascsi_image.h
Normal file
38
src/raspberrypi/rascsi_image.h
Normal 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;
|
||||
};
|
349
src/raspberrypi/rascsi_interface.proto
Normal file
349
src/raspberrypi/rascsi_interface.proto
Normal 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;
|
||||
}
|
344
src/raspberrypi/rascsi_response.cpp
Normal file
344
src/raspberrypi/rascsi_response.cpp
Normal 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;
|
||||
}
|
55
src/raspberrypi/rascsi_response.h
Normal file
55
src/raspberrypi/rascsi_response.h
Normal 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&);
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user