From bc5fbd1c051080b3e051251bda6cf8f02b2bee1b Mon Sep 17 00:00:00 2001 From: NicJA Date: Thu, 2 May 2019 22:06:44 +0000 Subject: [PATCH] WIP: add an initial skeleton for a real scsi.device based upon the ata device implementation. currently the values exposed and used are meaningless and are subject to change. move the scsicmds header into the scsi device tree and fix the ata device header to include it from the correct (system) location. the device is not yet built for any targets. git-svn-id: https://svn.aros.org/svn/aros/trunk/AROS@56378 fb15a70f-31f2-0310-bbcc-cdcc74a49acc --- rom/devs/ata/ata.h | 4 +- rom/devs/ata/mmakefile.src | 6 +- rom/devs/scsi/bootwait.c | 101 ++ rom/devs/{ata => scsi}/include/devices/scsicmds.h | 0 rom/devs/scsi/include/hardware/scsi.h | 144 +++ rom/devs/scsi/include/hidd/scsi.h | 79 ++ rom/devs/scsi/lowlevel.c | 185 ++++ rom/devs/scsi/mmakefile.src | 35 + rom/devs/scsi/scsi.c | 1084 +++++++++++++++++++++ rom/devs/scsi/scsi.conf | 136 +++ rom/devs/{ata/ata.h => scsi/scsi.h} | 227 +++-- rom/devs/scsi/scsi_bus.h | 102 ++ rom/devs/scsi/scsi_busclass.c | 1038 ++++++++++++++++++++ rom/devs/scsi/scsi_controllerclass.c | 124 +++ rom/devs/scsi/scsi_init.c | 375 +++++++ rom/devs/scsi/scsi_unitclass.c | 284 ++++++ rom/devs/scsi/timer.c | 72 ++ rom/devs/scsi/timer.h | 57 ++ rom/devs/scsi/waitnano.c | 82 ++ rom/devs/scsi/waitto.c | 51 + 20 files changed, 4065 insertions(+), 121 deletions(-) create mode 100644 rom/devs/scsi/bootwait.c rename rom/devs/{ata => scsi}/include/devices/scsicmds.h (100%) create mode 100644 rom/devs/scsi/include/hardware/scsi.h create mode 100644 rom/devs/scsi/include/hidd/scsi.h create mode 100644 rom/devs/scsi/lowlevel.c create mode 100644 rom/devs/scsi/mmakefile.src create mode 100644 rom/devs/scsi/scsi.c create mode 100644 rom/devs/scsi/scsi.conf copy rom/devs/{ata/ata.h => scsi/scsi.h} (60%) create mode 100644 rom/devs/scsi/scsi_bus.h create mode 100644 rom/devs/scsi/scsi_busclass.c create mode 100644 rom/devs/scsi/scsi_controllerclass.c create mode 100644 rom/devs/scsi/scsi_init.c create mode 100644 rom/devs/scsi/scsi_unitclass.c create mode 100644 rom/devs/scsi/timer.c create mode 100644 rom/devs/scsi/timer.h create mode 100644 rom/devs/scsi/waitnano.c create mode 100644 rom/devs/scsi/waitto.c diff --git a/rom/devs/ata/ata.h b/rom/devs/ata/ata.h index 9aed77f050..80dd7958cf 100644 --- a/rom/devs/ata/ata.h +++ b/rom/devs/ata/ata.h @@ -2,7 +2,7 @@ #define _ATA_H /* - Copyright © 2004-2018, The AROS Development Team. All rights reserved. + Copyright © 2004-2019, The AROS Development Team. All rights reserved. $Id$ Desc: ata.device main private include file @@ -31,7 +31,7 @@ #include #include -#include "include/devices/scsicmds.h" +#include #define MAX_DEVICEBUSES 2 #define MAX_BUSUNITS 2 diff --git a/rom/devs/ata/mmakefile.src b/rom/devs/ata/mmakefile.src index 71713b534c..1c4d71b809 100644 --- a/rom/devs/ata/mmakefile.src +++ b/rom/devs/ata/mmakefile.src @@ -2,12 +2,12 @@ include $(SRCDIR)/config/aros.cfg +#MM- kernel-ata-includes : kernel-scsi-includes +#MM- kernel-ata-kobj-includes : kernel-scsi--kobj-includes + USER_CPPFLAGS := -D__OOP_NOMETHODBASES__ -D__OOP_NOATTRBASES__ #USER_CPPFLAGS += -DDEBUG -INCLUDE_FILES := $(call WILDCARD, include/devices/*.h) -%copy_includes path=devices dir=include/devices - INCLUDE_FILES := $(call WILDCARD, include/hardware/*.h) %copy_includes path=hardware dir=include/hardware diff --git a/rom/devs/scsi/bootwait.c b/rom/devs/scsi/bootwait.c new file mode 100644 index 0000000000..1b62a50013 --- /dev/null +++ b/rom/devs/scsi/bootwait.c @@ -0,0 +1,101 @@ +/* + Copyright © 1995-2013, The AROS Development Team. All rights reserved. + $Id: bootwait.c 55802 2019-03-08 21:47:59Z wawa $ +*/ + +#include +#include + +#include +#include +#include + +#include LC_LIBDEFS_FILE + +#include "scsi.h" + +#if defined(__AROSPLATFORM_SMP__) +#include +#include +#include +#endif + +extern const char scsi_LibName[]; +extern const char scsi_LibID[]; +extern const int scsi_End; + +AROS_UFP3(static APTR, scsi_Wait, + AROS_UFPA(void *, dummy, D0), + AROS_UFPA(BPTR, segList, A0), + AROS_UFPA(struct ExecBase *, SysBase, A6)); + +const struct Resident scsi_BootWait = +{ + RTC_MATCHWORD, + (struct Resident *)&scsi_BootWait, + (void *)&scsi_End, + RTF_COLDSTART, + VERSION_NUMBER, + NT_TASK, + -49, /* dosboot.resource is -50 */ + "SCSI boot wait", + &scsi_LibID[6], + &scsi_Wait, +}; + +/* + * The purpose of this delay is to wait until device detection is done + * before boot sequence enters DOS bootstrap. Without this we reach the + * bootstrap earlier than devices are detected (and BootNodes inserted). + * As a result, we end up in unbootable system. + * Actually, i dislike this solution a bit. I think something else has + * to be implemented. However i do not know what. Even if we rewrite + * adding BootNodes, bootmenu still has to wait until all nodes are added. + * Making device detection synchronous is IMHO not a good option, it will + * increase booting time of our OS. + */ + +AROS_UFH3(static APTR, scsi_Wait, + AROS_UFPA(void *, dummy, D0), + AROS_UFPA(BPTR, segList, A0), + AROS_UFPA(struct ExecBase *, SysBase, A6)) +{ + AROS_USERFUNC_INIT + + struct scsiBase *SCSIBase; +#if defined(__AROSPLATFORM_SMP__) + void *ExecLockBase = OpenResource("execlock.resource"); +#endif + +#if defined(__AROSPLATFORM_SMP__) + if (ExecLockBase) + ObtainSystemLock(&SysBase->DeviceList, SPINLOCK_MODE_READ, LOCKF_FORBID); + else + Forbid(); +#else + Forbid(); +#endif + + /* We do not want to deal with IORequest and units, so just FindName() */ + SCSIBase = (struct scsiBase *)FindName(&SysBase->DeviceList, scsi_LibName); + +#if defined(__AROSPLATFORM_SMP__) + if (ExecLockBase) + ReleaseSystemLock(&SysBase->DeviceList, LOCKF_FORBID); + else + Permit(); +#else + Permit(); +#endif + + if (SCSIBase) + { + D(bug("[SCSI ] Waiting for device detection to complete...\n")); + ObtainSemaphore(&SCSIBase->DetectionSem); + ReleaseSemaphore(&SCSIBase->DetectionSem); + } + + return NULL; + + AROS_USERFUNC_EXIT +} diff --git a/rom/devs/ata/include/devices/scsicmds.h b/rom/devs/scsi/include/devices/scsicmds.h similarity index 100% rename from rom/devs/ata/include/devices/scsicmds.h rename to rom/devs/scsi/include/devices/scsicmds.h diff --git a/rom/devs/scsi/include/hardware/scsi.h b/rom/devs/scsi/include/hardware/scsi.h new file mode 100644 index 0000000000..9197593a3f --- /dev/null +++ b/rom/devs/scsi/include/hardware/scsi.h @@ -0,0 +1,144 @@ +#ifndef _HARDWARE_SCSI_H +#define _HARDWARE_SCSI_H + +/* + Copyright © 2019, The AROS Development Team. All rights reserved. + $Id$ + + Desc: SCSI hardware register definitions + Lang: English +*/ + +/* Registers */ +#define scsi_Error 1 +#define scsi_Feature 1 +#define scsi_Count 2 +#define scsi_LBALow 3 +#define scsi_Sector 3 +#define scsi_LBAMid 4 +#define scsi_CylinderLow 4 +#define scsi_LBAHigh 5 +#define scsi_CylinderHigh 5 +#define scsi_DevHead 6 +#define scsi_Status 7 +#define scsi_Command 7 +#define scsi_AltStatus 0x2 +#define scsi_AltControl 0x2 + +#define scsi_pi_Error 1 +#define scsi_pi_Features 1 +#define scsi_pi_Reason 2 +#define scsi_pi_ByteCntL 4 +#define scsi_pi_ByteCntH 5 +#define scsi_pi_DevSel 6 +#define scsi_pi_Status 7 +#define scsi_pi_Command 7 + +/* Status bits */ +#define SCSIB_SLAVE 4 +#define SCSIB_LBA 6 +#define SCSIB_ATAPI 7 +#define SCSIB_DATAREQ 3 +#define SCSIB_ERROR 0 +#define SCSIB_BUSY 7 + +#define SCSIF_SLAVE 0x10 +#define SCSIF_LBA 0x40 +#define SCSIF_ATAPI 0x80 +#define SCSIF_DATAREQ 0x08 +#define SCSIF_ERROR 0x01 +#define SCSIF_BUSY 0x80 +#define SCSIF_DRDY 0x40 + +#define SCSIPIF_CHECK 0x01 + +/* Commands */ +#define SCSI_RECALIBRATE 0x10 + +#define SCSI_IDENTIFY_DEVICE 0xEC +#define SCSI_CHECK_POWER_MODE 0xE5 +#define SCSI_STANDBY 0xE2 +#define SCSI_STANDBY_IMMED 0xE0 +#define SCSI_STANDBY_IMMEDIATE SCSI_STANDBY_IMMED +#define SCSI_IDLE_IMMED 0xE1 +#define SCSI_IDLE_IMMEDIATE SCSI_IDLE_IMMED +#define SCSI_IDLE 0xE3 +#define SCSI_FLUSH_CACHE 0xE7 +#define SCSI_FLUSH_CACHE_EXT 0xEA +#define SCSI_READ_DMA_EXT 0x25 +#define SCSI_READ_DMA64 SCSI_READ_DMA_EXT +#define SCSI_READ_DMA 0xC8 +#define SCSI_READ_SECTORS_EXT 0x24 +#define SCSI_READ64 SCSI_READ_SECTORS_EXT +#define SCSI_READ_SECTORS 0x20 +#define SCSI_READ SCSI_READ_SECTORS +#define SCSI_WRITE_DMA_EXT 0x35 +#define SCSI_WRITE_DMA64 SCSI_WRITE_DMA_EXT +#define SCSI_WRITE_DMA 0xCA +#define SCSI_WRITE_SECTORS_EXT 0x34 +#define SCSI_WRITE64 SCSI_WRITE_SECTORS_EXT +#define SCSI_WRITE_SECTORS 0x30 +#define SCSI_WRITE SCSI_WRITE_SECTORS +#define SCSI_WRITE_UNCORRECTABLE 0x45 +#define SCSI_READ_VERIFY_SECTORS 0x40 +#define SCSI_READ_VERIFY_SECTORS_EXT 0x42 +#define SCSI_READ_BUFFER 0xE4 +#define SCSI_WRITE_BUFFER 0xE8 +#define SCSI_EXECUTE_DEVICE_DIAG 0x90 +#define SCSI_EXECUTE_DIAG SCSI_EXECUTE_DEVICE_DIAG +#define SCSI_SET_FEATURES 0xEF +#define SCSI_SMART 0xB0 +#define SCSI_PACKET_IDENTIFY 0xA1 +#define SCSI_IDENTIFY_ATAPI SCSI_PACKET_IDENTIFY +#define SCSI_PACKET 0xA0 +#define SCSI_READ_FPDMA 0x60 +#define SCSI_WRITE_FPDMA 0x61 +#define SCSI_READ_LOG_EXT 0x2F +#define SCSI_NOP 0x00 +#define SCSI_DEVICE_RESET 0x08 +#define SCSI_MEDIA_EJECT 0xED +#define SCSI_SECURITY_UNLOCK 0xF2 +#define SCSI_SECURITY_FREEZE_LOCK 0xF5 +#define SCSI_DATA_SET_MANAGEMENT 0x06 +#define SCSI_DOWNLOAD_MICROCODE 0x92 +#define SCSI_WRITE_STREAM_DMA_EXT 0x3A +#define SCSI_READ_LOG_DMA_EXT 0x47 +#define SCSI_READ_STREAM_DMA_EXT 0x2A +#define SCSI_WRITE_DMA_FUA 0x3D +#define SCSI_WRITE_LOG_DMA_EXT 0x57 +#define SCSI_READ_DMA_QUEUED 0xC7 +#define SCSI_READ_DMA_QUEUED_EXT 0x26 +#define SCSI_WRITE_DMA_QUEUED 0xCC +#define SCSI_WRITE_DMA_QUEUED_EXT 0x36 +#define SCSI_WRITE_DMA_QUEUED_FUA_EXT 0x3E +#define SCSI_SET_MULTIPLE 0XC6 +#define SCSI_READ_MULTIPLE 0xC4 +#define SCSI_READ_MULTIPLE_EXT 0x29 +#define SCSI_READ_MULTIPLE64 SCSI_READ_MULTIPLE_EXT +#define SCSI_WRITE_MULTIPLE 0xC5 +#define SCSI_WRITE_MULTIPLE_EXT 0x39 +#define SCSI_WRITE_MULTIPLE64 SCSI_WRITE_MULTIPLE_EXT +#define SCSI_WRITE_MULTIPLE_FUA_EXT 0xCE + +#define SCSI_DEVICE_CONFIG_IDENTIFY 0xB1 +#define SCSI_DEVICE_CONFIG_ID_FEATURES 0xC2 + + +/* SET_FEATURES sub-commands */ +#define SCSI_SET_FEATURES_ENABLE_CACHE 0x02 +#define SCSI_SET_FEATURES_DISABLE_CACHE 0x82 +#define SCSI_SET_FEATURES_DISABLE_READ_AHEAD 0x55 +#define SCSI_SET_FEATURES_ENABLE_READ_AHEAD 0xAA +#define SCSI_SET_FEATURES_SET_TRANSFER_MODE 0x03 + +/* ATAPI reason flags */ +#define ATAPIF_MASK 0x03 +#define ATAPIF_COMMAND 0x01 +#define ATAPIF_READ 0x02 +#define ATAPIF_WRITE 0x00 + +/* AltControl bits */ +#define SCSICTLF_INT_DISABLE 0x02 +#define SCSICTLF_RESET 0x04 + +#endif diff --git a/rom/devs/scsi/include/hidd/scsi.h b/rom/devs/scsi/include/hidd/scsi.h new file mode 100644 index 0000000000..a359efeace --- /dev/null +++ b/rom/devs/scsi/include/hidd/scsi.h @@ -0,0 +1,79 @@ +#ifndef HIDD_SCSI_H +#define HIDD_SCSI_H + +/* + Copyright © 2013-2018, The AROS Development Team. All rights reserved. + $Id$ + + Desc: SCSI bus driver HIDD definitions + Lang: english +*/ + +#define CLID_Hidd_SCSI "hidd.scsi" +#define CLID_Hidd_SCSIBus "hidd.scsi.bus" + +struct SCSI_BusInterface +{ + VOID (*scsi_out )(void *obj, UBYTE val, UWORD offset); + UBYTE (*scsi_in )(void *obj, UWORD offset); + VOID (*scsi_out_alt)(void *obj, UBYTE val, UWORD offset); + UBYTE (*scsi_in_alt )(void *obj, UWORD offset); +}; + +struct SCSI_PIOInterface +{ + VOID (*scsi_outsw )(void *obj, APTR address, ULONG count); + VOID (*scsi_insw )(void *obj, APTR address, ULONG count); + VOID (*scsi_outsl )(void *obj, APTR address, ULONG count); + VOID (*scsi_insl )(void *obj, APTR address, ULONG count); +}; + +struct SCSI_DMAInterface +{ + BOOL (*dma_Prepare)(void *obj, APTR buffer, IPTR size, BOOL read); + VOID (*dma_Start )(void *obj); + VOID (*dma_End )(void *obj, APTR buffer, IPTR size, BOOL read); + ULONG (*dma_Result )(void *obj); +}; + +typedef enum +{ + AB_XFER_PIO0 = 0, + AB_XFER_PIO1, + AB_XFER_PIO2, + AB_XFER_PIO3, + AB_XFER_PIO4, + + AB_XFER_MDMA0, + AB_XFER_MDMA1, + AB_XFER_MDMA2, + + AB_XFER_UDMA0, + AB_XFER_UDMA1, + AB_XFER_UDMA2, + AB_XFER_UDMA3, + AB_XFER_UDMA4, + AB_XFER_UDMA5, + AB_XFER_UDMA6, + + AB_XFER_48BIT = 27, /* LBA48 */ + AB_XFER_RWMULTI, /* Multisector */ + AB_XFER_PACKET, /* ATAPI */ + AB_XFER_LBA, /* LBA28 */ + AB_XFER_PIO32 /* 32-bit PIO */ +} scsi_XferMode; + +#define AF_XFER_PIO(x) (1<<(AB_XFER_PIO0+(x))) +#define AF_XFER_MDMA(x) (1<<(AB_XFER_MDMA0+(x))) +#define AF_XFER_UDMA(x) (1<<(AB_XFER_UDMA0+(x))) +#define AF_XFER_48BIT (1<<(AB_XFER_48BIT)) +#define AF_XFER_RWMULTI (1<<(AB_XFER_RWMULTI)) +#define AF_XFER_PACKET (1<<(AB_XFER_PACKET)) +#define AF_XFER_LBA (1<<(AB_XFER_LBA)) +#define AF_XFER_PIO32 (1<<(AB_XFER_PIO32)) + +//#include +#include +#include + +#endif diff --git a/rom/devs/scsi/lowlevel.c b/rom/devs/scsi/lowlevel.c new file mode 100644 index 0000000000..15bce219b4 --- /dev/null +++ b/rom/devs/scsi/lowlevel.c @@ -0,0 +1,185 @@ +/* + Copyright © 2004-2018, The AROS Development Team. All rights reserved. + $Id: lowlevel.c 55802 2019-03-08 21:47:59Z wawa $ + + Desc: + Lang: English +*/ + +/* + * TODO: + * - put a critical section around DMA transfers (shared dma channels) + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include "scsi.h" +#include "scsi_bus.h" +#include "timer.h" + +// use #define xxx(a) D(a) to enable particular sections. +#if DEBUG +#define DIRQ(a) D(a) +#define DIRQ_MORE(a) +#define DUMP(a) D(a) +#define DUMP_MORE(a) +#define DSCSI(a) D(a) +#define DATAPI(a) D(a) +#define DINIT(a) D(a) +#else +#define DIRQ(a) do { } while (0) +#define DIRQ_MORE(a) do { } while (0) +#define DUMP(a) do { } while (0) +#define DUMP_MORE(a) do { } while (0) +#define DSCSI(a) do { } while (0) +#define DATAPI(a) do { } while (0) +#define DINIT(a) +#endif +/* Errors that shouldn't happen */ +#define DERROR(a) a + +/* + * Initial device configuration that suits *all* cases + */ +void scsi_init_unit(struct scsi_Bus *bus, struct scsi_Unit *unit, UBYTE u) +{ + struct scsiBase *SCSIBase = bus->sb_Base; + OOP_Object *obj = OOP_OBJECT(SCSIBase->busClass, bus); + + unit->su_Bus = bus; + unit->pioInterface = bus->pioInterface; + unit->su_UnitNum = bus->sb_BusNum << 1 | u; // b << 8 | u + unit->su_DevMask = 0xa0 | (u << 4); + + DINIT(bug("[SCSI%02u] scsi_init_unit: bus %u unit %d\n", unit->su_UnitNum, bus->sb_BusNum, u)); + +#if (0) + /* Set PIO transfer functions, either 16 or 32 bits */ + if (SCSIBase->scsi_32bit && OOP_GET(obj, aHidd_SCSIBus_Use32Bit)) + Unit_Enable32Bit(unit); + else + Unit_Disable32Bit(unit); +#endif +} + +BOOL scsi_setup_unit(struct scsi_Bus *bus, struct scsi_Unit *unit) +{ + /* + * this stuff always goes along the same way + * WARNING: NO INTERRUPTS AT THIS POINT! + */ + UBYTE u; + + DINIT(bug("[SCSI ] scsi_setup_unit(%d)\n", unit->su_UnitNum)); +#if (0) + scsi_SelectUnit(unit); + + if (FALSE == scsi_WaitBusyTO(unit, 1, FALSE, FALSE, NULL)) + { + DINIT(bug("[SCSI%02ld] scsi_setup_unit: ERROR: Drive not ready for use. Keeping functions stubbed\n", unit->su_UnitNum)); + return FALSE; + } + + u = unit->su_UnitNum & 1; + switch (bus->sb_Dev[u]) + { + /* + * safe fallback settings + */ + case DEV_SATAPI: + case DEV_ATAPI: + case DEV_SATA: + case DEV_ATA: + unit->su_Identify = scsi_Identify; + break; + + default: + DINIT(bug("[SCSI%02ld] scsi_setup_unit: Unsupported device %lx. All functions will remain stubbed.\n", unit->su_UnitNum, bus->sb_Dev[u])); + return FALSE; + } + + DINIT(bug("[SCSI ] scsi_setup_unit: Enabling IRQs\n")); + PIO_OutAlt(bus, 0x0, scsi_AltControl); + + /* + * now make unit self diagnose + */ + if (unit->su_Identify(unit) != 0) + { + return FALSE; + } +#endif + + return TRUE; +} + +void scsi_InitBus(struct scsi_Bus *bus) +{ + struct scsiBase *SCSIBase = bus->sb_Base; + OOP_Object *obj = OOP_OBJECT(SCSIBase->busClass, bus); + IPTR haveAltIO; + UBYTE tmp1, tmp2; + UWORD i; + + /* + * initialize timer for the sake of scanning + */ + bus->sb_Timer = scsi_OpenTimer(bus->sb_Base); + +#if (0) + OOP_GetAttr(obj, aHidd_SCSIBus_UseIOAlt, &haveAltIO); + bus->haveAltIO = haveAltIO != 0; +#endif + + DINIT(bug("[SCSI ] scsi_InitBus(%p)\n", bus)); + + bus->sb_Dev[0] = DEV_NONE; + bus->sb_Dev[1] = DEV_NONE; + + /* Check if device 0 and/or 1 is present on this bus. It may happen that + a single drive answers for both device addresses, but the phantom + drive will be filtered out later */ + for (i = 0; i < MAX_BUSUNITS; i++) + { + /* Select device and disable IRQs */ +#if (0) + PIO_Out(bus, DEVHEAD_VAL | (i << 4), scsi_DevHead); +#endif + scsi_WaitTO(bus->sb_Timer, 0, 400, 0); + PIO_OutAlt(bus, SCSICTLF_INT_DISABLE, scsi_AltControl); + + /* Write some pattern to registers. This is a variant of a more + common technique, with the difference that we don't use the + sector count register because some bad ATAPI drives disallow + writing to it */ + PIO_Out(bus, 0x55, scsi_LBALow); + PIO_Out(bus, 0xaa, scsi_LBAMid); + PIO_Out(bus, 0xaa, scsi_LBALow); + PIO_Out(bus, 0x55, scsi_LBAMid); + PIO_Out(bus, 0x55, scsi_LBALow); + PIO_Out(bus, 0xaa, scsi_LBAMid); + + tmp1 = PIO_In(bus, scsi_LBALow); + tmp2 = PIO_In(bus, scsi_LBAMid); + DB2(bug("[SCSI ] scsi_InitBus: Reply 0x%02X 0x%02X\n", tmp1, tmp2)); + + if ((tmp1 == 0x55) && (tmp2 == 0xaa)) + bus->sb_Dev[i] = DEV_UNKNOWN; + DINIT(bug("[SCSI ] scsi_InitBus: Device type = 0x%02X\n", bus->sb_Dev[i])); + } +#if (0) + scsi_ResetBus(bus); +#endif + scsi_CloseTimer(bus->sb_Timer); + DINIT(bug("[SCSI ] scsi_InitBus: Finished\n")); +} diff --git a/rom/devs/scsi/mmakefile.src b/rom/devs/scsi/mmakefile.src new file mode 100644 index 0000000000..15893a62d9 --- /dev/null +++ b/rom/devs/scsi/mmakefile.src @@ -0,0 +1,35 @@ +# $Id: mmakefile.src 55802 2019-03-08 21:47:59Z wawa $ + +include $(SRCDIR)/config/aros.cfg + +USER_CPPFLAGS := -D__OOP_NOMETHODBASES__ -D__OOP_NOATTRBASES__ +USER_CPPFLAGS += -DDEBUG=1 + +INCLUDE_FILES := $(call WILDCARD, include/devices/*.h) +%copy_includes path=devices dir=include/devices + +INCLUDE_FILES := $(call WILDCARD, include/hardware/*.h) +%copy_includes path=hardware dir=include/hardware + +INCLUDE_FILES := $(call WILDCARD, include/hidd/*.h) +%copy_includes path=hidd dir=include/hidd + +USER_LDFLAGS := -static + +SCSI_DEVICEFILES := \ + scsi_init \ + scsi \ + lowlevel \ + timer \ + bootwait \ + waitnano \ + waitto + +SCSI_CLASSFILES := \ + scsi_controllerclass \ + scsi_busclass \ + scsi_unitclass + +%build_module mmake=kernel-scsi \ + modname=scsi modtype=device version=$(AROS_TARGET_PLATFORM) \ + files="$(SCSI_DEVICEFILES) $(SCSI_CLASSFILES)" diff --git a/rom/devs/scsi/scsi.c b/rom/devs/scsi/scsi.c new file mode 100644 index 0000000000..b952a59994 --- /dev/null +++ b/rom/devs/scsi/scsi.c @@ -0,0 +1,1084 @@ +/* + Copyright © 2019, The AROS Development Team. All rights reserved + $Id$ + + Desc: + Lang: English +*/ + +#include + +#include + +/* We want all other bases obtained from our base */ +#define __NOLIBBASE__ + +#include + +#include +#include +#include +#include +#include +#include +#include + +//#include + +#include "timer.h" +#include "scsi.h" + +#include LC_LIBDEFS_FILE + +#define DINIT(x) + +//---------------------------IO Commands--------------------------------------- + +/* Invalid comand does nothing, complains only. */ +static void cmd_Invalid(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + D(bug("[SCSI%02ld] %s(%d)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, io->io_Command)); + io->io_Error = IOERR_NOCMD; +} + +/* Don't need to reset the drive? */ +static void cmd_Reset(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + IOStdReq(io)->io_Actual = 0; +} + +/* CMD_READ implementation */ +static void cmd_Read32(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)IOStdReq(io)->io_Unit; + + if (AF_Removable == (unit->su_Flags & (AF_Removable | AF_DiscPresent))) + { + D(bug("[SCSI%02ld] %s: USUALLY YOU'D WANT TO CHECK IF DISC IS PRESENT FIRST\n", unit->su_UnitNum, __func__)); + io->io_Error = TDERR_DiskChanged; + return; + } + + ULONG block = IOStdReq(io)->io_Offset; + ULONG count = IOStdReq(io)->io_Length; + + D(bug("[SCSI%02ld] %s(%08x, %08x)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, block, count)); + + ULONG mask = (1 << unit->su_SectorShift) - 1; + + /* + During this IO call it should be sure that both offset and + length are already aligned properly to sector boundaries. + */ + if ((block & mask) | (count & mask)) + { + D(bug("[SCSI%02ld] %s: offset or length not sector-aligned.\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + cmd_Invalid(io, LIBBASE); + } + else + { + block >>= unit->su_SectorShift; + count >>= unit->su_SectorShift; + ULONG cnt = 0; + + if ((0 == (unit->su_XferModes & AF_XFER_PACKET)) && ((block + count) > unit->su_Capacity)) + { + bug("[SCSI%02ld] %s: Requested block (%lx;%ld) outside disk range (%lx)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, block, count, unit->su_Capacity); + io->io_Error = IOERR_BADADDRESS; + return; + } + + /* Call the Unit's access funtion */ + io->io_Error = unit->su_Read32(unit, block, count, + IOStdReq(io)->io_Data, &cnt); + + IOStdReq(io)->io_Actual = cnt; + } +} + +/* + NSCMD_TD_READ64, TD_READ64 implementation. Basically the same, just packs + the 64 bit offset in both io_Offset (31:0) and io_Actual (63:32) +*/ +static void cmd_Read64(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)IOStdReq(io)->io_Unit; + + if (AF_Removable == (unit->su_Flags & (AF_Removable | AF_DiscPresent))) + { + D(bug("[SCSI%02ld] %s: USUALLY YOU'D WANT TO CHECK IF DISC IS PRESENT FIRST\n", unit->su_UnitNum, __func__)); + io->io_Error = TDERR_DiskChanged; + return; + } + + UQUAD block = IOStdReq(io)->io_Offset | (UQUAD)(IOStdReq(io)->io_Actual) << 32; + ULONG count = IOStdReq(io)->io_Length; + + D(bug("[SCSI%02ld] %s(%08x-%08x, %08x)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, IOStdReq(io)->io_Actual, IOStdReq(io)->io_Offset, count)); + + ULONG mask = (1 << unit->su_SectorShift) - 1; + + if ((block & (UQUAD)mask) | (count & mask) | (count == 0)) + { + D(bug("[SCSI%02ld] %s: offset or length not sector-aligned.\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + cmd_Invalid(io, LIBBASE); + } + else + { + block >>= unit->su_SectorShift; + count >>= unit->su_SectorShift; + ULONG cnt = 0; + + /* + If the sum of sector offset and the sector count doesn't overflow + the 28-bit LBA address, use 32-bit access for speed and simplicity. + Otherwise do the 48-bit LBA addressing. + */ + if ((block + count) < 0x0fffffff) + { + if ((0 == (unit->su_XferModes & AF_XFER_PACKET)) && ((block + count) > unit->su_Capacity)) + { + bug("[SCSI%02ld] %s: Requested block (%lx;%ld) outside disk range (%lx)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, block, count, unit->su_Capacity); + io->io_Error = IOERR_BADADDRESS; + return; + } + io->io_Error = unit->su_Read32(unit, (ULONG)(block & 0x0fffffff), count, IOStdReq(io)->io_Data, &cnt); + } + else + { + if ((0 == (unit->su_XferModes & AF_XFER_PACKET)) && ((block + count) > unit->su_Capacity48)) + { + bug("[SCSI%02ld] %s: Requested block (%lx:%08lx;%ld) outside disk range (%lx:%08lx)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, block>>32, block&0xfffffffful, count, unit->su_Capacity48>>32, unit->su_Capacity48 & 0xfffffffful); + io->io_Error = IOERR_BADADDRESS; + return; + } + + io->io_Error = unit->su_Read64(unit, block, count, IOStdReq(io)->io_Data, &cnt); + } + + IOStdReq(io)->io_Actual = cnt; + } +} + +/* CMD_WRITE implementation */ +static void cmd_Write32(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)IOStdReq(io)->io_Unit; + + if (AF_Removable == (unit->su_Flags & (AF_Removable | AF_DiscPresent))) + { + D(bug("[SCSI%02ld] %s: USUALLY YOU'D WANT TO CHECK IF DISC IS PRESENT FIRST\n", unit->su_UnitNum, __func__)); + io->io_Error = TDERR_DiskChanged; + return; + } + + ULONG block = IOStdReq(io)->io_Offset; + ULONG count = IOStdReq(io)->io_Length; + + D(bug("[SCSI%02ld] %s(%08x, %08x)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, block, count)); + + ULONG mask = (1 << unit->su_SectorShift) - 1; + + /* + During this IO call it should be sure that both offset and + length are already aligned properly to sector boundaries. + */ + if ((block & mask) | (count & mask)) + { + D(bug("[SCSI%02ld] %s: offset or length not sector-aligned.\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + cmd_Invalid(io, LIBBASE); + } + else + { + block >>= unit->su_SectorShift; + count >>= unit->su_SectorShift; + ULONG cnt = 0; + + if ((0 == (unit->su_XferModes & AF_XFER_PACKET)) + && ((block + count) > unit->su_Capacity)) + { + bug("[SCSI%02ld] %s: Requested block (%lx;%ld) outside disk range (%lx)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, + __func__, + block, count, unit->su_Capacity); + io->io_Error = IOERR_BADADDRESS; + return; + } + + /* Call the Unit's access funtion */ + io->io_Error = unit->su_Write32(unit, block, count, + IOStdReq(io)->io_Data, &cnt); + + IOStdReq(io)->io_Actual = cnt; + } +} + +/* + NSCMD_TD_WRITE64, TD_WRITE64 implementation. Basically the same, just packs + the 64 bit offset in both io_Offset (31:0) and io_Actual (63:32) +*/ +static void cmd_Write64(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)IOStdReq(io)->io_Unit; + + if (AF_Removable == (unit->su_Flags & (AF_Removable | AF_DiscPresent))) + { + D(bug("[SCSI%02ld] %s: USUALLY YOU'D WANT TO CHECK IF DISC IS PRESENT FIRST\n", unit->su_UnitNum, __func__)); + io->io_Error = TDERR_DiskChanged; + return; + } + + UQUAD block = IOStdReq(io)->io_Offset | (UQUAD)(IOStdReq(io)->io_Actual) << 32; + ULONG count = IOStdReq(io)->io_Length; + + D(bug("[SCSI%02ld] %s(%08x-%08x, %08x)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, IOStdReq(io)->io_Actual, IOStdReq(io)->io_Offset, count)); + + ULONG mask = (1 << unit->su_SectorShift) - 1; + + if ((block & mask) | (count & mask) | (count==0)) + { + D(bug("[SCSI%02ld] %s: offset or length not sector-aligned.\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + cmd_Invalid(io, LIBBASE); + } + else + { + block >>= unit->su_SectorShift; + count >>= unit->su_SectorShift; + ULONG cnt = 0; + + /* + If the sum of sector offset and the sector count doesn't overflow + the 28-bit LBA address, use 32-bit access for speed and simplicity. + Otherwise do the 48-bit LBA addressing. + */ + if ((block + count) < 0x0fffffff) + { + if ((0 == (unit->su_XferModes & AF_XFER_PACKET)) + && ((block + count) > unit->su_Capacity)) + { + bug("[SCSI%02ld] %s: Requested block (%lx;%ld) outside disk range " + "(%lx)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, block, count, unit->su_Capacity); + io->io_Error = IOERR_BADADDRESS; + return; + } + io->io_Error = unit->su_Write32(unit, (ULONG)(block & 0x0fffffff), + count, IOStdReq(io)->io_Data, &cnt); + } + else + { + if ((0 == (unit->su_XferModes & AF_XFER_PACKET)) + && ((block + count) > unit->su_Capacity48)) + { + bug("[SCSI%02ld] %s: Requested block (%lx:%08lx;%ld) outside disk " + "range (%lx:%08lx)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, + __func__, + block>>32, block&0xfffffffful, + count, unit->su_Capacity48>>32, + unit->su_Capacity48 & 0xfffffffful); + io->io_Error = IOERR_BADADDRESS; + return; + } + + io->io_Error = unit->su_Write64(unit, block, count, + IOStdReq(io)->io_Data, &cnt); + } + IOStdReq(io)->io_Actual = cnt; + } +} + + +/* use CMD_FLUSH to force all IO waiting commands to abort */ +static void cmd_Flush(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct IORequest *msg; + struct scsi_Bus *bus = ((struct scsi_Unit *)io->io_Unit)->su_Bus; + + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + Forbid(); + + while((msg = (struct IORequest *)GetMsg((struct MsgPort *)bus->sb_MsgPort))) + { + msg->io_Error = IOERR_ABORTED; + ReplyMsg((struct Message *)msg); + } + + Permit(); +} + +/* + Internal command used to check whether the media in drive has been changed + since last call. If so, the handlers given by user are called. +*/ +static void cmd_TestChanged(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; + struct IORequest *msg; + + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + if ((unit->su_XferModes & AF_XFER_PACKET) && (unit->su_Flags & AF_Removable)) + { +#if (0) + atapi_TestUnitOK(unit); +#endif + if (unit->su_Flags & AF_DiscChanged) + { + unit->su_ChangeNum++; + + Forbid(); + + /* old-fashioned RemoveInt call first */ + if (unit->su_RemoveInt) + Cause(unit->su_RemoveInt); + + /* And now the whole list of possible calls */ + ForeachNode(&unit->su_SoftList, msg) + { + Cause((struct Interrupt *)IOStdReq(msg)->io_Data); + } + + unit->su_Flags &= ~AF_DiscChanged; + + Permit(); + } + } +} + +static void cmd_Update(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + /* Do nothing now. In near future there should be drive cache flush though */ + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); +} + +static void cmd_Remove(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; + + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + if (unit->su_RemoveInt) + io->io_Error = TDERR_DriveInUse; + else + unit->su_RemoveInt = IOStdReq(io)->io_Data; +} + +static void cmd_ChangeNum(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + IOStdReq(io)->io_Actual = ((struct scsi_Unit *)io->io_Unit)->su_ChangeNum; +} + +static void cmd_ChangeState(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; + + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + if (unit->su_Flags & AF_DiscPresent) + IOStdReq(io)->io_Actual = 0; + else + IOStdReq(io)->io_Actual = 1; + + D(bug("[SCSI%02ld] %s: Media %s\n", unit->su_UnitNum, __func__, IOStdReq(io)->io_Actual ? "ABSENT" : "PRESENT")); +} + +static void cmd_ProtStatus(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; + + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + if (unit->su_DevType) + IOStdReq(io)->io_Actual = -1; + else + IOStdReq(io)->io_Actual = 0; + +} + +static void cmd_GetNumTracks(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + IOStdReq(io)->io_Actual = ((struct scsi_Unit *)io->io_Unit)->su_Cylinders; +} + +static void cmd_AddChangeInt(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; + + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + Forbid(); + AddHead(&unit->su_SoftList, (struct Node *)io); + Permit(); + + io->io_Flags &= ~IOF_QUICK; + unit->su_Unit.unit_flags &= ~UNITF_ACTIVE; +} + +static void cmd_RemChangeInt(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + Forbid(); + Remove((struct Node *)io); + Permit(); +} + +static void cmd_Eject(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; + + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + IOStdReq(io)->io_Error = unit->su_Eject(unit); + cmd_TestChanged(io, LIBBASE); +} + +static void cmd_GetGeometry(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; + + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + if (IOStdReq(io)->io_Length == sizeof(struct DriveGeometry)) + { + struct DriveGeometry *dg = (struct DriveGeometry *)IOStdReq(io)->io_Data; + + dg->dg_SectorSize = 1 << unit->su_SectorShift; + + if (unit->su_Capacity48 != 0) + { + if ((unit->su_Capacity48 >> 32) != 0) + dg->dg_TotalSectors = 0xffffffff; + else + dg->dg_TotalSectors = unit->su_Capacity48; + } + else + dg->dg_TotalSectors = unit->su_Capacity; + + dg->dg_Cylinders = unit->su_Cylinders; + dg->dg_CylSectors = unit->su_Sectors * unit->su_Heads; + dg->dg_Heads = unit->su_Heads; + dg->dg_TrackSectors = unit->su_Sectors; + dg->dg_BufMemType = MEMF_PUBLIC; + dg->dg_DeviceType = unit->su_DevType; + if (dg->dg_DeviceType != DG_DIRECT_ACCESS) + dg->dg_Flags = (unit->su_Flags & AF_Removable) ? DGF_REMOVABLE : 0; + else + dg->dg_Flags = 0; + dg->dg_Reserved = 0; + + IOStdReq(io)->io_Actual = sizeof(struct DriveGeometry); + } + else if (IOStdReq(io)->io_Length == 514) + { + CopyMemQuick(unit->su_Drive, IOStdReq(io)->io_Data, 512); + } + else io->io_Error = TDERR_NotSpecified; +} + +static void cmd_DirectScsi(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; + + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + IOStdReq(io)->io_Actual = sizeof(struct SCSICmd); +#if (0) + if (unit->su_XferModes & AF_XFER_PACKET) + { + io->io_Error = unit->su_DirectSCSI(unit, (struct SCSICmd *)IOStdReq(io)->io_Data); + } + else if (unit->su_DevType == DG_DIRECT_ACCESS) + { + io->io_Error = SCSIEmu(unit, (struct SCSICmd *)IOStdReq(io)->io_Data); + } + else io->io_Error = IOERR_BADADDRESS; +#else + io->io_Error = IOERR_BADADDRESS; +#endif +} + +static BOOL ValidSMARTCmd(struct IORequest *io) +{ +#if (0) + if ((IOStdReq(io)->io_Reserved1) != (IOStdReq(io)->io_Reserved2)) + return FALSE; + + switch (IOStdReq(io)->io_Reserved1) + { + case SMARTC_TEST_AVAIL: + case SMARTC_READ_VALUES: + case SMARTC_READ_THRESHOLDS: + if (!IOStdReq(io)->io_Data) + { + D(bug("[SCSI%02ld] %s: invalid io_Data (%p)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, IOStdReq(io)->io_Data)); + io->io_Error = IOERR_BADADDRESS; + return FALSE; + } + if ((IOStdReq(io)->io_Offset != SMARTC_TEST_AVAIL) && (IOStdReq(io)->io_Length != SMART_DSCSI_LENGTH)) + { + D(bug("[SCSI%02ld] %s: invalid io_Length (%d)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, IOStdReq(io)->io_Length)); + io->io_Error = IOERR_BADLENGTH; + return FALSE; + } + break; + + case SMARTC_ENABLE: + case SMARTC_DISABLE: + case SMARTC_STATUS: + break; + + default: + D(bug("[SCSI%02ld] %s: invalid SMART command (%d)\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, IOStdReq(io)->io_Offset)); + io->io_Error = IOERR_NOCMD; + return FALSE; + } +#endif + return TRUE; +} + +static void cmd_SMART(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; +#if (0) + struct scsi_Bus *bus = unit->su_Bus; + UBYTE u; +#endif + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + if (unit->su_Flags & AF_DiscPresent) + { + io->io_Error = IOERR_OPENFAIL; + return; + } + + if (!ValidSMARTCmd(io)) + return; +#if (0) + u = unit->su_UnitNum & 1; + + if (bus->sb_Dev[u] == DEV_ATA || bus->sb_Dev[u] == DEV_SATA) + { + if (IOStdReq(io)->io_Reserved1 == ATAFEATURE_TEST_AVAIL) + { + if (IOStdReq(io)->io_Length >= sizeof(ULONG)) + { + *((ULONG *)IOStdReq(io)->io_Data) = SMART_MAGIC_ID; + IOStdReq(io)->io_Actual = sizeof(ULONG); + } + io->io_Error = 0; + return; + } + scsi_SMARTCmd(IOStdReq(io)); + } + else +#endif + io->io_Error = IOERR_NOCMD; +} + +static void cmd_TRIM(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; + + D(bug("[SCSI%02ld] %s()\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + if ((unit->su_Drive->id_SCSIVersion >= 7) && (unit->su_Drive->id_DSManagement & 1)) + { + D(bug("[SCSI%02ld] %s: Unit supports TRIM\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); +#if (0) + if (IOStdReq(io)->io_Reserved1 == ATAFEATURE_TEST_AVAIL) + { + if (IOStdReq(io)->io_Length >= sizeof(ULONG)) + { + *((ULONG *)IOStdReq(io)->io_Data) = TRIM_MAGIC_ID; + IOStdReq(io)->io_Actual = sizeof(ULONG); + } + io->io_Error = 0; + return; + } + scsi_TRIMCmd(IOStdReq(io)); +#endif + } + else + io->io_Error = IOERR_NOCMD; +} + +//----------------------------------------------------------------------------- + +/* + command translation tables - used to call proper IO functions. +*/ + +#define N_TD_READ64 0 +#define N_TD_WRITE64 1 +#define N_TD_SEEK64 2 +#define N_TD_FORMAT64 3 + +typedef void (*mapfunc)(struct IORequest *, LIBBASETYPEPTR); + +static mapfunc const map64[]= { + [N_TD_READ64] = cmd_Read64, + [N_TD_WRITE64] = cmd_Write64, + [N_TD_SEEK64] = cmd_Reset, + [N_TD_FORMAT64] = cmd_Write64 +}; + +static mapfunc const map32[] = { + [CMD_INVALID] = cmd_Invalid, + [CMD_RESET] = cmd_Reset, + [CMD_READ] = cmd_Read32, + [CMD_WRITE] = cmd_Write32, + [CMD_UPDATE] = cmd_Update, + [CMD_CLEAR] = cmd_Reset, + [CMD_STOP] = cmd_Reset, + [CMD_START] = cmd_Reset, + [CMD_FLUSH] = cmd_Flush, + [TD_MOTOR] = cmd_Reset, + [TD_SEEK] = cmd_Reset, + [TD_FORMAT] = cmd_Write32, + [TD_REMOVE] = cmd_Remove, + [TD_CHANGENUM] = cmd_ChangeNum, + [TD_CHANGESTATE]= cmd_ChangeState, + [TD_PROTSTATUS] = cmd_ProtStatus, + [TD_RAWREAD] = cmd_Invalid, + [TD_RAWWRITE] = cmd_Invalid, + [TD_GETNUMTRACKS] = cmd_GetNumTracks, + [TD_ADDCHANGEINT] = cmd_AddChangeInt, + [TD_REMCHANGEINT] = cmd_RemChangeInt, + [TD_GETGEOMETRY]= cmd_GetGeometry, + [TD_EJECT] = cmd_Eject, + [TD_READ64] = cmd_Read64, + [TD_WRITE64] = cmd_Write64, + [TD_SEEK64] = cmd_Reset, + [TD_FORMAT64] = cmd_Write64, + [HD_SCSICMD] = cmd_DirectScsi, + [HD_SCSICMD+1] = cmd_TestChanged +}; + +static UWORD const NSDSupported[] = { + CMD_RESET, + CMD_READ, + CMD_WRITE, + CMD_UPDATE, + CMD_CLEAR, + CMD_STOP, + CMD_START, + CMD_FLUSH, + TD_MOTOR, + TD_SEEK, + TD_FORMAT, + TD_REMOVE, + TD_CHANGENUM, + TD_CHANGESTATE, + TD_PROTSTATUS, + TD_GETNUMTRACKS, + TD_ADDCHANGEINT, + TD_REMCHANGEINT, + TD_GETGEOMETRY, + TD_EJECT, + TD_READ64, + TD_WRITE64, + TD_SEEK64, + TD_FORMAT64, + HD_SCSICMD, + TD_GETDRIVETYPE, + NSCMD_DEVICEQUERY, + NSCMD_TD_READ64, + NSCMD_TD_WRITE64, + NSCMD_TD_SEEK64, + NSCMD_TD_FORMAT64, + 0 +}; + +/* + Do proper IO actions depending on the request. It's called from the bus + tasks and from BeginIO in case of immediate commands. +*/ +static void HandleIO(struct IORequest *io, LIBBASETYPEPTR LIBBASE) +{ + io->io_Error = 0; + + /* Handle few commands directly here */ + switch (io->io_Command) + { + /* + New Style Devices query. Introduce self as trackdisk and provide list of + commands supported + */ + case NSCMD_DEVICEQUERY: + { + struct NSDeviceQueryResult *nsdq = (struct NSDeviceQueryResult *)IOStdReq(io)->io_Data; + nsdq->DevQueryFormat = 0; + nsdq->SizeAvailable = sizeof(struct NSDeviceQueryResult); + nsdq->DeviceType = NSDEVTYPE_TRACKDISK; + nsdq->DeviceSubType = 0; + nsdq->SupportedCommands = (UWORD *)NSDSupported; + } + IOStdReq(io)->io_Actual = sizeof(struct NSDeviceQueryResult); + break; + + /* + New Style Devices report here the 'NSTY' - only if such value is + returned here, the NSCMD_DEVICEQUERY might be called. Otherwice it should + report error. + */ + case TD_GETDRIVETYPE: + IOStdReq(io)->io_Actual = DRIVE_NEWSTYLE; + break; + + /* + Call all other commands using the command pointer tables for 32- and + 64-bit accesses. If requested function is defined call it, otherwise + make the function cmd_Invalid. + */ + default: + if (io->io_Command <= (HD_SCSICMD+1)) + { + if (map32[io->io_Command]) + map32[io->io_Command](io, LIBBASE); + else + cmd_Invalid(io, LIBBASE); + } + else if (io->io_Command >= NSCMD_TD_READ64 && io->io_Command <= NSCMD_TD_FORMAT64) + { + if (map64[io->io_Command - NSCMD_TD_READ64]) + map64[io->io_Command - NSCMD_TD_READ64](io, LIBBASE); + else + cmd_Invalid(io, LIBBASE); + } + else cmd_Invalid(io, LIBBASE); + break; + } +} + + +static const ULONG IMMEDIATE_COMMANDS = 0x803ff1e3; // 10000000001111111111000111100011 + +/* See whether the command can be done quick */ +static BOOL isSlow(struct IORequest *io) +{ + BOOL slow = TRUE; /* Assume always slow command */ + + /* For commands with numbers <= 31 check the mask */ + if (io->io_Command <= 31) + { + if (IMMEDIATE_COMMANDS & (1 << io->io_Command)) + slow = FALSE; + } +#if(0) + else if ((io->io_Command >= HD_SMARTCMD && io->io_Command <= HD_TRIMCMD) && + (IOStdReq(io)->io_Reserved1 == ATAFEATURE_TEST_AVAIL)) slow = FALSE; +#endif + else if (io->io_Command == NSCMD_TD_SEEK64 || io->io_Command == NSCMD_DEVICEQUERY) slow = FALSE; + + return slow; +} + +/* + Try to do IO commands. All commands which require talking with scsi devices + will be handled slow, that is they will be passed to bus task which will + execute them as soon as hardware will be free. +*/ +AROS_LH1(void, BeginIO, + AROS_LHA(struct IORequest *, io, A1), + LIBBASETYPEPTR, LIBBASE, 5, scsi) +{ + AROS_LIBFUNC_INIT + + struct scsi_Unit *unit = (struct scsi_Unit *)io->io_Unit; + + io->io_Message.mn_Node.ln_Type = NT_MESSAGE; + + /* Disable interrupts for a while to modify message flags */ + Disable(); + + D(bug("[SCSI%02ld] %s: Executing IO Command %lx\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__, io->io_Command)); + + /* + If the command is not-immediate, or presence of disc is still unknown, + let the bus task do the job. + */ + if (isSlow(io)) + { + unit->su_Unit.unit_flags |= UNITF_ACTIVE | UNITF_INTASK; + io->io_Flags &= ~IOF_QUICK; + Enable(); + + /* Put the message to the bus */ + PutMsg(unit->su_Bus->sb_MsgPort, (struct Message *)io); + } + else + { + D(bug("[SCSI%02ld] %s: ->Fast command\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + + /* Immediate command. Mark unit as active and do the command directly */ + unit->su_Unit.unit_flags |= UNITF_ACTIVE; + Enable(); + HandleIO(io, LIBBASE); + + unit->su_Unit.unit_flags &= ~UNITF_ACTIVE; + + /* + If the command was not intended to be immediate and it was not the + TD_ADDCHANGEINT, reply to confirm command execution now. + */ + if (!(io->io_Flags & IOF_QUICK) && (io->io_Command != TD_ADDCHANGEINT)) + { + ReplyMsg((struct Message *)io); + } + } + + D(bug("[SCSI%02ld] %s: Done\n", ((struct scsi_Unit*)io->io_Unit)->su_UnitNum, __func__)); + AROS_LIBFUNC_EXIT +} + +AROS_LH1(LONG, AbortIO, + AROS_LHA(struct IORequest *, io, A1), + LIBBASETYPEPTR, LIBBASE, 6, scsi) +{ + AROS_LIBFUNC_INIT + + /* Cannot Abort IO */ + return 0; + + AROS_LIBFUNC_EXIT +} + +AROS_LH1(ULONG, GetRdskLba, + AROS_LHA(struct IORequest *, io, A1), + LIBBASETYPEPTR, LIBBASE, 7, scsi) +{ + AROS_LIBFUNC_INIT + + return 0; + + AROS_LIBFUNC_EXIT +} + +AROS_LH1(ULONG, GetBlkSize, + AROS_LHA(struct IORequest *, io, A1), + LIBBASETYPEPTR, LIBBASE, 8, scsi) +{ + AROS_LIBFUNC_INIT + + return Unit(io)->su_SectorShift; + + AROS_LIBFUNC_EXIT +} + +/* + * The daemon of scsi.device first opens all ATAPI devices and then enters + * endless loop. Every 2 seconds it tells ATAPI units to check the media + * presence. In case of any state change they will rise user-specified + * functions. + * The check is done by sending HD_SCSICMD+1 command (internal testchanged + * command). ATAPI units should already handle the command further. + */ +void DaemonCode(LIBBASETYPEPTR LIBBASE) +{ + struct IORequest *timer; // timer + UBYTE b = 0; + ULONG sigs; + + D(bug("[SCSI**] You woke up DAEMON\n")); + + /* + * Prepare message ports and timer.device's request + */ + timer = scsi_OpenTimer(LIBBASE); + if (!timer) + { + D(bug("[SCSI++] Failed to open timer!\n")); + + Forbid(); + Signal(LIBBASE->daemonParent, SIGF_SINGLE); + return; + } + + /* Calibrate 400ns delay */ + if (!scsi_Calibrate(timer, LIBBASE)) + { + scsi_CloseTimer(timer); + Forbid(); + Signal(LIBBASE->daemonParent, SIGF_SINGLE); + return; + } + + /* This also signals that we have initialized successfully */ + LIBBASE->scsi_Daemon = FindTask(NULL); + Signal(LIBBASE->daemonParent, SIGF_SINGLE); + + D(bug("[SCSI++] Starting sweep medium presence detection\n")); + + /* + * Endless loop + */ + do + { + /* + * call separate IORequest for every ATAPI device + * we're calling HD_SCSICMD+1 command here -- anything like test unit ready? + * FIXME: This is not a very nice approach in terms of performance. + * This inserts own command into command queue every 2 seconds, so + * this would give periodic performance drops under high loads. + * It would be much better if unit tasks ping their devices by themselves, + * when idle. This would also save us from lots of headaches with dealing + * with list of these requests. Additionally i start disliking all these + * semaphores. + */ + if (0 == (b & 1)) + { + struct IOStdReq *ios; + + DB2(bug("[SCSI++] Detecting media presence\n")); + ObtainSemaphore(&LIBBASE->DaemonSem); + + ForeachNode(&LIBBASE->Daemon_ios, ios) + { + /* Using the request will clobber its Node. Save links. */ + struct Node *s = ios->io_Message.mn_Node.ln_Succ; + struct Node *p = ios->io_Message.mn_Node.ln_Pred; + + DoIO((struct IORequest *)ios); + + ios->io_Message.mn_Node.ln_Succ = s; + ios->io_Message.mn_Node.ln_Pred = p; + } + + ReleaseSemaphore(&LIBBASE->DaemonSem); + } + + /* + * And then hide and wait for 1 second + */ + DB2(bug("[SCSI++] 1 second delay, timer 0x%p...\n", timer)); + sigs = scsi_WaitTO(timer, 1, 0, SIGBREAKF_CTRL_C); + + DB2(bug("[SCSI++] Delay completed\n")); + b++; + } while (!sigs); + + D(bug("[SCSI++] Daemon quits\n")); + + scsi_CloseTimer(timer); + + Forbid(); + Signal(LIBBASE->daemonParent, SIGF_SINGLE); +} + +/* + Bus task body. It doesn't really do much. It receives simply all IORequests + in endless loop and calls proper handling function. The IO is Semaphore- + protected within a bus. +*/ +void BusTaskCode(struct scsi_Bus *bus, LIBBASETYPEPTR LIBBASE) +{ + ULONG sig; + int iter; + struct IORequest *msg; + OOP_Object *unitObj; + struct scsi_Unit *unit; + + DINIT(bug("[SCSI**] Task started (bus: %u)\n", bus->sb_BusNum)); + + bus->sb_Timer = scsi_OpenTimer(LIBBASE); + bus->sb_BounceBufferPool = CreatePool(MEMF_CLEAR | MEMF_31BIT, 131072, 65536); + + /* Get the signal used for sleeping */ + bus->sb_Task = FindTask(0); + bus->sb_SleepySignal = AllocSignal(-1); + /* Failed to get it? Use SIGBREAKB_CTRL_E instead */ + if (bus->sb_SleepySignal < 0) + bus->sb_SleepySignal = SIGBREAKB_CTRL_E; + + sig = 1L << bus->sb_MsgPort->mp_SigBit; + + for (iter = 0; iter < MAX_BUSUNITS; ++iter) + { + DINIT(bug("[SCSI**] Device %u type %d\n", iter, bus->sb_Dev[iter])); + + if (bus->sb_Dev[iter] > DEV_UNKNOWN) + { + unitObj = OOP_NewObject(LIBBASE->unitClass, NULL, NULL); + if (unitObj) + { + unit = OOP_INST_DATA(LIBBASE->unitClass, unitObj); + scsi_init_unit(bus, unit, iter); + if (scsi_setup_unit(bus, unit)) + { + /* + * Add unit to the bus. + * At this point it becomes visible to OpenDevice(). + */ + bus->sb_Units[iter] = unitObj; + + if (unit->su_XferModes & AF_XFER_PACKET) + { + scsi_RegisterVolume(0, 0, unit); + + /* For ATAPI device we also submit media presence detection request */ + unit->DaemonReq = (struct IOStdReq *)CreateIORequest(LIBBASE->DaemonPort, sizeof(struct IOStdReq)); + if (unit->DaemonReq) + { + /* + * We don't want to keep stalled open count of 1, so we + * don't call OpenDevice() here. Instead we fill in the needed + * fields manually. + */ + unit->DaemonReq->io_Device = &LIBBASE->scsi_Device; + unit->DaemonReq->io_Unit = &unit->su_Unit; + unit->DaemonReq->io_Command = HD_SCSICMD+1; + + ObtainSemaphore(&LIBBASE->DaemonSem); + AddTail((struct List *)&LIBBASE->Daemon_ios, + &unit->DaemonReq->io_Message.mn_Node); + ReleaseSemaphore(&LIBBASE->DaemonSem); + } + } + else + { + scsi_RegisterVolume(0, unit->su_Cylinders - 1, unit); + } + } + else + { + /* Destroy unit that couldn't be initialised */ + OOP_DisposeObject((OOP_Object *)unit); + bus->sb_Dev[iter] = DEV_NONE; + } + } + } + } + + D(bug("[SCSI--] Bus %u scan finished\n", bus->sb_BusNum)); + ReleaseSemaphore(&LIBBASE->DetectionSem); + + /* Wait forever and process messages */ + for (;;) + { + Wait(sig); + + /* Even if you get new signal, do not process it until Unit is not active */ + if (!(bus->sb_Flags & UNITF_ACTIVE)) + { + bus->sb_Flags |= UNITF_ACTIVE; + + /* Empty the request queue */ + while ((msg = (struct IORequest *)GetMsg(bus->sb_MsgPort))) + { + /* And do IO's */ + HandleIO(msg, LIBBASE); + /* TD_ADDCHANGEINT doesn't require reply */ + if (msg->io_Command != TD_ADDCHANGEINT) + { + ReplyMsg((struct Message *)msg); + } + } + + bus->sb_Flags &= ~(UNITF_INTASK | UNITF_ACTIVE); + } + } +} diff --git a/rom/devs/scsi/scsi.conf b/rom/devs/scsi/scsi.conf new file mode 100644 index 0000000000..4d48948c8e --- /dev/null +++ b/rom/devs/scsi/scsi.conf @@ -0,0 +1,136 @@ +##begin config +basename scsi +version 43.0 +libbase SCSIBase +libbasetype struct scsiBase +residentpri 4 +beginio_func BeginIO +abortio_func AbortIO +oopbase_field scsi_OOPBase +seglist_field scsi_SegList +addromtag scsi_BootWait +##end config + +##begin cdefprivate +#include +#include +#include "scsi.h" +##end cdefprivate + +##begin cdef +#include +#include +##end cdef + +##begin functionlist +ULONG GetRdskLba(struct IORequest *io) (A1) +ULONG GetBlkSize(struct IORequest *io) (A1) +##end functionlist + +##begin interface +##begin config +interfaceid hidd.scsi.bus +interfacename Hidd_SCSIBus +methodstub HIDD_SCSIBus +methodbase HiddSCSIBusBase +attributebase HiddSCSIBusAB +##end config + +##begin attributelist +BOOL Use80Wire # [..G] Tells if this bus uses 80-conductor cable +BOOL Use32Bit # [..G] Tells if this bus is 32-bit capable +BOOL UseDMA # [..G] Tells if this bus is DMA-capable +ULONG PIODataSize # [I..] Size of PIO interface data structure +ULONG DMADataSize # [I..] Size of DMA interface data structure +APTR *BusVectors # [I..] PIO interface control function table +APTR *PIOVectors # [I..] PIO interface transfer function table +APTR *DMAVectors # [I..] DMA interface function table +BOOL UseIOAlt # [..G} Tells if this bus supports alternate register bank +BOOL CanSetXferMode # [..G} Tells if transfer mode change is supported +##end attributelist + +##begin methodlist +APTR GetPIOInterface() +APTR GetDMAInterface() +BOOL SetXferMode(UBYTE unit, scsi_XferMode mode) +void Shutdown() +##end methodlist +##end interface + +##begin class +##begin config +basename SCSI +type hidd +classid CLID_Hidd_SCSI +classdatatype struct scsi_Controller +superclass CLID_Hidd_StorageController +classptr_field scsiClass +##end config + +##begin methodlist +.interface Root +New +Dispose +Get +.interface Hidd_StorageController +RemoveBus +SetUpBus +CleanUpBus +##end methodlist +##end class + +##begin class +##begin config +basename SCSIBus +type hidd +classid CLID_Hidd_SCSIBus +classdatatype struct scsi_Bus +superclass CLID_Hidd_StorageBus +classptr_field busClass +##end config + +##begin methodlist +.interface Root +New +Dispose +Get +.interface Hidd_StorageBus +EnumUnits +.interface Hidd_SCSIBus +GetPIOInterface +GetDMAInterface +SetXferMode +Shutdown +##end methodlist +##end class + +##begin interface +##begin config +interfaceid hidd.scsi.unit +interfacename Hidd_SCSIUnit +attributebase HiddSCSIUnitAB +##end config + +##begin attributelist +ULONG XferModes # [..G] Supported transfer modes +UBYTE MultiSector # [..G] Maximum amount of multisector transfer +ULONG ConfiguredModes # [..G] Configured transfer modes +##end attributelist +##end interface + +##begin class +##begin config +basename SCSIUnit +type hidd +classdatatype struct scsi_Unit +superclass CLID_Hidd_StorageUnit +classptr_field unitClass +##end config + +##begin methodlist +.interface Root +New +Dispose +Get +##end methodlist +##end class diff --git a/rom/devs/ata/ata.h b/rom/devs/scsi/scsi.h similarity index 60% copy from rom/devs/ata/ata.h copy to rom/devs/scsi/scsi.h index 9aed77f050..392963523d 100644 --- a/rom/devs/ata/ata.h +++ b/rom/devs/scsi/scsi.h @@ -1,11 +1,11 @@ -#ifndef _ATA_H -#define _ATA_H +#ifndef _SCSI_H +#define _SCSI_H /* - Copyright © 2004-2018, The AROS Development Team. All rights reserved. + Copyright © 2019, The AROS Development Team. All rights reserved. $Id$ - Desc: ata.device main private include file + Desc: scsi.device main private include file Lang: English */ @@ -28,8 +28,8 @@ #include #include #include -#include -#include +#include +#include #include "include/devices/scsicmds.h" @@ -44,47 +44,47 @@ Please note, that all structures here are more or less chained together. The aim is, every single function in ata.device, no matter whether it takes - ata_Unit or ata_Bus or God knows what else, would have access to ata device + scsi_Unit or scsi_Bus or God knows what else, would have access to ata device base and through it, to all other device structures. - I just wanted to avoid passing ataBase everywhere. :-D + I just wanted to avoid passing scsiBase everywhere. :-D */ /* structure forward declarations */ -struct ata_Unit; -struct ata_Bus; +struct scsi_Unit; +struct scsi_Bus; /* ata.device base */ -struct ataBase +struct scsiBase { - struct Device ata_Device; /* Exec device structure */ - struct Task *ata_Daemon; /* master task pointer */ + struct Device scsi_Device; /* Exec device structure */ + struct Task *scsi_Daemon; /* master task pointer */ struct MsgPort *DaemonPort; /* Daemon's message port */ struct MinList Daemon_ios; /* Daemon's IORequests */ struct SignalSemaphore DaemonSem; struct Task *daemonParent; /* Who sends control requests to daemon */ - int ata__buscount; /* Number of all buses */ + int scsi__buscount; /* Number of all buses */ struct SignalSemaphore DetectionSem; /* Device detection semaphore */ /* Arguments and flags */ - UBYTE ata_32bit; - UBYTE ata_NoMulti; - UBYTE ata_NoDMA; - UBYTE ata_Poll; + UBYTE scsi_32bit; + UBYTE scsi_NoMulti; + UBYTE scsi_NoDMA; + UBYTE scsi_Poll; /* * memory pool */ - APTR ata_MemPool; + APTR scsi_MemPool; - ULONG ata_ItersPer100ns; + ULONG scsi_ItersPer100ns; - struct Library *ata_OOPBase; - struct Library *ata_UtilityBase; - BPTR ata_SegList; + struct Library *scsi_OOPBase; + struct Library *scsi_UtilityBase; + BPTR scsi_SegList; /* Bus HIDD classes */ - OOP_Class *ataClass; + OOP_Class *scsiClass; OOP_Class *busClass; OOP_Class *unitClass; @@ -92,7 +92,7 @@ struct ataBase OOP_AttrBase unitAttrBase; OOP_AttrBase hwAttrBase; OOP_AttrBase busAttrBase; - OOP_AttrBase atabusAttrBase; + OOP_AttrBase sbusAttrBase; OOP_AttrBase sunitAttrBase; #endif #if defined(__OOP_NOMETHODBASES__) @@ -101,50 +101,50 @@ struct ataBase OOP_MethodID HiddSCMethodBase; #endif - struct List ata_Controllers; + struct List scsi_Controllers; }; #if defined(__OOP_NOATTRBASES__) #undef HWAttrBase #undef HiddBusAB -#undef HiddATABusAB -#undef HiddATAUnitAB +#undef HiddSCSIBusAB +#undef HiddSCSIUnitAB #undef HiddStorageUnitAB -#define HWAttrBase (ATABase->hwAttrBase) -#define HiddBusAB (ATABase->busAttrBase) -#define HiddATABusAB (ATABase->atabusAttrBase) -#define HiddATAUnitAB (ATABase->unitAttrBase) -#define HiddStorageUnitAB (ATABase->sunitAttrBase) +#define HWAttrBase (SCSIBase->hwAttrBase) +#define HiddBusAB (SCSIBase->busAttrBase) +#define HiddSCSIBusAB (SCSIBase->sbusAttrBase) +#define HiddSCSIUnitAB (SCSIBase->unitAttrBase) +#define HiddStorageUnitAB (SCSIBase->sunitAttrBase) #endif #if defined(__OOP_NOMETHODBASES__) #undef HWBase -#undef HiddATABusBase -#define HWBase (ATABase->hwMethodBase) -#define HiddATABusBase (ATABase->busMethodBase) -#define HiddStorageControllerBase (ATABase->HiddSCMethodBase) +#undef HiddSCSIBusBase +#define HWBase (SCSIBase->hwMethodBase) +#define HiddSCSIBusBase (SCSIBase->busMethodBase) +#define HiddStorageControllerBase (SCSIBase->HiddSCMethodBase) #endif -#define OOPBase (ATABase->ata_OOPBase) -#define UtilityBase (ATABase->ata_UtilityBase) +#define OOPBase (SCSIBase->scsi_OOPBase) +#define UtilityBase (SCSIBase->scsi_UtilityBase) -struct ata_Controller +struct scsi_Controller { - struct Node ac_Node; - OOP_Class *ac_Class; - OOP_Object *ac_Object; + struct Node sc_Node; + OOP_Class *sc_Class; + OOP_Object *sc_Object; }; /* A single IDE bus (channel) */ -struct ata_Bus +struct scsi_Bus { - struct ataBase *ab_Base; /* device self */ + struct scsiBase *sb_Base; /* device self */ /** Bus object data **/ - struct ATA_BusInterface *busVectors; /* Control vector table */ - struct ATA_PIOInterface *pioVectors; /* PIO vector table */ + struct SCSI_BusInterface *busVectors; /* Control vector table */ + struct SCSI_PIOInterface *pioVectors; /* PIO vector table */ APTR *dmaVectors; /* DMA vector table */ ULONG pioDataSize; /* PIO interface data size */ ULONG dmaDataSize; /* DMA interface data size */ @@ -153,28 +153,28 @@ struct ata_Bus BOOL keepEmpty; /* Whether we should keep empty bus object */ BOOL haveAltIO; - volatile UBYTE ab_Dev[MAX_BUSUNITS]; /* Master/Slave type, see below */ - UBYTE ab_Flags; /* Bus flags similar to unit flags */ - BYTE ab_SleepySignal; /* Signal used to wake the task up, when it's waiting */ + volatile UBYTE sb_Dev[MAX_BUSUNITS]; /* Master/Slave type, see below */ + UBYTE sb_Flags; /* Bus flags similar to unit flags */ + BYTE sb_SleepySignal; /* Signal used to wake the task up, when it's waiting */ /** Data Requests/DMA **/ - UBYTE ab_BusNum; /* bus id - used to calculate device id */ + UBYTE sb_BusNum; /* bus id - used to calculate device id */ - OOP_Object *ab_Units[MAX_BUSUNITS]; /* Units on the bus */ - struct ata_Unit *ab_SelectedUnit; /* Currently selected unit */ + OOP_Object *sb_Units[MAX_BUSUNITS]; /* Units on the bus */ + struct scsi_Unit *sb_SelectedUnit; /* Currently selected unit */ - ULONG ab_IntCnt; + ULONG sb_IntCnt; - struct Task *ab_Task; /* Bus task handling all not-immediate transactions */ - struct MsgPort *ab_MsgPort; /* Task's message port */ - struct IORequest *ab_Timer; /* timer stuff */ + struct Task *sb_Task; /* Bus task handling all not-immediate transactions */ + struct MsgPort *sb_MsgPort; /* Task's message port */ + struct IORequest *sb_Timer; /* timer stuff */ - struct Interrupt ab_ResetInt; + struct Interrupt sb_ResetInt; - APTR ab_BounceBufferPool; + APTR sb_BounceBufferPool; /** functions go here **/ - void (*ab_HandleIRQ)(struct ata_Unit* unit, UBYTE status); + void (*sb_HandleIRQ)(struct scsi_Unit* unit, UBYTE status); }; /* Device types */ @@ -227,8 +227,8 @@ struct DriveIdent { UWORD pad8[6]; // 69-74 UWORD id_QueueDepth; // 75 UWORD pad9[4]; // 76-79 - UWORD id_ATAVersion; // 80 - UWORD id_ATARevision; // 81 + UWORD id_SCSIVersion; // 80 + UWORD id_SCSIRevision; // 81 UWORD id_Commands1; // 82 UWORD id_Commands2; // 83 UWORD id_Commands3; // 84 @@ -264,7 +264,7 @@ struct DriveIdent { typedef struct { - UBYTE command; // current ATA command + UBYTE command; // current SCSI command UBYTE feature; // FF to indicate no feature UBYTE secmul; // for read multiple - multiplier. default 1 UBYTE pad; @@ -289,7 +289,7 @@ typedef struct CT_LBA28, CT_LBA48, } type; -} ata_CommandBlock; +} scsi_CommandBlock; struct DaemonIO { @@ -301,24 +301,24 @@ struct DaemonIO Unit structure describing given device on the bus. It contains all the necessary information unit/device may need. */ -struct ata_Unit +struct scsi_Unit { - struct Unit au_Unit; /* exec's unit */ - struct DriveIdent *au_Drive; /* Drive Ident after IDENTIFY command */ - struct ata_Bus *au_Bus; /* Bus on which this unit is */ + struct Unit su_Unit; /* exec's unit */ + struct DriveIdent *su_Drive; /* Drive Ident after IDENTIFY command */ + struct scsi_Bus *su_Bus; /* Bus on which this unit is */ struct IOStdReq *DaemonReq; /* Disk change monitoring request */ - ULONG au_XferModes; /* available transfer modes */ - ULONG au_UseModes; /* Used transfer modes */ + ULONG su_XferModes; /* available transfer modes */ + ULONG su_UseModes; /* Used transfer modes */ - ULONG au_Capacity; /* Highest sector accessible through LBA28 */ - UQUAD au_Capacity48; /* Highest sector accessible through LBA48 */ - ULONG au_Cylinders; - UBYTE au_Heads; - UBYTE au_Sectors; - UBYTE au_Model[41]; - UBYTE au_FirmwareRev[9]; - UBYTE au_SerialNumber[21]; + ULONG su_Capacity; /* Highest sector accessible through LBA28 */ + UQUAD su_Capacity48; /* Highest sector accessible through LBA48 */ + ULONG su_Cylinders; + UBYTE su_Heads; + UBYTE su_Sectors; + UBYTE su_Model[41]; + UBYTE su_FirmwareRev[9]; + UBYTE su_SerialNumber[21]; /* Here are stored pointers to functions responsible for handling this @@ -327,34 +327,34 @@ struct ata_Unit in PIO mode reading single sectors, using multisector PIO, or multiword DMA. */ - BYTE (*au_Read32 )(struct ata_Unit *, ULONG, ULONG, APTR, ULONG *); - BYTE (*au_Write32 )(struct ata_Unit *, ULONG, ULONG, APTR, ULONG *); - BYTE (*au_Read64 )(struct ata_Unit *, UQUAD, ULONG, APTR, ULONG *); - BYTE (*au_Write64 )(struct ata_Unit *, UQUAD, ULONG, APTR, ULONG *); - BYTE (*au_Eject )(struct ata_Unit *); - BYTE (*au_DirectSCSI)(struct ata_Unit *, struct SCSICmd*); - BYTE (*au_Identify )(struct ata_Unit *); - VOID (*au_ins )(APTR, APTR, ULONG); - VOID (*au_outs )(APTR, APTR, ULONG); + BYTE (*su_Read32 )(struct scsi_Unit *, ULONG, ULONG, APTR, ULONG *); + BYTE (*su_Write32 )(struct scsi_Unit *, ULONG, ULONG, APTR, ULONG *); + BYTE (*su_Read64 )(struct scsi_Unit *, UQUAD, ULONG, APTR, ULONG *); + BYTE (*su_Write64 )(struct scsi_Unit *, UQUAD, ULONG, APTR, ULONG *); + BYTE (*su_Eject )(struct scsi_Unit *); + BYTE (*su_DirectSCSI)(struct scsi_Unit *, struct SCSICmd*); + BYTE (*su_Identify )(struct scsi_Unit *); + VOID (*su_ins )(APTR, APTR, ULONG); + VOID (*su_outs )(APTR, APTR, ULONG); void *pioInterface; /* PIO interface object, cached for performance */ - ULONG au_UnitNum; /* Unit number as coded by device */ - ULONG au_Flags; /* Unit flags, see below */ - ULONG au_ChangeNum; /* Number of disc changes */ + ULONG su_UnitNum; /* Unit number as coded by device */ + ULONG su_Flags; /* Unit flags, see below */ + ULONG su_ChangeNum; /* Number of disc changes */ - struct Interrupt *au_RemoveInt; /* Raise this interrupt on a disc change */ - struct List au_SoftList; /* Raise even more interrupts from this list on disc change */ + struct Interrupt *su_RemoveInt; /* Raise this interrupt on a disc change */ + struct List su_SoftList; /* Raise even more interrupts from this list on disc change */ - UBYTE au_SectorShift; /* Sector shift. 9 here is 512 bytes sector */ - UBYTE au_DevMask; /* device mask used to simplify device number coding */ - UBYTE au_SenseKey; /* Sense key from ATAPI devices */ - UBYTE au_DevType; + UBYTE su_SectorShift; /* Sector shift. 9 here is 512 bytes sector */ + UBYTE su_DevMask; /* device mask used to simplify device number coding */ + UBYTE su_SenseKey; /* Sense key from ATAPI devices */ + UBYTE su_DevType; /******* PIO IO ********/ - APTR au_cmd_data; - ULONG au_cmd_length; - ULONG au_cmd_total; - ULONG au_cmd_error; + APTR su_cmd_data; + ULONG su_cmd_length; + ULONG su_cmd_total; + ULONG su_cmd_error; }; #define AF_XFER_DMA_MASK (AF_XFER_MDMA(0)|AF_XFER_MDMA(1)|AF_XFER_MDMA(2)| \ @@ -374,30 +374,25 @@ struct ata_Unit #define AF_DMA (1 << AB_DMA) #define AF_CHSOnly (1 << AB_CHSOnly) -#define Unit(io) ((struct ata_Unit *)(io)->io_Unit) +#define Unit(io) ((struct scsi_Unit *)(io)->io_Unit) #define IOStdReq(io) ((struct IOStdReq *)io) /* Function prototypes */ -BOOL Hidd_ATABus_Start(OOP_Object *o, struct ataBase *ATABase); -AROS_UFP3(BOOL, Hidd_ATABus_Open, +BOOL Hidd_SCSIBus_Start(OOP_Object *o, struct scsiBase *SCSIBase); +AROS_UFP3(BOOL, Hidd_SCSIBus_Open, AROS_UFPA(struct Hook *, h, A0), AROS_UFPA(OOP_Object *, obj, A2), AROS_UFPA(IPTR, reqUnit, A1)); -void ata_InitBus(struct ata_Bus *); -int atapi_TestUnitOK(struct ata_Unit *); -BOOL ata_setup_unit(struct ata_Bus *bus, struct ata_Unit *unit); -void ata_init_unit(struct ata_Bus *bus, struct ata_Unit *unit, UBYTE u); +void scsi_InitBus(struct scsi_Bus *); +int atapi_TestUnitOK(struct scsi_Unit *); +BOOL scsi_setup_unit(struct scsi_Bus *bus, struct scsi_Unit *unit); +void scsi_init_unit(struct scsi_Bus *bus, struct scsi_Unit *unit, UBYTE u); -BOOL ata_RegisterVolume(ULONG StartCyl, ULONG EndCyl, struct ata_Unit *unit); -void BusTaskCode(struct ata_Bus *bus, struct ataBase *ATABase); -void DaemonCode(struct ataBase *LIBBASE); - -BYTE SCSIEmu(struct ata_Unit*, struct SCSICmd*); - -void ata_SMARTCmd(struct IOStdReq *io); -void ata_TRIMCmd(struct IOStdReq *io); +BOOL scsi_RegisterVolume(ULONG StartCyl, ULONG EndCyl, struct scsi_Unit *unit); +void BusTaskCode(struct scsi_Bus *bus, struct scsiBase *SCSIBase); +void DaemonCode(struct scsiBase *LIBBASE); #define ATAPI_SS_EJECT 0x02 #define ATAPI_SS_LOAD 0x03 @@ -411,5 +406,5 @@ struct atapi_StartStop UBYTE pad2[7]; }; -#endif // _ATA_H +#endif // _SCSI_H diff --git a/rom/devs/scsi/scsi_bus.h b/rom/devs/scsi/scsi_bus.h new file mode 100644 index 0000000000..1fae80ff53 --- /dev/null +++ b/rom/devs/scsi/scsi_bus.h @@ -0,0 +1,102 @@ +/* + Copyright © 2019, The AROS Development Team. All rights reserved. + $Id$ + + Desc: private inline stubs for calling DMA and PIO vectors + Lang: English +*/ + +static inline VOID PIO_Out(struct scsi_Bus* bus, UBYTE val, UWORD offset) +{ + register void *obj = bus->pioInterface; + struct SCSI_BusInterface *vec = obj - sizeof(struct SCSI_BusInterface); + + vec->scsi_out(obj, val, offset); +} + +static inline UBYTE PIO_In(struct scsi_Bus* bus, UWORD offset) +{ + register void *obj = bus->pioInterface; + struct SCSI_BusInterface *vec = obj - sizeof(struct SCSI_BusInterface); + + return vec->scsi_in(obj, offset); +} + +static inline VOID PIO_OutAlt(struct scsi_Bus* bus, UBYTE val, UWORD offset) +{ + register void *obj = bus->pioInterface; + struct SCSI_BusInterface *vec = obj - sizeof(struct SCSI_BusInterface); + + vec->scsi_out_alt(obj, val, offset); +} + +static inline UBYTE PIO_InAlt(struct scsi_Bus* bus, UWORD offset) +{ + register void *obj = bus->pioInterface; + struct SCSI_BusInterface *vec = obj - sizeof(struct SCSI_BusInterface); + + return vec->scsi_in_alt(obj, offset); +} + +static inline BOOL DMA_Setup(struct scsi_Bus *bus, APTR buffer, IPTR size, BOOL read) +{ + register void *obj = bus->dmaInterface; + struct SCSI_DMAInterface *vec = obj - sizeof(struct SCSI_DMAInterface); + + return vec->dma_Prepare(obj, buffer, size, read); +} + +static inline void DMA_Start(struct scsi_Bus *bus) +{ + register void *obj = bus->dmaInterface; + struct SCSI_DMAInterface *vec = obj - sizeof(struct SCSI_DMAInterface); + + vec->dma_Start(obj); +} + +static inline void DMA_End(struct scsi_Bus *bus, APTR addr, IPTR len, BOOL read) +{ + register void *obj = bus->dmaInterface; + struct SCSI_DMAInterface *vec = obj - sizeof(struct SCSI_DMAInterface); + + vec->dma_End(obj, addr, len, read); +} + +static inline ULONG DMA_GetResult(struct scsi_Bus *bus) +{ + register void *obj = bus->dmaInterface; + struct SCSI_DMAInterface *vec = obj - sizeof(struct SCSI_DMAInterface); + + return vec->dma_Result(obj); +} + +/* Convert data instance pointer back to OOP object pointer */ +#define OOP_OBJECT(cl, data) (((void *)data) - cl->InstOffset) + +static inline void Unit_Enable32Bit(struct scsi_Unit *unit) +{ + struct scsi_Bus *bus = unit->su_Bus; + + unit->su_UseModes |= AF_XFER_PIO32; + unit->su_ins = bus->pioVectors->scsi_insl; + unit->su_outs = bus->pioVectors->scsi_outsl; +} + +static inline void Unit_Disable32Bit(struct scsi_Unit *unit) +{ + struct scsi_Bus *bus = unit->su_Bus; + + unit->su_UseModes &= ~AF_XFER_PIO32; + unit->su_ins = bus->pioVectors->scsi_insw; + unit->su_outs = bus->pioVectors->scsi_outsw; +} + +static inline void Unit_OutS(struct scsi_Unit *unit, APTR data, ULONG length) +{ + unit->su_outs(unit->pioInterface, data, length); +} + +static inline void Unit_InS(struct scsi_Unit *unit, APTR data, ULONG length) +{ + unit->su_ins(unit->pioInterface, data, length); +} diff --git a/rom/devs/scsi/scsi_busclass.c b/rom/devs/scsi/scsi_busclass.c new file mode 100644 index 0000000000..4a808ff4f8 --- /dev/null +++ b/rom/devs/scsi/scsi_busclass.c @@ -0,0 +1,1038 @@ +/* + Copyright © 2019, The AROS Development Team. All rights reserved. + $Id$ +*/ + +#include + +#include + +/* We want all other bases obtained from our base */ +#define __NOLIBBASE__ + +#include +#include + +#include +#include +#include +#include +#include + +#include "scsi.h" +#include "scsi_Bus.h" + +#define DIRQ(x) +#define DATTR(x) + +static void Hidd_SCSIBus_HandleIRQ(UBYTE status, struct scsi_Bus *bus) +{ + struct scsi_Unit *unit = bus->sb_SelectedUnit; + + /* + * don't waste your time on checking other devices. + * pass irq ONLY if task is expecting one; + */ + if (unit && bus->sb_HandleIRQ) + { + /* ok, we have a routine to handle any form of transmission etc. */ + DIRQ(bug("[SCSI%02d] IRQ: Calling dedicated handler 0x%p... \n", + unit->su_UnitNum, bus->sb_HandleIRQ)); + bus->sb_HandleIRQ(unit, status); + + return; + } + + DIRQ({ + /* + * if we got *here* then device is most likely not expected to have an irq. + */ + bug("[SCSI%02d] Spurious IRQ\n", unit ? unit->su_UnitNum : -1); + + if (0 == (ATAF_BUSY & status)) + { + bug("[SCSI ] STATUS: %02lx\n" , status); + bug("[SCSI ] ALT STATUS: %02lx\n" , PIO_InAlt(bus, scsi_AltStatus)); + bug("[SCSI ] ERROR: %02lx\n" , PIO_In(bus, scsi_Error)); + bug("[SCSI ] IRQ: REASON: %02lx\n", PIO_In(bus, atapi_Reason)); + } + }); +} + +static AROS_INTH1(ataBus_Reset, struct scsi_Bus *, bus) +{ + AROS_INTFUNC_INIT + + struct scsiBase *SCSIBase = bus->sb_Base; + OOP_Object *obj = (void *)bus - SCSIBase->busClass->InstOffset; + + D(bug("[SCSI:Bus] %s()\n", __func__)); + + HIDD_SCSIBus_Shutdown(obj); + + return FALSE; + + AROS_INTFUNC_EXIT +} + +/***************************************************************************************** + + NAME + --background-- + + LOCATION + CLID_Hidd_SCSIBus + + NOTES + This class serves as a base class for implementing IDE (ATA) bus drivers. + One particularity of this class is that IDE bus is very speed-critical. + At the other hand, the driver implements very lowlevel operations which + are called quite often. OOP_DoMethod() call is not fast enough, and in + order to circumvent this limitation, additionally to normal OOP API + IDE bus drivers offer two additional non-standard interfaces. Internally + they are implemented as library-alike function table plus driver-specific + data. For the purpose of some performance optimizations the function + table is private to ata.device and managed entirely by the base class. + Driver classes have access only to data portion. + + These interfaces are documented below. + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + --PIO interface-- + + LOCATION + CLID_Hidd_SCSIBus + + NOTES + PIO interface is responsible for accessing I/O registers on the IDE + bus, as well as performing PIO-mode 16- and 32-bit data transfers. + This interface is mandatory and must be implemented by the driver, + however some functions are optional. They can be either omitted + entirely from the function table, or set to NULL pointers. + + Control functions table for the interface consists of the following + functions (listed in their order in the array): + + VOID scsi_out(void *obj, UBYTE val, UWORD offset) + - Write byte into primary register bank with the given offset. + + UBYTE scsi_in(void *obj, UWORD offset) + - Read byte from primary register bank with the given offset. + + VOID scsi_out_alt(void *obj, UBYTE val, UWORD offset) + - Write byte into alternate register bank with the given offset. + This function is optional. + + UBYTE scsi_in_alt(void *obj, UWORD offset) + - Read byte from alternate register bank with the given offset. + This function is optional. + + Transfer functions table for the interface consists of the following + functions (listed in their order in the array): + + VOID scsi_outsw(void *obj, APTR address, ULONG count) + - Perform 16-bit PIO data write operation from the given memory + region of the given size. + + VOID scsi_insw(void *obj, APTR address, ULONG count) + - Perform 16-bit PIO data read operation into the given memory + region of the given size. + + VOID scsi_outsl(void *obj, APTR address, ULONG count) + - Perform 32-bit PIO data write operation from the given memory + region of the given size. This function is optional. + + UBYTE scsi_insl(void *obj, APTR address, ULONG count) + - Perform 32-bit PIO data read operation into the given memory + region of the given size. This function is optional. + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + --DMA interface-- + + LOCATION + CLID_Hidd_SCSIBus + + NOTES + DMA interface is optional, and is needed in order to support DMA data + transfers. + + Function table for the interface consists of the following functions: + + BOOL dma_Setup(void *obj, APTR buffer, IPTR size, BOOL read) + - Prepare the controller to DMA data transfer. The last argument is + TRUE for read operation and FALSE for write. The function should + return TRUE for success or FALSE for failure. + + VOID dma_Start(void *obj) + - Start DMA transfer. + + VOID dma_End(void *obj, APTR buffer, IPTR size, BOOL read) + - End DMA transfer and perform post-transfer cleanup of the given region. + + ULONG dma_Result(void *obj) + - Get resulting status of the operation. The function should return 0 + for successful completion or error code to be passed up to ata.device + caller in io_Result field of the IORequest. + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_Use80Wire + + SYNOPSIS + [..G], BOOL + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Tells whether the bus currently uses 80-conductor cable. + + NOTES + This attribute actually makes difference only for DMA modes. If + your bus driver returns FALSE, ata.device will not use modes + higher than UDMA2 on the bus. + + EXAMPLE + + BUGS + + SEE ALSO + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_Use32Bit + + SYNOPSIS + [.SG], BOOL + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + When queried, tells whether the bus supports 32-bit PIO data transfers. + When set, enables or disables 32-bit mode for PIO data transfers. + + NOTES + + EXAMPLE + + BUGS + + SEE ALSO + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_UseDMA + + SYNOPSIS + [..G], BOOL + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Tells whether the bus supports DMA transfers. + + NOTES + + EXAMPLE + + BUGS + + SEE ALSO + + INTERNALS + Default implementation in base class returns value depending on whether + the subclass provided DMA interface function table during object creation. + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_PIODataSize + + SYNOPSIS + [I..], BOOL + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Specifies size of PIO interface data structure. + + NOTES + + EXAMPLE + + BUGS + + SEE ALSO + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_DMADataSize + + SYNOPSIS + [I..], BOOL + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Specifies size of DMA interface data structure. + + NOTES + + EXAMPLE + + BUGS + + SEE ALSO + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_BusVectors + + SYNOPSIS + [I..], APTR * + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Specifies control functions table for building PIO interface object. + The function table is an array of function pointers terminated + by -1 value. The terminator must be present for purpose of + binary compatibility with future extensions. + + NOTES + This function table is mandatory to be implemented by the driver. + + EXAMPLE + + BUGS + + SEE ALSO + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_PIOVectors + + SYNOPSIS + [I..], APTR * + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Specifies transfers function table for building PIO interface object. + The function table is an array of function pointers terminated + by -1 value. The terminator must be present for purpose of + binary compatibility with future extensions. + + NOTES + This function table is mandatory to be implemented by the driver. + + EXAMPLE + + BUGS + + SEE ALSO + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_DMAVectors + + SYNOPSIS + [I..], APTR * + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Specifies function table for building DMA interface object. If not supplied, + the bus is considered not DMA-capable. + + NOTES + + EXAMPLE + + BUGS + + SEE ALSO + aoHidd_SCSIBus_PIOVectors + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_UseIOAlt + + SYNOPSIS + [..G], BOOL + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Tells whether the bus supports alternate registers bank + (scsi_AltControl and scsi_AltStatus). + + NOTES + + EXAMPLE + + BUGS + + SEE ALSO + + INTERNALS + Default implementation in base class returns value depending on whether + the subclass provided respective I/O functions in bus interface vector + table during object creation. + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_Master + + SYNOPSIS + [..G], OOP_Object * + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Returns a pointer to OOP object of private unit class, representing + a master drive on the bus, or NULL if there's no master device. + + NOTES + + EXAMPLE + + BUGS + + SEE ALSO + aoHidd_SCSIBus_Slave + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_Slave + + SYNOPSIS + [..G], OOP_Object * + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Returns a pointer to OOP object of private unit class, representing + a slave drive on the bus, or NULL if there's no master device. + + NOTES + + EXAMPLE + + BUGS + + SEE ALSO + aoHidd_SCSIBus_Master + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIBus_CanSetXferMode + + SYNOPSIS + [..G], BOOL + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Tells whether the bus driver implements moHidd_SCSIBus_SetXferMode method. + + NOTES + + EXAMPLE + + BUGS + Current version of ata.device does not use this attribute, and it is + considered reserved. + + SEE ALSO + moHidd_SCSIBus_SetXferMode + + INTERNALS + +*****************************************************************************************/ + +OOP_Object *SCSIBus__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg) +{ + struct scsiBase *SCSIBase = cl->UserData; + D(bug("[SCSI:Bus] %s()\n", __func__)); + + o = (OOP_Object *)OOP_DoSuperMethod(cl, o, &msg->mID); + if (o) + { + struct scsi_Bus *data = OOP_INST_DATA(cl, o); + struct TagItem *tstate = msg->attrList; + struct TagItem *tag; + + D( + bug("[SCSI:Bus] %s: instance @ 0x%p\n", __func__, o); + bug("[SCSI:Bus] %s: scsi_Bus @ 0x%p\n", __func__, data); + ) + + /* Defaults */ + data->keepEmpty = TRUE; + + while ((tag = NextTagItem(&tstate))) + { + ULONG idx; + + Hidd_SCSIBus_Switch(tag->ti_Tag, idx) + { + case aoHidd_SCSIBus_PIODataSize: + data->pioDataSize = tag->ti_Data; + D(bug("[SCSI:Bus] %s: PIODataSize = %d\n", __func__, data->pioDataSize);) + break; + + case aoHidd_SCSIBus_DMADataSize: + data->dmaDataSize = tag->ti_Data; + DATTR(bug("[SCSI:Bus] %s: DMADataSize = %d\n", __func__, data->dmaDataSize);) + break; + + case aoHidd_SCSIBus_BusVectors: + data->busVectors = (struct SCSI_BusInterface *)tag->ti_Data; + DATTR(bug("[SCSI:Bus] %s: BusVectors @ 0x%p\n", __func__, data->busVectors);) + break; + + case aoHidd_SCSIBus_PIOVectors: + data->pioVectors = (struct SCSI_PIOInterface *)tag->ti_Data; + DATTR(bug("[SCSI:Bus] %s: PIOVectors @ 0x%p\n", __func__, data->pioVectors);) + break; + + case aoHidd_SCSIBus_DMAVectors: + data->dmaVectors = (APTR *)tag->ti_Data; + DATTR(bug("[SCSI:Bus] %s: DMAVectors @ 0x%p\n", __func__, data->dmaVectors);) + break; + } + Hidd_Bus_Switch(tag->ti_Tag, idx) + { + case aoHidd_Bus_KeepEmpty: + data->keepEmpty = tag->ti_Data; + break; + } + } + + /* Cache device base pointer. Useful. */ + data->sb_Base = SCSIBase; + + /* Install reset callback */ + data->sb_ResetInt.is_Node.ln_Name = SCSIBase->scsi_Device.dd_Library.lib_Node.ln_Name; + data->sb_ResetInt.is_Code = (VOID_FUNC)ataBus_Reset; + data->sb_ResetInt.is_Data = data; + AddResetCallback(&data->sb_ResetInt); + } + return o; +} + +void SCSIBus__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg) +{ + struct scsi_Bus *data = OOP_INST_DATA(cl, o); + + RemResetCallback(&data->sb_ResetInt); + + if (data->dmaInterface) + { + void *ptr = data->dmaInterface - sizeof(struct SCSI_DMAInterface); + + FreeMem(ptr, sizeof(struct SCSI_DMAInterface) + data->dmaDataSize); + } + if (data->pioInterface) + { + void *ptr = data->pioInterface - sizeof(struct SCSI_BusInterface); + + FreeMem(ptr, sizeof(struct SCSI_BusInterface) + data->pioDataSize); + } + + OOP_DoSuperMethod(cl, o, msg); +} + +/* + * Here we take into account that the table can be either + * terminated early, or have NULL entries. + */ +#define HAVE_VECTOR(x) (x && (x != (APTR)-1)) + +void SCSIBus__Root__Get(OOP_Class *cl, OOP_Object *o, struct pRoot_Get *msg) +{ + struct scsiBase *SCSIBase = cl->UserData; + struct scsi_Bus *data = OOP_INST_DATA(cl, o); + ULONG idx; + + Hidd_Bus_Switch (msg->attrID, idx) + { + case aoHidd_Bus_MaxUnits: + *msg->storage = MAX_BUSUNITS; + return; + } + + Hidd_SCSIBus_Switch (msg->attrID, idx) + { + case aoHidd_SCSIBus_Use80Wire: + *msg->storage = FALSE; + return; + + case aoHidd_SCSIBus_Use32Bit: + *msg->storage = (HAVE_VECTOR(data->pioVectors->scsi_outsl) && + HAVE_VECTOR(data->pioVectors->scsi_insl)) ? + TRUE : FALSE; + return; + + case aoHidd_SCSIBus_UseDMA: + *msg->storage = data->dmaVectors ? TRUE : FALSE; + return; + + case aoHidd_SCSIBus_UseIOAlt: + *msg->storage = (HAVE_VECTOR(data->busVectors->scsi_out_alt) && + HAVE_VECTOR(data->busVectors->scsi_in_alt)) ? + TRUE : FALSE; + return; + + case aoHidd_SCSIBus_CanSetXferMode: + *msg->storage = FALSE; + return; + } + + OOP_DoSuperMethod(cl, o, &msg->mID); +} + +void SCSIBus__Hidd_StorageBus__EnumUnits(OOP_Class *cl, OOP_Object *o, struct pHidd_StorageBus_EnumUnits *msg) +{ + struct scsi_Bus *data = OOP_INST_DATA(cl, o); + BOOL stop = FALSE; + + D(bug ("[SCSI:Bus] Hidd_StorageBus__EnumUnits()\n");) + + if (data->sb_Units[0]) + stop = CALLHOOKPKT(msg->callback, data->sb_Units[0], msg->hookMsg); + if ((!stop) && (data->sb_Units[1])) + stop = CALLHOOKPKT(msg->callback, data->sb_Units[1], msg->hookMsg); +} + +/* Default scsi_out_alt does nothing */ +static void default_out_alt(void *obj, UBYTE val, UWORD offset) +{ + +} + +/* Default scsi_in_alt wraps AltStatus to status */ +static UBYTE default_in_alt(void *obj, UWORD offset) +{ + struct SCSI_BusInterface *vec = obj - sizeof(struct SCSI_BusInterface); + + return vec->scsi_in(obj, scsi_Status); +} + +static void CopyVectors(APTR *dest, APTR *src, unsigned int num) +{ + unsigned int i; + + for (i = 0; i < num; i++) + { + if (src[i] == (APTR *)-1) + return; + if (src[i]) + dest[i] = src[i]; + } +} + +/***************************************************************************************** + + NAME + moHidd_SCSIBus_GetPIOInterface + + SYNOPSIS + APTR OOP_DoMethod(OOP_Object *obj, struct pHidd_SCSIBus_GetPIOInterface *Msg); + + APTR HIDD_SCSIBus_GetPIOInterface(void); + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Instantiates encapsulated PIO interface object and returns its + pointer. + + INPUTS + None + + RESULT + A pointer to opaque PIO interface object or NULL in case of failure. + + NOTES + This method should be overloaded by driver subclasses in order to + initialize data portion of the interface object. + + EXAMPLE + + BUGS + + SEE ALSO + moHidd_SCSIBus_GetDMAInterface + + INTERNALS + Interface objects contain not only driver-specific data, but also + a private vector table. Because of this you cannot just AllocMem() + the necessary structure in your driver. Always call OOP_DoSuperMethod() + in order for the base class to instantiate the interface correctly. + +*****************************************************************************************/ + +APTR SCSIBus__Hidd_SCSIBus__GetPIOInterface(OOP_Class *cl, OOP_Object *o, OOP_Msg msg) +{ + struct scsi_Bus *data = OOP_INST_DATA(cl, o); + struct SCSI_BusInterface *vec; + + D( + bug("[SCSI:Bus] %s()\n", __func__); + bug("[SCSI:Bus] %s: scsi_Bus @ 0x%p\n", __func__, data); + ) + + vec = AllocMem(sizeof(struct SCSI_BusInterface) + data->pioDataSize, + MEMF_PUBLIC|MEMF_CLEAR); + if (vec) + { + D(bug("[SCSI:Bus] %s: SCSI_BusInterface @ 0x%p (%d bytes + %d)\n", __func__, vec, sizeof(struct SCSI_BusInterface), data->pioDataSize);) + + /* Some default vectors for simplicity */ + vec->scsi_out_alt = default_out_alt; + vec->scsi_in_alt = default_in_alt; + + CopyVectors((APTR *)vec, (APTR *)data->busVectors, + sizeof(struct SCSI_BusInterface) / sizeof(APTR)); + + data->pioInterface = &vec[1]; + return data->pioInterface; + } + + return NULL; +} + +/***************************************************************************************** + + NAME + moHidd_SCSIBus_GetDMAInterface + + SYNOPSIS + APTR OOP_DoMethod(OOP_Object *obj, struct pHidd_SCSIBus_GetDMAInterface *Msg); + + APTR HIDD_SCSIBus_GetDMAInterface(void); + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Instantiates encapsulated DMA interface object and returns its + pointer. + + INPUTS + None + + RESULT + A pointer to opaque DMA interface object or NULL upon failure or + if DMA is not supported by this bus. + + NOTES + This method should be overloaded by driver subclasses in order to + initialize data portion of the interface object. + + EXAMPLE + + BUGS + + SEE ALSO + moHidd_SCSIBus_GetPIOInterface + + INTERNALS + Interface objects contain not only driver-specific data, but also + a private vector table. Because of this you cannot just AllocMem() + the necessary structure in your driver. Always call OOP_DoSuperMethod() + in order for the base class to instantiate the interface correctly. + +*****************************************************************************************/ + +APTR SCSIBus__Hidd_SCSIBus__GetDMAInterface(OOP_Class *cl, OOP_Object *o, OOP_Msg msg) +{ + struct scsi_Bus *data = OOP_INST_DATA(cl, o); + struct SCSI_DMAInterface *vec; + + D(bug("[SCSI:Bus] %s()\n", __func__)); + + if (!data->dmaVectors) + return NULL; + + vec = AllocMem(sizeof(struct SCSI_DMAInterface) + data->dmaDataSize, + MEMF_PUBLIC|MEMF_CLEAR); + if (vec) + { + CopyVectors((APTR *)vec, data->dmaVectors, + sizeof(struct SCSI_DMAInterface) / sizeof(APTR)); + + data->dmaInterface = &vec[1]; + return data->dmaInterface; + } + + return NULL; +} + +/***************************************************************************************** + + NAME + moHidd_SCSIBus_SetXferMode + + SYNOPSIS + APTR OOP_DoMethod(OOP_Object *obj, struct pHidd_SCSIBus_SetXferMode *Msg); + + APTR HIDD_SCSIBus_SetXferMode(UBYTE unit, scsi_XferMode mode); + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Sets the desired transfer mode for the given drive on the bus controller. + + INPUTS + unit - drive number (0 for master and 1 for slave) + mode - Mode number (see hidd/ata.h) + + RESULT + TRUE if successful or FALSE if the desired mode is not supported + by the hardware. + + NOTES + The default implementation is provided for drivers not supporting + DMA and always returns FALSE if the caller attempts to set any of + DMA modes. + + EXAMPLE + + BUGS + Current version of ata.device does not use this method, and it is + considered reserved. + + SEE ALSO + aoHidd_SCSIBus_CanSetXferMode + + INTERNALS + +*****************************************************************************************/ + +BOOL SCSIBus__Hidd_SCSIBus__SetXferMode(OOP_Class *cl, OOP_Object *o, struct pHidd_SCSIBus_SetXferMode *msg) +{ + D(bug("[SCSI:Bus] %s()\n", __func__)); + + if ((msg->mode >= AB_XFER_MDMA0) && (msg->mode <= AB_XFER_UDMA6)) + { + /* DMA is not supported, we cannot set DMA modes */ + return FALSE; + } + + return TRUE; +} + +/***************************************************************************************** + + NAME + moHidd_SCSIBus_Shutdown + + SYNOPSIS + APTR OOP_DoMethod(OOP_Object *obj, struct pHidd_SCSIBus_Shutdown *Msg); + + APTR HIDD_SCSIBus_Shutdown(void); + + LOCATION + CLID_Hidd_SCSIBus + + FUNCTION + Instantly shutdown all activity on the bus. + + INPUTS + None + + RESULT + None + + NOTES + This method is called by ata.device during system reset handler execution. + + EXAMPLE + + BUGS + + SEE ALSO + + INTERNALS + Default implementation disables interrupt using AltControl register. + +*****************************************************************************************/ + +void SCSIBus__Hidd_SCSIBus__Shutdown(OOP_Class *cl, OOP_Object *o, OOP_Msg *msg) +{ + struct scsi_Bus *data = OOP_INST_DATA(cl, o); + + D(bug("[SCSI:Bus] %s()\n", __func__)); + + if (data->pioInterface) + { + struct SCSI_BusInterface *vec = data->pioInterface - sizeof(struct SCSI_BusInterface); + + vec->scsi_out_alt(data->pioInterface, SCSICTLF_INT_DISABLE, scsi_AltControl); + } +} + +/***************** Private nonvirtual methods follow *****************/ + +BOOL Hidd_SCSIBus_Start(OOP_Object *o, struct scsiBase *SCSIBase) +{ + struct scsi_Bus *sb = OOP_INST_DATA(SCSIBase->busClass, o); + + D(bug("[SCSI:Bus] %s()\n", __func__)); + + /* Attach IRQ handler */ + OOP_SetAttrsTags(o, aHidd_Bus_IRQHandler, Hidd_SCSIBus_HandleIRQ, + aHidd_Bus_IRQData , sb, + TAG_DONE); + + /* scan bus - try to locate all devices (disables irq) */ + scsi_InitBus(sb); + + if ((sb->sb_Dev[0] == DEV_NONE) && (sb->sb_Dev[1] == DEV_NONE) && + (!sb->keepEmpty)) + { + /* + * If there are no devices, and KeepEmpty is not set + * the bus will be thrown away. + */ + return FALSE; + } + + /* + * Assign bus number. + * TODO: + * 1. This does not take into account possibility to + * unload drivers. In this case existing units will disappear, + * freeing up their numbers. These numbers should be reused. + * 2. We REALLY need modify-and-fetch atomics. + */ + Forbid(); + sb->sb_BusNum = SCSIBase->scsi__buscount++; + Permit(); + + if ((sb->sb_Dev[0] < DEV_ATA) && (sb->sb_Dev[1] < DEV_ATA)) + { + /* Do not start up task if there are no usable devices. */ + return TRUE; + } + + /* + * This small trick is based on the fact that shared semaphores + * have no specific owner. You can obtain and release them from + * within any task. It will block only on attempt to re-lock it + * in exclusive mode. + * So instead of complex handshake we obtain the semaphore before + * starting bus task. It will release the semaphore when done. + */ + ObtainSemaphoreShared(&SCSIBase->DetectionSem); + + /* + * Start up bus task. It will perform scanning asynchronously, and + * then, if successful, insert units. This allows to keep things parallel. + */ + D(bug("[SCSI>>] Start: Bus %u: Unit 0 - %d, Unit 1 - %d\n", sb->sb_BusNum, sb->sb_Dev[0], sb->sb_Dev[1])); + return NewCreateTask(TASKTAG_PC , BusTaskCode, + TASKTAG_NAME , "ATA[PI] Subsystem", + TASKTAG_STACKSIZE , STACK_SIZE, + TASKTAG_PRI , TASK_PRI, + TASKTAG_TASKMSGPORT, &sb->sb_MsgPort, + TASKTAG_ARG1 , sb, + TASKTAG_ARG2 , SCSIBase, + TAG_DONE) ? TRUE : FALSE; +} + +AROS_UFH3(BOOL, Hidd_SCSIBus_Open, + AROS_UFHA(struct Hook *, h, A0), + AROS_UFHA(OOP_Object *, obj, A2), + AROS_UFHA(IPTR, reqUnit, A1)) +{ + AROS_USERFUNC_INIT + + struct IORequest *req = h->h_Data; + struct scsiBase *SCSIBase = (struct scsiBase *)req->io_Device; + struct scsi_Bus *sb = (struct scsi_Bus *)OOP_INST_DATA(SCSIBase->busClass, obj); + ULONG bus = reqUnit >> 1; + UBYTE dev = reqUnit & 1; + + D(bug("[SCSI:Bus] %s()\n", __func__)); + D(bug("[SCSI%02ld] Checking bus %u dev %u\n", reqUnit, bus, dev)); + + if ((sb->sb_BusNum == bus) && sb->sb_Units[dev]) + { + struct scsi_Unit *unit = (struct scsi_Unit *)OOP_INST_DATA(SCSIBase->unitClass, sb->sb_Units[dev]); + + /* Got the unit */ + req->io_Unit = &unit->su_Unit; + req->io_Error = 0; + + unit->su_Unit.unit_OpenCnt++; + return TRUE; + } + + return FALSE; + + AROS_USERFUNC_EXIT +} diff --git a/rom/devs/scsi/scsi_controllerclass.c b/rom/devs/scsi/scsi_controllerclass.c new file mode 100644 index 0000000000..ac439029e9 --- /dev/null +++ b/rom/devs/scsi/scsi_controllerclass.c @@ -0,0 +1,124 @@ +/* + Copyright (C) 2019, The AROS Development Team. All rights reserved. + $Id$ +*/ + +#include + +/* We want all other bases obtained from our base */ +#define __NOLIBBASE__ + +#include + +#include +#include +#include +#include +#include + +#include "scsi.h" + +const char scsi_DevName[] = "SCSI Controller"; + +OOP_Object *SCSI__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg) +{ + struct scsiBase *SCSIBase = cl->UserData; + + OOP_Object *scsiController = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg)msg); + if (scsiController) + { + struct scsi_Controller *data = OOP_INST_DATA(cl, scsiController); + + /*register the controller in ata.device */ + D(bug ("[SCSI:Controller] Root__New: Controller Entry @ 0x%p\n", data);) + + data->sc_Class = cl; + data->sc_Object = scsiController; + + AddTail(&SCSIBase->scsi_Controllers, &data->sc_Node); + } + return scsiController; +} + +VOID SCSI__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg) +{ + struct scsiBase *SCSIBase = cl->UserData; + struct scsi_Controller *scsiNode, *tmpNode; + + D(bug ("[SCSI:Controller] Root__Dispose(0x%p)\n", o);) + + ForeachNodeSafe (&SCSIBase->scsi_Controllers, scsiNode, tmpNode) + { + if (scsiNode->sc_Object == o) + { + D(bug ("[SCSI:Controller] Root__Dispose: Destroying Controller Entry @ 0x%p\n", scsiNode);) + Remove(&scsiNode->sc_Node); + OOP_DoSuperMethod(cl, o, (OOP_Msg)msg); + return; + } + } +} + +void SCSI__Root__Get(OOP_Class *cl, OOP_Object *o, struct pRoot_Get *msg) +{ +#if (0) + struct scsi_Controller *data = OOP_INST_DATA(cl, o); + IPTR idx; + + if (IS_SCSI_ATTR(msg->attrID, idx)) + { + switch (idx) + { + case aoHidd_SCSI_xxx: + *msg->storage = (IPTR)data->yyyy; + return; + } + } +#endif + OOP_DoSuperMethod(cl, o, (OOP_Msg)msg); +} + +BOOL SCSI__Hidd_StorageController__RemoveBus(OOP_Class *cl, OOP_Object *o, struct pHidd_StorageController_RemoveBus *msg) +{ + D(bug ("[SCSI:Controller] Hidd_StorageController__RemoveBus(0x%p)\n", msg->busObject);) + /* + * Currently we don't support unloading SCSI bus drivers. + * This is a very-very big TODO. + */ + return FALSE; +} + +BOOL SCSI__Hidd_StorageController__SetUpBus(OOP_Class *cl, OOP_Object *o, struct pHidd_StorageController_SetUpBus *msg) +{ + struct scsiBase *SCSIBase = cl->UserData; + + D(bug ("[SCSI:Controller] Hidd_StorageController__SetUpBus(0x%p)\n", msg->busObject);) + +#if (0) + /* + * Instantiate interfaces. PIO is mandatory, DMA is not. + * We don't keep interface pointers here because our bus class + * stores them itself. + * We do this in SetUpBus because the object must be fully + * created in order for this stuff to work. + */ + if (!HIDD_SCSIBus_GetPIOInterface(msg->busObject)) + return FALSE; + + D(bug ("[SCSI:Controller] Hidd_StorageController__SetUpBus: PIO Interfaces obtained\n");) + + if (!SCSIBase->scsi_NoDMA) + HIDD_SCSIBus_GetDMAInterface(msg->busObject); + + D(bug ("[SCSI:Controller] Hidd_StorageController__SetUpBus: Starting Bus...\n");) +#endif + + /* Add the bus to the device and start service */ + return Hidd_SCSIBus_Start(msg->busObject, SCSIBase); +} + +void SCSI__Hidd_StorageController__CleanUpBus(OOP_Class *cl, OOP_Object *o, struct pHidd_StorageController_CleanUpBus *msg) +{ + D(bug ("[SCSI:Controller] Hidd_StorageController__CleanUpBus(0x%p)\n", msg->busObject);) + /* By default we have nothing to do here */ +} diff --git a/rom/devs/scsi/scsi_init.c b/rom/devs/scsi/scsi_init.c new file mode 100644 index 0000000000..500dc0852e --- /dev/null +++ b/rom/devs/scsi/scsi_init.c @@ -0,0 +1,375 @@ +/* + Copyright © 2004-2019, The AROS Development Team. All rights reserved + $Id$ + + Desc: + Lang: English +*/ + +#include + +#include + +/* We want all other bases obtained from our base */ +#define __NOLIBBASE__ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi.h" +#include "timer.h" + +#include LC_LIBDEFS_FILE + +/* Add a bootnode using expansion.library */ +BOOL scsi_RegisterVolume(ULONG StartCyl, ULONG EndCyl, struct scsi_Unit *unit) +{ + struct ExpansionBase *ExpansionBase; + struct DeviceNode *devnode; + TEXT dosdevname[4] = "HD0"; + const ULONG IdDOS = AROS_MAKE_ID('D','O','S','\001'); + const ULONG IdCDVD = AROS_MAKE_ID('C','D','V','D'); + + ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library", + 40L); + + if (ExpansionBase) + { + IPTR pp[24]; + + /* This should be dealt with using some sort of volume manager or such. */ + switch (unit->su_DevType) + { + case DG_DIRECT_ACCESS: + break; + case DG_CDROM: + dosdevname[0] = 'C'; + break; + default: + D(bug("[SCSI>>]:-scsi_RegisterVolume called on unknown devicetype\n")); + } + + if (unit->su_UnitNum < 10) + dosdevname[2] += unit->su_UnitNum % 10; + else + dosdevname[2] = 'A' - 10 + unit->su_UnitNum; + + pp[0] = (IPTR)dosdevname; + pp[1] = (IPTR)MOD_NAME_STRING; + pp[2] = unit->su_UnitNum; + pp[DE_TABLESIZE + 4] = DE_BOOTBLOCKS; + pp[DE_SIZEBLOCK + 4] = 1 << (unit->su_SectorShift - 2); + pp[DE_NUMHEADS + 4] = unit->su_Heads; + pp[DE_SECSPERBLOCK + 4] = 1; + pp[DE_BLKSPERTRACK + 4] = unit->su_Sectors; + pp[DE_RESERVEDBLKS + 4] = 2; + pp[DE_LOWCYL + 4] = StartCyl; + pp[DE_HIGHCYL + 4] = EndCyl; + pp[DE_NUMBUFFERS + 4] = 10; + pp[DE_BUFMEMTYPE + 4] = MEMF_PUBLIC | MEMF_31BIT; + pp[DE_MAXTRANSFER + 4] = 0x00200000; + pp[DE_MASK + 4] = 0x7FFFFFFE; + pp[DE_BOOTPRI + 4] = ((unit->su_DevType == DG_DIRECT_ACCESS) ? 0 : 10); + pp[DE_DOSTYPE + 4] = ((unit->su_DevType == DG_DIRECT_ACCESS) ? IdDOS : IdCDVD); + pp[DE_CONTROL + 4] = 0; + pp[DE_BOOTBLOCKS + 4] = 2; + + devnode = MakeDosNode(pp); + + if (devnode) + { + D(bug("[SCSI>>]:-scsi_RegisterVolume: '%b', type=0x%08lx with StartCyl=%d, EndCyl=%d .. ", + devnode->dn_Name, pp[DE_DOSTYPE + 4], StartCyl, EndCyl)); + + AddBootNode(pp[DE_BOOTPRI + 4], ADNF_STARTPROC, devnode, NULL); + D(bug("done\n")); + + return TRUE; + } + + CloseLibrary((struct Library *)ExpansionBase); + } + + return FALSE; +} + +#if defined(__OOP_NOATTRBASES__) +/* Keep order the same as order of IDs in struct scsiBase! */ +static CONST_STRPTR const attrBaseIDs[] = +{ + IID_Hidd_SCSIUnit, + IID_HW, + IID_Hidd_Bus, + IID_Hidd_SCSIBus, + IID_Hidd_StorageUnit, + NULL +}; +#endif + +#if defined(__OOP_NOMETHODBASES__) +static CONST_STRPTR const methBaseIDs[] = +{ + IID_HW, + IID_Hidd_SCSIBus, + IID_Hidd_StorageController, + NULL +}; +#endif + +static int SCSI_init(struct scsiBase *SCSIBase) +{ + struct BootLoaderBase *BootLoaderBase; + + D(bug("[SCSI--] %s: scsi.device Initialization\n", __PRETTY_FUNCTION__)); + + /* Prepare the list of detected controllers */ + NEWLIST(&SCSIBase->scsi_Controllers); + + /* Set default scsi.device config options */ + SCSIBase->scsi_32bit = FALSE; + SCSIBase->scsi_NoMulti = FALSE; + SCSIBase->scsi_NoDMA = FALSE; + SCSIBase->scsi_Poll = FALSE; + + /* + * start initialization: + * obtain kernel parameters + */ + BootLoaderBase = OpenResource("bootloader.resource"); + D(bug("[SCSI--] %s: BootloaderBase = %p\n", __PRETTY_FUNCTION__, BootLoaderBase)); + if (BootLoaderBase != NULL) + { + struct List *list; + struct Node *node; + + list = (struct List *)GetBootInfo(BL_Args); + if (list) + { + ForeachNode(list, node) + { + if (strncmp(node->ln_Name, "SCSI=", 4) == 0) + { + const char *CmdLine = &node->ln_Name[4]; + + if (strstr(CmdLine, "disable")) + { + D(bug("[SCSI ] %s: Disabling SCSI support\n", __PRETTY_FUNCTION__)); + return FALSE; + } + if (strstr(CmdLine, "32bit")) + { + D(bug("[SCSI ] %s: Using 32-bit IO transfers\n", __PRETTY_FUNCTION__)); + SCSIBase->scsi_32bit = TRUE; + } + if (strstr(CmdLine, "nomulti")) + { + D(bug("[SCSI ] %s: Disabled multisector transfers\n", __PRETTY_FUNCTION__)); + SCSIBase->scsi_NoMulti = TRUE; + } + if (strstr(CmdLine, "nodma")) + { + D(bug("[SCSI ] %s: Disabled DMA transfers\n", __PRETTY_FUNCTION__)); + SCSIBase->scsi_NoDMA = TRUE; + } + if (strstr(CmdLine, "poll")) + { + D(bug("[SCSI ] %s: Using polling to detect end of busy state\n", __PRETTY_FUNCTION__)); + SCSIBase->scsi_Poll = TRUE; + } + } + } + } + } + + SCSIBase->scsi_UtilityBase = OpenLibrary("utility.library", 36); + if (!SCSIBase->scsi_UtilityBase) + { + bug("[SCSI--] %s: Failed to open utility.library v36\n", __PRETTY_FUNCTION__); + return FALSE; + } + /* + * I've decided to use memory pools again. Alloc everything needed from + * a pool, so that we avoid memory fragmentation. + */ + SCSIBase->scsi_MemPool = CreatePool(MEMF_CLEAR | MEMF_PUBLIC | MEMF_SEM_PROTECTED , 8192, 4096); + if (SCSIBase->scsi_MemPool == NULL) + { + bug("[SCSI--] %s: Failed to Allocate MemPool!\n", __PRETTY_FUNCTION__); + return FALSE; + } + + D(bug("[SCSI--] %s: MemPool @ %p\n", __PRETTY_FUNCTION__, SCSIBase->scsi_MemPool)); + +#if defined(__OOP_NOATTRBASES__) + if (OOP_ObtainAttrBasesArray(&SCSIBase->unitAttrBase, attrBaseIDs)) + { + bug("[SCSI--] %s: Failed to obtain AttrBases!\n", __PRETTY_FUNCTION__); + return FALSE; + } + D( + bug("[SCSI--] %s: HiddBusAB %x @ 0x%p\n", __func__, HiddBusAB, &HiddBusAB); + bug("[SCSI--] %s: HiddSCSIBusAB %x @ 0x%p\n", __func__, HiddSCSIBusAB, &HiddSCSIBusAB); + ) +#endif + +#if defined(__OOP_NOMETHODBASES__) + if (OOP_ObtainMethodBasesArray(&SCSIBase->hwMethodBase, methBaseIDs)) + { + bug("[SCSI--] %s: Failed to obtain MethodBases!\n", __PRETTY_FUNCTION__); + bug("[SCSI--] %s: %s = %p\n", __PRETTY_FUNCTION__, methBaseIDs[0], SCSIBase->hwMethodBase); + bug("[SCSI--] %s: %s = %p\n", __PRETTY_FUNCTION__, methBaseIDs[1], SCSIBase->busMethodBase); + bug("[SCSI--] %s: %s = %p\n", __PRETTY_FUNCTION__, methBaseIDs[2], SCSIBase->HiddSCMethodBase); +#if defined(__OOP_NOATTRBASES__) + OOP_ReleaseAttrBasesArray(&SCSIBase->unitAttrBase, attrBaseIDs); +#endif + return FALSE; + } +#endif + + D(bug("[SCSI ] %s: Base SCSI Hidd Class @ 0x%p\n", __PRETTY_FUNCTION__, SCSIBase->scsiClass)); + + /* Try to setup daemon task looking for diskchanges */ + NEWLIST(&SCSIBase->Daemon_ios); + InitSemaphore(&SCSIBase->DaemonSem); + InitSemaphore(&SCSIBase->DetectionSem); + SCSIBase->daemonParent = FindTask(NULL); + SetSignal(0, SIGF_SINGLE); + + if (!NewCreateTask(TASKTAG_PC, DaemonCode, + TASKTAG_NAME , "SCSI.daemon", + TASKTAG_STACKSIZE , STACK_SIZE, + TASKTAG_TASKMSGPORT, &SCSIBase->DaemonPort, + TASKTAG_PRI , TASK_PRI - 1, /* The daemon should have a little bit lower Pri than handler tasks */ + TASKTAG_ARG1 , SCSIBase, + TAG_DONE)) + { + bug("[SCSI ] %s: Failed to start up daemon!\n", __PRETTY_FUNCTION__); + return FALSE; + } + + /* Wait for handshake */ + Wait(SIGF_SINGLE); + D(bug("[SCSI ] %s: Daemon task set to 0x%p\n", __PRETTY_FUNCTION__, SCSIBase->scsi_Daemon)); + + return SCSIBase->scsi_Daemon ? TRUE : FALSE; +} + +static int scsi_expunge(struct scsiBase *SCSIBase) +{ + struct scsi_Controller *scsiNode, *tmpNode; + ForeachNodeSafe (&SCSIBase->scsi_Controllers, scsiNode, tmpNode) + { + OOP_Object *storageRoot; + /* + * CLID_Hidd_Storage is a singletone, you can get it as many times as + * you want. Here we save up some space in struct scsiBase by + * obtaining storageRoot object only when we need it. This happens + * rarely, so small performance loss is OK here. + */ + storageRoot = OOP_NewObject(NULL, CLID_Hidd_Storage, NULL); + if (!storageRoot) + storageRoot = OOP_NewObject(NULL, CLID_HW_Root, NULL); + if (storageRoot && HW_RemoveDriver(storageRoot, scsiNode->sc_Object)) + { + Remove(&scsiNode->sc_Node); + /* Destroy our singletone */ + OOP_MethodID disp_msg = OOP_GetMethodID(IID_Root, moRoot_Dispose); + + D(bug("[SCSI ] scsi_expunge: Stopping Daemon...\n")); + SCSIBase->daemonParent = FindTask(NULL); + SetSignal(0, SIGF_SINGLE); + Signal(SCSIBase->scsi_Daemon, SIGBREAKF_CTRL_C); + Wait(SIGF_SINGLE); + + D(bug("[SCSI ] scsi_expunge: Done, destroying subystem object\n")); + OOP_DoSuperMethod(scsiNode->sc_Class, scsiNode->sc_Object, &disp_msg); + FreeMem(scsiNode, sizeof(struct scsi_Controller)); + } + else + { + /* Our subsystem is in use, we have some bus driver(s) around. */ + D(bug("[SCSI ] scsi_expunge: SCSI subsystem is in use\n")); + return FALSE; + } + } + +#if defined(__OOP_NOATTRBASES__) + D(bug("[SCSI ] scsi_expunge: Releasing attribute bases\n")); + OOP_ReleaseAttrBasesArray(&SCSIBase->unitAttrBase, attrBaseIDs); +#endif + + if (SCSIBase->scsi_UtilityBase) + CloseLibrary(SCSIBase->scsi_UtilityBase); + + D(bug("[SCSI ] scsi_expunge: Exiting\n")); + return TRUE; +} + +static int open(struct scsiBase *SCSIBase, struct IORequest *iorq, + ULONG unitnum, ULONG flags) +{ + struct scsi_Controller *scsiNode; + struct Hook searchHook = + { + .h_Entry = Hidd_SCSIBus_Open, + .h_Data = iorq + }; + + /* Assume it failed */ + iorq->io_Error = IOERR_OPENFAIL; + iorq->io_Device = &SCSIBase->scsi_Device; + iorq->io_Unit = (APTR)(IPTR)-1; + + /* Try to find the unit */ + ForeachNode (&SCSIBase->scsi_Controllers, scsiNode) + { + HIDD_StorageController_EnumBuses(scsiNode->sc_Object, &searchHook, (APTR)(IPTR)unitnum); + } + D(bug("[SCSI%02d] Open result: %d\n", unitnum, iorq->io_Error)); + + /* If found, io_Error will be reset to zero */ + return iorq->io_Error ? FALSE : TRUE; +} + +/* Close given device */ +static int close +( + LIBBASETYPEPTR LIBBASE, + struct IORequest *iorq +) +{ + struct scsi_Unit *unit = (struct scsi_Unit *)iorq->io_Unit; + + /* First of all make the important fields of struct IORequest invalid! */ + iorq->io_Unit = (struct Unit *)~0; + + /* Decrease use counters of unit */ + unit->su_Unit.unit_OpenCnt--; + + return TRUE; +} + +ADD2INITLIB(SCSI_init, 0) +ADD2EXPUNGELIB(scsi_expunge, 0) +ADD2OPENDEV(open, 0) +ADD2CLOSEDEV(close, 0) diff --git a/rom/devs/scsi/scsi_unitclass.c b/rom/devs/scsi/scsi_unitclass.c new file mode 100644 index 0000000000..22ed246ad9 --- /dev/null +++ b/rom/devs/scsi/scsi_unitclass.c @@ -0,0 +1,284 @@ +/* + Copyright © 2019, The AROS Development Team. All rights reserved. + $Id$ +*/ + +#include + +#include + +/* We want all other bases obtained from our base */ +#define __NOLIBBASE__ + +#include +#include +#include + +#include "scsi.h" + +/***************************************************************************************** + + NAME + --background-- + + LOCATION + IID_Hidd_SCSIUnit + + NOTES + Unit class is private to ata.device. Instances of this class represent + devices connected to IDE buses, and can be used to obtain information + about these devices. + +*****************************************************************************************/ + +/* + * a STUB function for commands not supported by this particular device + */ +static BYTE scsi_STUB(struct scsi_Unit *su) +{ + D(bug("[SCSI%02ld] CALLED STUB FUNCTION (GENERIC). THIS OPERATION IS NOT " + "SUPPORTED BY DEVICE\n", su->su_UnitNum)); + return CDERR_NOCMD; +} + +static BYTE scsi_STUB_IO32(struct scsi_Unit *su, ULONG blk, ULONG len, + APTR buf, ULONG* act) +{ + D(bug("[SCSI%02ld] CALLED STUB FUNCTION (IO32). THIS OPERATION IS NOT " + "SUPPORTED BY DEVICE\n", su->su_UnitNum)); + return CDERR_NOCMD; +} + +static BYTE scsi_STUB_IO64(struct scsi_Unit *su, UQUAD blk, ULONG len, + APTR buf, ULONG* act) +{ + D(bug("[SCSI%02ld] CALLED STUB FUNCTION -- IO ACCESS TO BLOCK %08lx:%08lx, LENGTH %08lx. THIS OPERATION IS NOT SUPPORTED BY DEVICE\n", su->su_UnitNum, (blk >> 32), (blk & 0xffffffff), len)); + return CDERR_NOCMD; +} + +static BYTE scsi_STUB_SCSI(struct scsi_Unit *su, struct SCSICmd* cmd) +{ + D(bug("[SCSI%02ld] CALLED STUB FUNCTION. THIS OPERATION IS NOT SUPPORTED BY DEVICE\n", su->su_UnitNum)); + return CDERR_NOCMD; +} + +OOP_Object *SCSIUnit__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg) +{ + D(bug("[SCSI:Unit] %s()\n", __PRETTY_FUNCTION__)); + + o = (OOP_Object *)OOP_DoSuperMethod(cl, o, &msg->mID); + if (o) + { + struct scsiBase *SCSIBase = cl->UserData; + struct scsi_Unit *unit = OOP_INST_DATA(cl, o); + + D(bug("[SCSI:Unit] %s: instance @ 0x%p\n", __PRETTY_FUNCTION__, o)); + + unit->su_Drive = AllocPooled(SCSIBase->scsi_MemPool, sizeof(struct DriveIdent)); + if (!unit->su_Drive) + { + OOP_MethodID disp_msg = msg->mID - moRoot_New + moRoot_Dispose; + + OOP_DoSuperMethod(cl, o, &disp_msg); + return NULL; + } + + unit->su_SectorShift = 9; /* this really has to be set here. */ + + NEWLIST(&unit->su_SoftList); + + /* + * since the stack is always handled by caller + * it's safe to stub all calls with one function + */ + unit->su_Read32 = scsi_STUB_IO32; + unit->su_Read64 = scsi_STUB_IO64; + unit->su_Write32 = scsi_STUB_IO32; + unit->su_Write64 = scsi_STUB_IO64; + unit->su_Eject = scsi_STUB; + unit->su_DirectSCSI = scsi_STUB_SCSI; + unit->su_Identify = scsi_STUB; + } + return o; +} + +void SCSIUnit__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg) +{ + struct scsiBase *SCSIBase = cl->UserData; + struct scsi_Unit *unit = OOP_INST_DATA(cl, o); + + D(bug("[SCSI:Unit] %s()\n", __PRETTY_FUNCTION__)); + + FreePooled(SCSIBase->scsi_MemPool, unit->su_Drive, sizeof(struct DriveIdent)); + OOP_DoSuperMethod(cl, o, msg); +} + +/***************************************************************************************** + + NAME + aoHidd_SCSIUnit_XferModes + + SYNOPSIS + [..G], ULONG + + LOCATION + IID_Hidd_SCSIUnit + + FUNCTION + Tells which transfer modes are supported by this device. The returned value + is a bitwise combination of the following flags (see include/hidd/ata.h): + + AF_XFER_PIO(x) - PIO mode number x (0 - 4) + AF_XFER_MDMA(x) - multiword DMA mode number x (0 - 2) + AF_XFER_UDMA(x) - Ultra DMA mode number x (0 - 6) + AF_XFER_48BIT - LBA48 block addressing + AF_XFER_RWMILTI - Multisector PIO + AF_XFER_PACKET - ATAPI + AF_XFER_LBA - LBA28 block addressing + AF_XFER_PIO32 - 32-bit PIO + + NOTES + + EXAMPLE + + BUGS + 32-bit PIO is actually controller's property and not drive's property. + Because of this AF_XFER_PIO32 flag can never be returned by this attribute. + Nevertheless, it can be returned by aoHidd_SCSIUnit_ConfiguredModes + attribute. + + SEE ALSO + aoHidd_SCSIUnit_ConfiguredModes + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIUnit_MultiSector + + SYNOPSIS + [..G], UBYTE + + LOCATION + IID_Hidd_SCSIUnit + + FUNCTION + Tells maximum allowed number of sectors for multisector transfer. + + NOTES + + EXAMPLE + + BUGS + + SEE ALSO + + INTERNALS + +*****************************************************************************************/ +/***************************************************************************************** + + NAME + aoHidd_SCSIUnit_ConfiguredModes + + SYNOPSIS + [..G], ULONG + + LOCATION + IID_Hidd_SCSIUnit + + FUNCTION + Tells which transfer modes are currently configured for use with the drive. + The returned value is a bitmask of the same flags as for + aoHidd_SCSIUnit_XferModes attribute. + + NOTES + + EXAMPLE + + BUGS + Currently ata.device does not distinguish between PIO modes and does not + set any bit for them. Absence of DMA mode flags automatically means that + PIO mode is used. + + SEE ALSO + aoHidd_SCSIUnit_XferModes + + INTERNALS + +*****************************************************************************************/ + +void SCSIUnit__Root__Get(OOP_Class *cl, OOP_Object *o, struct pRoot_Get *msg) +{ + struct scsiBase *SCSIBase = cl->UserData; + struct scsi_Unit *unit = OOP_INST_DATA(cl, o); + ULONG idx; + + Hidd_StorageUnit_Switch (msg->attrID, idx) + { + case aoHidd_StorageUnit_Device: + *msg->storage = (IPTR)"scsi.device"; + return; + + case aoHidd_StorageUnit_Number: + *msg->storage = unit->su_UnitNum; + return; + + case aoHidd_StorageUnit_Type: + { + UBYTE u = unit->su_UnitNum & 1; + switch (unit->su_Bus->sb_Dev[u]) + { + case DEV_SATA: + case DEV_ATA: + *msg->storage = vHidd_StorageUnit_Type_FixedDisk; + break; + + case DEV_SATAPI: + case DEV_ATAPI: + *msg->storage = vHidd_StorageUnit_Type_OpticalDisc; + break; + + default: + *msg->storage = vHidd_StorageUnit_Type_Unknown; + break; + } + return; + } + + case aoHidd_StorageUnit_Model: + *msg->storage = (IPTR)unit->su_Model; + return; + + case aoHidd_StorageUnit_Revision: + *msg->storage = (IPTR)unit->su_FirmwareRev; + return; + + case aoHidd_StorageUnit_Serial: + *msg->storage = (IPTR)unit->su_SerialNumber; + return; + + case aoHidd_StorageUnit_Removable: + *msg->storage = (unit->su_Flags & AF_Removable) ? TRUE : FALSE; + return; + } + + Hidd_SCSIUnit_Switch (msg->attrID, idx) + { + case aoHidd_SCSIUnit_XferModes: + *msg->storage = unit->su_XferModes; + return; + + case aoHidd_SCSIUnit_MultiSector: + *msg->storage = unit->su_Drive->id_RWMultipleSize & 0xFF; + return; + + case aoHidd_SCSIUnit_ConfiguredModes: + *msg->storage = unit->su_UseModes; + return; + } + + OOP_DoSuperMethod(cl, o, &msg->mID); +} diff --git a/rom/devs/scsi/timer.c b/rom/devs/scsi/timer.c new file mode 100644 index 0000000000..01f73e91c3 --- /dev/null +++ b/rom/devs/scsi/timer.c @@ -0,0 +1,72 @@ +/* + Copyright © 2009-2013, The AROS Development Team. All rights reserved + $Id: timer.c 55802 2019-03-08 21:47:59Z wawa $ + + Desc: + Lang: English +*/ + +#include + +#include + +/* We want all other bases obtained from our base */ +#define __NOLIBBASE__ + +#include + +#include +#include +#include + +#include "timer.h" +#include "scsi.h" + +struct IORequest *scsi_OpenTimer(struct scsiBase *base) +{ + struct MsgPort *p = CreateMsgPort(); + if (NULL != p) + { + struct IORequest *io = CreateIORequest(p, sizeof(struct timerequest)); + + if (NULL != io) + { + /* + * ok. ECLOCK does not have too great resolution, either. + * we will have to sacrifice our performance a little bit, meaning, the 400ns will turn into (worst case) 2us. + * hopefully we won't have to call that TOO often... + */ + if (0 == OpenDevice("timer.device", UNIT_MICROHZ, io, 0)) + { + return io; + } + else + { + bug("[SCSI ] Failed to open timer.device, unit MICROHZ\n"); + } + DeleteIORequest(io); + } + else + { + bug("[SCSI ] Failed to create timerequest\n"); + } + DeleteMsgPort(p); + } + else + { + bug("[SCSI ] Failed to create timer port\n"); + } + + return NULL; +} + +void scsi_CloseTimer(struct IORequest *tmr) +{ + if (NULL != tmr) + { + struct MsgPort *p = tmr->io_Message.mn_ReplyPort; + CloseDevice(tmr); + DeleteIORequest(tmr); + DeleteMsgPort(p); + } +} diff --git a/rom/devs/scsi/timer.h b/rom/devs/scsi/timer.h new file mode 100644 index 0000000000..fe6de21087 --- /dev/null +++ b/rom/devs/scsi/timer.h @@ -0,0 +1,57 @@ +/* + Copyright © 2009-2013, The AROS Development Team. All rights reserved + $Id: timer.h 46720 2013-02-28 18:48:18Z sonic $ + + Desc: + Lang: English +*/ + +struct scsiBase; + +/* + * scsi_OpenTimer + * create timerequest to manage timed operations + * result + * timerequest to be used with any of the calls below + * note + * only one task can use given timerequest + */ +struct IORequest *scsi_OpenTimer(struct scsiBase *base); + +/* + * scsi_CloseTimer + * dispose timerequest; most likely never used ;) + * params + * tmr - obtained via scsi_OpenTimer() + * result + * none + */ +void scsi_CloseTimer(struct IORequest *tmr); + +/* + * scsi_Wait + * wait for a period of time or a signal + * params + * tmr - obtained via scsi_OpenTimer() + * secs - number of seconds to wait + * micro - number of microseconds to wait + * sigs - additionally - signal to wait for + * result + * ULONG signals - if caught before timeout + */ +ULONG scsi_WaitTO(struct IORequest* tmr, ULONG secs, ULONG micro, ULONG sigs); + +BOOL scsi_Calibrate(struct IORequest* tmr, struct scsiBase *base); + +/* + * scsi_WaitNano + * waits for (pretty much) specified amount of time. benchmarked. + * params + * ns - amount of nanoseconds; + * result + * none + * note + * rounds up ns to nearest multiple of 100 + */ +void scsi_WaitNano(ULONG ns, struct scsiBase *base); + diff --git a/rom/devs/scsi/waitnano.c b/rom/devs/scsi/waitnano.c new file mode 100644 index 0000000000..53d4ef1f6e --- /dev/null +++ b/rom/devs/scsi/waitnano.c @@ -0,0 +1,82 @@ +/* + Copyright © 2013, The AROS Development Team. All rights reserved + $Id: waitnano.c 55802 2019-03-08 21:47:59Z wawa $ +*/ + +#include + +#include + +/* We want all other bases obtained from our base */ +#define __NOLIBBASE__ + +#include + +#include +#include +#include + +#include "timer.h" +#include "scsi.h" + +BOOL scsi_Calibrate(struct IORequest* tmr, struct scsiBase *base) +{ + register ULONG x; + register ULONG scale = 0x8000; // min iterations... + volatile register ULONG t = 1; + struct timeval t1, t2; + struct Device *TimerBase = tmr->io_Device; + + D(bug("[SCSI ] Calibration started\n")); + + while (scale <= 0x80000000) + { + Forbid(); + GetUpTime(&t1); + for (x = 1; x < scale; x++) + t = (((t + x) * t) - x) / x; // add, mul, sub, div, trivial benchmark. + + GetUpTime(&t2); + Permit(); + SubTime(&t2, &t1); + + // ok, it's going to be totally insane, if secs > 1. + if (t2.tv_secs != 0) + { + bug("[SCSI ] micro wait useless.\n"); + return FALSE; + } + + /* + * we expect at least 10000 times longer period, which should be 'achievable' + * unlikely we will cross the magic boundary here of 4 billion instructions in 10 millisecond (yielding 400'000MIPS?) + * on the other side, if we go as low as 1, then 4 iterations of add/sub/mul/div is perfectly fine yielding a bit more than 400ns... + */ + + if (t2.tv_micro >= 10000) + break; + scale <<= 1; + } + + D(bug("[SCSI ] Executed %ld ops in %ldus\n", scale, t2.tv_micro)); + + // always round up to the next value.. so 30.9 -> 31, 5.1 -> 6, etc + x = (x + t2.tv_micro - 1) / t2.tv_micro; + x = (x+9) / 10; + + bug("[SCSI ] Approximate number of iterations per 100 nanoseconds: %ld\n", x); + base->scsi_ItersPer100ns = x; + return TRUE; +} + +void scsi_WaitNano(register ULONG ns, struct scsiBase *base) +{ + volatile register ULONG t = 1; + ns = (ns + 99) / 100; + ns *= base->scsi_ItersPer100ns; + while (ns > 0) + { + t = (((t + ns) * t) - ns) / ns; // add, mul, sub, div, trivial benchmark. + --ns; + } +} diff --git a/rom/devs/scsi/waitto.c b/rom/devs/scsi/waitto.c new file mode 100644 index 0000000000..d4a16e6d65 --- /dev/null +++ b/rom/devs/scsi/waitto.c @@ -0,0 +1,51 @@ +/* + Copyright © 2013-2014, The AROS Development Team. All rights reserved + $Id: waitto.c 55802 2019-03-08 21:47:59Z wawa $ +*/ + +#include + +#include + +/* We want all other bases obtained from our base */ +#define __NOLIBBASE__ + +#include + +#include +#include +#include + +#include "timer.h" +#include "scsi.h" + +/* Waits for a signal or a timeout */ +ULONG scsi_WaitTO(struct IORequest* tmr, ULONG secs, ULONG micro, ULONG sigs) +{ + ULONG sig = 1 << tmr->io_Message.mn_ReplyPort->mp_SigBit; + + D(struct Node *t = (struct Node *)FindTask(NULL)); + D(bug("[SCSI ] Timed wait %lds %ldu (task='%s')\n", secs, micro, + t->ln_Name)); + + tmr->io_Command = TR_ADDREQUEST; + ((struct timerequest*)tmr)->tr_time.tv_secs = secs; + ((struct timerequest*)tmr)->tr_time.tv_micro = micro; + + SendIO(tmr); + D(bug("[SCSI ] Preset signals: %lx ('%s')\n", SetSignal(0, 0), t->ln_Name)); + D(bug("[SCSI ] Signals requested: %lx ('%s')\n", sigs, t->ln_Name)); + D(bug("[SCSI ] Timer signal: %lx ('%s')\n", sig, t->ln_Name)); + sigs = Wait(sigs | sig); + D(bug("[SCSI ] Signals received: %lx ('%s')\n", sigs, t->ln_Name)); + if (0 == (sigs & sig)) + { + if (!CheckIO(tmr)) + AbortIO(tmr); + } + WaitIO(tmr); + + SetSignal(0, sig); + + return sigs & ~sig; +} -- 2.11.4.GIT