greenscsi/src/dump.ino
2021-12-11 16:59:32 -05:00

404 lines
9.5 KiB
C++
Executable File

#if SUPPORT_INITIATOR
// Take control of the SCSI bus
void initiatorTakeBus() {
pinModeFastSlew(ATN, OUTPUT_OPENDRAIN);
pinModeFastSlew(ACK, OUTPUT_OPENDRAIN);
pinModeFastSlew(RST, OUTPUT_OPENDRAIN);
pinModeFastSlew(SEL, OUTPUT_OPENDRAIN);
pinMode(BSY, INPUT_PULLUP);
pinMode(MSG, INPUT_PULLUP);
pinMode(CD, INPUT_PULLUP);
pinMode(IO, INPUT_PULLUP);
pinMode(REQ, INPUT_PULLUP);
pinModeFastSlew(DB0, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB1, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB2, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB3, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB4, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB5, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB6, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB7, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB8, OUTPUT_OPENDRAIN);
SCSI_INITIATOR_INACTIVE();
}
// Return the bus to floating, allowing other hosts to take over
void initiatorReleaseBus() {
// Input port
pinMode(ATN, INPUT_PULLUP);
pinMode(ACK, INPUT_PULLUP);
pinMode(RST, INPUT_PULLUP);
pinMode(SEL, INPUT_PULLUP);
// Output port
pinModeFastSlew(BSY, OUTPUT_OPENDRAIN);
pinModeFastSlew(MSG, OUTPUT_OPENDRAIN);
pinModeFastSlew(CD, OUTPUT_OPENDRAIN);
pinModeFastSlew(IO, OUTPUT_OPENDRAIN);
pinModeFastSlew(REQ, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB0, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB1, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB2, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB3, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB4, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB5, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB6, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB7, OUTPUT_OPENDRAIN);
pinModeFastSlew(DB8, OUTPUT_OPENDRAIN);
// Turn off the output port
SCSI_TARGET_INACTIVE();
}
// Issue a bus reset
void initiatorBusFree() {
SET_RST_ACTIVE();
delay(50);
SET_RST_INACTIVE();
delay(20);
}
// Selection phase, assert Host & Target ID bits and assert SEL, wait for BSY to be asserted by the target, or timeout.
int initiateSelection(uint8_t target_id, boolean withATN) {
int timeout = 10000;
GPIOB_PDDR = SCSI_DB_MASK;
SCSI_DB_OUTPUT((1 << target_id) | 0x80);
SET_SEL_ACTIVE();
while(timeout--) {
delay(20);
if(GET_BSY()) break;
}
if(withATN) SET_ATN_ACTIVE();
SET_SEL_INACTIVE();
SCSI_DB_INPUT();
return GET_BSY();
}
// Message Out phase, hold ATN asserted until the last message byte
int initiateMessageOut(const uint8_t *buf, uint8_t len) {
int timeout = 10000;
SET_ATN_ACTIVE();
while(timeout--) {
delay(20);
if(!GET_CD() && !GET_IO() && GET_MSG()) break;
}
if(timeout > 0) {
for(uint8_t x = 0; x < len; x++) {
timeout = 10000;
while(!GET_REQ() && (timeout--));
if(!timeout) break;
GPIOB_PDDR = SCSI_DB_MASK;
SCSI_DB_OUTPUT(buf[x]);
if((x + 1) == len)
SET_ATN_INACTIVE();
SET_ACK_ACTIVE();
timeout = 10000;
while(GET_REQ() && (timeout--));
SET_ACK_INACTIVE();
if(!timeout) break;
}
}
SET_ATN_INACTIVE();
SCSI_DB_INPUT();
return (timeout > 0);
}
// Message In phase, read a single byte from target once it's phase matches
int initiateMessageIn(uint8_t *buf) {
int timeout = 10000;
while(timeout--) {
delay(20);
if(!GET_CD() && GET_IO() && GET_MSG()) break;
}
if(timeout > 0) {
timeout = 10000;
while(!GET_REQ() && (timeout--));
if(!timeout) goto failed;
buf[0] = readIO();
SET_ACK_ACTIVE();
timeout = 10000;
while(GET_REQ() && (timeout--));
SET_ACK_INACTIVE();
}
failed:
return (timeout > 0);
}
// Command Out phase, tell the target what we want from it.
int initiateCommandOut(const uint8_t *buf, uint8_t len) {
int timeout = 10000;
while(timeout--) {
delay(20);
if(GET_CD() && !GET_IO() && !GET_MSG()) break;
}
if(timeout > 0) {
for(uint8_t x = 0; x < len; x++) {
timeout = 10000;
while(!GET_REQ() && (timeout--));
if(!timeout) break;
GPIOB_PDDR = SCSI_DB_MASK;
SCSI_DB_OUTPUT(buf[x]);
SET_ACK_ACTIVE();
timeout = 10000;
while(GET_REQ() && (timeout--));
SET_ACK_INACTIVE();
if(!timeout) break;
}
}
SCSI_DB_INPUT();
return (timeout > 0);
}
// Data Out phase, send data to target
int initiateDataOut(const uint8_t *buf, uint16_t len) {
int timeout = 10000;
while(timeout--) {
delay(20);
if(!GET_CD() && !GET_IO() && !GET_MSG()) break;
}
if(timeout > 0) {
for(uint16_t x = 0; x < len; x++) {
timeout = 10000;
while(!GET_REQ() && (timeout--));
if(!timeout) break;
GPIOB_PDDR = SCSI_DB_MASK;
SCSI_DB_OUTPUT(buf[x]);
SET_ACK_ACTIVE();
timeout = 10000;
while(GET_REQ() && (timeout--));
SET_ACK_INACTIVE();
if(!timeout) break;
}
}
SCSI_DB_INPUT();
return (timeout > 0);
}
// Data In phase, get data from target
int initiateDataIn(uint8_t *buf, uint16_t len) {
int timeout = 10000;
while(timeout--) {
delay(20);
if(!GET_CD() && GET_IO() && !GET_MSG()) break;
}
if(timeout > 0) {
for(uint16_t x = 0; x < len; x++) {
timeout = 10000;
while(!GET_REQ() && (timeout--));
if(!timeout) break;
buf[x] = readIO();
SET_ACK_ACTIVE();
timeout = 10000;
while(GET_REQ() && (timeout--));
SET_ACK_INACTIVE();
if(!timeout) break;
}
}
return (timeout > 0);
}
// Status In phase, read target's command status
int initiateStatusIn(uint8_t *buf) {
int timeout = 10000;
while(timeout--) {
delay(20);
if(GET_CD() && GET_IO() && !GET_MSG()) break;
}
if(timeout > 0) {
timeout = 10000;
while(!GET_REQ() && (timeout--));
if(!timeout) goto failed;
buf[0] = readIO();
SET_ACK_ACTIVE();
timeout = 10000;
while(GET_REQ() && (timeout--));
SET_ACK_INACTIVE();
}
failed:
return (timeout > 0);
}
int initiateReadCapacity(uint8_t target_id, uint32_t *blockSize, uint32_t *blockCount) {
initiatorTakeBus();
// Select Target
if(!initiateSelection(target_id, true)) goto failed;
// Select LUN 0
m_buf[0] = 0x80;
if(!initiateMessageOut(m_buf, 1)) goto failed;
// Execute Read Capacity
memset(m_cmd, 0x00, 10);
m_cmd[0] = CMD_READ_CAPACITY10;
if(!initiateCommandOut(m_cmd, 10)) goto failed;
// Get Data Back
if(!initiateDataIn(m_buf, 8)) goto failed;
*blockSize = (m_buf[0] << 24) | (m_buf[1] << 16) || (m_buf[2] << 8) | (m_buf[3] << 0);
*blockCount = (m_buf[4] << 24) | (m_buf[5] << 16) || (m_buf[6] << 8) | (m_buf[7] << 0);
// Get Status
if(!initiateStatusIn(m_buf)) goto failed;
// Get Messages
if(!initiateMessageIn(m_buf)) goto failed;
return 1;
failed:
initiatorBusFree();
initiatorReleaseBus();
return 0;
}
int initiateReadVolume(FsFile file, uint8_t target_id, uint32_t blockSize, uint32_t blockCount) {
uint32_t pos = 0;
initiatorTakeBus();
while(blockCount) {
uint32_t blocks = min(blockCount, (MAX_BLOCKSIZE / blockSize));
if(!initiateSelection(target_id, true)) goto failed;
// Select LUN 0
m_buf[0] = 0x80;
if(!initiateMessageOut(m_buf, 1)) goto failed;
// Execute Read
memset(m_cmd, 0x00, 10);
m_cmd[0] = CMD_READ10;
m_cmd[2] = (pos >> 24) & 0xff;
m_cmd[3] = (pos >> 16) & 0xff;
m_cmd[4] = (pos >> 8) & 0xff;
m_cmd[5] = (pos >> 0) & 0xff;
m_cmd[6] = (blocks >> 8) & 0xff;
m_cmd[7] = (blocks >> 0) & 0xff;
if(!initiateCommandOut(m_cmd, 10)) goto failed;
// Get Data Back
if(!initiateDataIn(m_buf, blockSize * blocks)) goto failed;
file.write(m_buf, blockSize * blocks);
// Get Status
if(!initiateStatusIn(m_buf)) goto failed;
// Get Messages
if(!initiateMessageIn(m_buf)) goto failed;
pos += blocks;
blockCount -= blocks;
}
return 1;
failed:
initiatorBusFree();
initiatorReleaseBus();
return 0;
}
void dumpcmd(int argc, char **argv) {
uint8_t target_id = 0xff;
uint32_t blockSize, blockCount;
char tmp_path[MAX_FILE_PATH+1];
FsFile file;
if(argc < 3) {
// Syntax error
return;
}
target_id = strtoul(argv[1], NULL, 0);
if(target_id > 7) {
Serial.print("ERROR");
if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename);
Serial.printf(": SCSI ID '%s' is not within range.\r\n", argv[1]);
return;
}
fixupPath(tmp_path, argv[2]);
if(strncmp(tmp_path, "/sd/", 4)) {
Serial.print("ERROR");
if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename);
Serial.printf(": Can only create images on the SD Card.\r\n");
return;
}
if(!file.open(tmp_path+3, O_WRONLY | O_CREAT | O_TRUNC)) {
Serial.print("ERROR");
if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename);
Serial.printf(": Unable to open '%s'.\r\n", tmp_path);
return;
}
detachInterrupt(RST);
detachInterrupt(SEL);
if(!initiateReadCapacity(target_id, &blockSize, &blockCount)) {
Serial.print("ERROR");
if(scriptlevel >= 0) Serial.printf(" on line %d of '%s'", exec_line, exec_filename);
Serial.printf(": Unable to read the capacity of SCSI target device.\r\n");
} else {
initiateReadVolume(file, target_id, blockSize, blockCount);
}
attachInterrupt(RST, onBusReset, FALLING);
attachInterrupt(SEL, SelectionPhaseISR, FALLING);
file.close();
}
#endif