| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 | /**  * Copyright (C) 2023 Eric Helgeson *  * This file is part of BlueSCSI *  * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version.  *  * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details.  *  * You should have received a copy of the GNU General Public License * along with this program.  If not, see <https://www.gnu.org/licenses/>.**/#include "BlueSCSI_Toolbox.h"#include "BlueSCSI_disk.h"#include "BlueSCSI_cdrom.h"#include "BlueSCSI_log.h"#include <minIni.h>#include <SdFat.h>extern "C" {#include <scsi2sd_time.h>#include <sd.h>#include <mode.h>}static void doCountFiles(const char * dir_name){    File dir;    File file;    char name[ MAX_MAC_PATH + 1];    dir.open(dir_name);    dir.rewindDirectory();    uint8_t file_count = 0;    while (file.openNext(&dir, O_RDONLY))    {        if(file.getError() > 0)        {            file.close();            break;        }        file.getName(name, 32 + 1);        file.close();        // only count valid files.        if(!scsiDiskFilenameValid(name))        {            file_count = file_count + 1;            if(file_count > 100) {                scsiDev.status = CHECK_CONDITION;                scsiDev.target->sense.code = ILLEGAL_REQUEST;                scsiDev.target->sense.asc = OPEN_RETRO_SCSI_TOO_MANY_FILES;                scsiDev.phase = STATUS;                dir.close();                return;            }        }  }  scsiDev.data[0] = file_count;  scsiDev.dataLen = sizeof(file_count);  scsiDev.phase = DATA_IN;}void onListFiles(const char * dir_name) {    File dir;    File file;    memset(scsiDev.data, 0, 4096);    int ENTRY_SIZE = 40;    char name[MAX_MAC_PATH + 1];    dir.open(dir_name);    dir.rewindDirectory();    uint8_t index = 0;    byte file_entry[40] = {0};    while (file.openNext(&dir, O_RDONLY))    {        uint8_t isDir = file.isDirectory() ? 0x00 : 0x01;        file.getName(name, MAX_MAC_PATH + 1);        uint64_t size = file.fileSize();        file.close();        if(!scsiDiskFilenameValid(name))            continue;        file_entry[0] = index;        file_entry[1] = isDir;        int c = 0; // Index of char in name[]        for(int i = 2; i < (MAX_MAC_PATH + 1 + 2); i++) {   // bytes 2 - 34            file_entry[i] = name[c++];        }        file_entry[35] = 0; //(size >> 32) & 0xff;        file_entry[36] = (size >> 24) & 0xff;        file_entry[37] = (size >> 16) & 0xff;        file_entry[38] = (size >> 8) & 0xff;        file_entry[39] = (size) & 0xff;        memcpy(&(scsiDev.data[ENTRY_SIZE * index]), file_entry, ENTRY_SIZE);        index = index + 1;    }    dir.close();    scsiDev.dataLen = 4096;    scsiDev.phase = DATA_IN;}File get_file_from_index(uint8_t index, const char * dir_name){  File dir;  FsFile file_test;  char name[MAX_MAC_PATH + 1];  dir.open(dir_name);  dir.rewindDirectory(); // Back to the top  int count = 0;  while (file_test.openNext(&dir, O_RDONLY))  {    // If error there is no next file to open.    if(file_test.getError() > 0) {      file_test.close();      break;    }    file_test.getName(name, MAX_MAC_PATH + 1);    if(!scsiDiskFilenameValid(name))      continue;    if (count == index)    {      dir.close();      return file_test;    }    else    {      file_test.close();    }    count++;  }  file_test.close();  dir.close();  return file_test;}// Devices that are active on this SCSI device.void onListDevices(){  for (int i = 0; i < NUM_SCSIID; i++)  {    const S2S_TargetCfg* cfg = s2s_getConfigById(i);    // id, type    if (cfg && (cfg->scsiId & S2S_CFG_TARGET_ENABLED))    {        scsiDev.data[i] = (int)cfg->deviceType; // 2 == cd    }    else    {        scsiDev.data[i] = 255; // not enabled target.    }  }  scsiDev.dataLen = 16;}void onSetNextCD(){    char name[MAX_FILE_PATH];    char full_path[MAX_FILE_PATH * 2];    uint8_t file_index = scsiDev.cdb[1];    uint8_t cd_scsi_id = scsiDev.cdb[2];    image_config_t &img = *(image_config_t*)scsiDev.target->cfg;    File next_cd = get_file_from_index(file_index, CD_IMG_DIR);    next_cd.getName(name, sizeof(name));    next_cd.close();    snprintf(full_path, (MAX_FILE_PATH * 2), "%s/%s", CD_IMG_DIR, name);    cdromSwitch(img, full_path);}File gFile; // global so we can keep it open while transfering.void onGetFile10(void) {    uint8_t index = scsiDev.cdb[1];    uint32_t offset = ((uint32_t)scsiDev.cdb[2] << 24) | ((uint32_t)scsiDev.cdb[3] << 16) | ((uint32_t)scsiDev.cdb[4] << 8) | scsiDev.cdb[5];    if (offset == 0) // first time, open the file.    {        gFile = get_file_from_index(index, "/shared");        if(!gFile.isDirectory() && !gFile.isReadable())        {            scsiDev.status = CHECK_CONDITION;            scsiDev.target->sense.code = ILLEGAL_REQUEST;            //SCSI_ASC_INVALID_FIELD_IN_CDB            scsiDev.phase = STATUS;            return;        }    }    uint32_t file_total = gFile.size();    memset(scsiDev.data, 0, 4096);    gFile.seekSet(offset * 4096);    int bytes_read = gFile.read(scsiDev.data, 4096);    if(offset * 4096 >= file_total) // transfer done, close.    {        gFile.close();    }    scsiDev.dataLen = bytes_read;    scsiDev.phase = DATA_IN;}/*  Prepares a file for receving. The file name is null terminated in the scsi data.*/File receveFile;void onSendFilePrep(void){    char file_name[32+1];    memset(file_name, '\0', 32+1);    scsiEnterPhase(DATA_OUT);    for (int i = 0; i < 32+1; ++i)    {        file_name[i] = scsiReadByte();    }    SD.chdir("/shared");    receveFile.open(file_name, FILE_WRITE);    SD.chdir("/");    if(receveFile.isOpen() && receveFile.isWritable())    {        receveFile.rewind();        receveFile.sync();        // do i need to manually set phase to status here?        return;    } else {        receveFile.close();        scsiDev.status = CHECK_CONDITION;        scsiDev.target->sense.code = ILLEGAL_REQUEST;        //SCSI_ASC_INVALID_FIELD_IN_CDB        scsiDev.phase = STATUS;    }}void onSendFileEnd(void){    receveFile.sync();    receveFile.close();    scsiDev.phase = STATUS;}void onSendFile10(void){    if(!receveFile.isOpen() || !receveFile.isWritable())    {        scsiDev.status = CHECK_CONDITION;        scsiDev.target->sense.code = ILLEGAL_REQUEST;        //SCSI_ASC_INVALID_FIELD_IN_CDB        scsiDev.phase = STATUS;    }    // Number of bytes sent this request, 1..512.    uint16_t bytes_sent = ((uint16_t)scsiDev.cdb[1] << 8)  | scsiDev.cdb[2];    // 512 byte offset of where to put these bytes.    uint32_t offset     = ((uint32_t)scsiDev.cdb[3] << 16) | ((uint32_t)scsiDev.cdb[4] << 8) | scsiDev.cdb[5];    uint16_t buf_size   = 512;    uint8_t buf[512];    // Check if last block of file, and not the only bock in file.    if(bytes_sent < buf_size)    {        buf_size = bytes_sent;    }    scsiEnterPhase(DATA_OUT);    scsiRead(buf, bytes_sent, NULL);    receveFile.seekCur(offset * 512);    receveFile.write(buf, buf_size);    if(receveFile.getWriteError())    {        receveFile.clearWriteError();        receveFile.close();        scsiDev.status = CHECK_CONDITION;        scsiDev.target->sense.code = ILLEGAL_REQUEST;    }    //scsiDev.phase = STATUS;}void onToggleDebug(){    if(g_log_debug)    {        debuglog("Turning Debug logs off.");    }    else    {        log("Turning Debug logs on.");    }    g_log_debug = !g_log_debug;    scsiDev.phase = STATUS;}extern "C" int scsiBlueSCSIToolboxCommand(){    int commandHandled = 1;    uint8_t command = scsiDev.cdb[0];     if (unlikely(command == BLUESCSI_TOOLBOX_COUNT_FILES))    {        doCountFiles("/shared");    }    else if (unlikely(command == BLUESCSI_TOOLBOX_LIST_FILES))    {        // TODO: Allow user to set dir name via ini        onListFiles("/shared");    }    else if (unlikely(command == BLUESCSI_TOOLBOX_GET_FILE))    {        onGetFile10();    }    else if (unlikely(command == BLUESCSI_TOOLBOX_SEND_FILE_PREP))    {        onSendFilePrep();    }    else if (unlikely(command == BLUESCSI_TOOLBOX_SEND_FILE_10))    {        onSendFile10();    }    else if (unlikely(command == BLUESCSI_TOOLBOX_SEND_FILE_END))    {        onSendFileEnd();    }    else if(unlikely(command == BLUESCSI_TOOLBOX_TOGGLE_DEBUG))    {        onToggleDebug();    }    else if(unlikely(command == BLUESCSI_TOOLBOX_LIST_CDS))    {       onListFiles("/CDImages");    }    else if(unlikely(command == BLUESCSI_TOOLBOX_SET_NEXT_CD))    {       onSetNextCD();    }    else if(unlikely(command == BLUESCSI_TOOLBOX_LIST_DEVICES))    {        onListDevices();    }    else if (unlikely(command == BLUESCSI_TOOLBOX_COUNT_CDS))    {        // TODO: Allow user to set dir name via ini        doCountFiles("/CDImages");    }    else    {        commandHandled = 0;    }    return commandHandled;}
 |