/* @(#)scsi-atari.c 1.3 09/07/13 Copyright 1997,2000,2009 J. Schilling */ #ifndef lint static char __sccsid[] = "@(#)scsi-atari.c 1.3 09/07/13 Copyright 1997,2000,2009 J. Schilling"; #endif /* * Interface for Atari generic SCSI implementation. * * Copyright (c) 1997, 2000, 2009 J. Schilling * Atari systems support code written by Yvan Doyeux * * * SCSIDRV driver is needed to enable libscg on Atari computers. * I need to pass through assembler to avoid using of -mshort GCC option. * Actually SCSIDRV requires real 16-bit word for several function arguments. * * If you are using HDDRIVER, it already includes the SCSIDRV programming interface, * otherwise you must run SCSIDRV.PRG before. * * The following bus-numbers are reserved for this: * 0 (ACSI) * 1 (Standard SCSI: Falcon, TT, Medusa, MagicMac...) * 2 (IDE in Atari-compatibles) * * For further information, see packages SCSIDRV.ZIP and CBHD502.ZIP. * * Warning: you may change this source, but if you do that * you need to change the _scg_version and _scg_auth* string below. * You may not return "schily" for an SCG_AUTHOR request anymore. * Choose your name instead of "schily" and make clear that the version * string is related to a modified source. */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * * The following exceptions apply: * CDDL ยง3.6 needs to be replaced by: "You may create a Larger Work by * combining Covered Software with other code if all other code is governed by * the terms of a license that is OSI approved (see www.opensource.org) and * you may distribute the Larger Work as a single product. In such a case, * You must make sure the requirements of this License are fulfilled for * the Covered Software." * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include #include #include #include #include #define cdecl #define BOOLEAN long #define BYTE char /* Signed byte */ #define UBYTE unsigned char /* Unsigned byte */ #define WORD short /* Signed word (16 bits) */ #define UWORD unsigned short /* Unsigned word */ #define SCSIRevision 0x0101 #define MAXBUSNO 31 #define MAX_SCSIDRV_HANDLE 32 #define MAX_SCG 3 /* Max # of SCSI controllers */ #define MAX_TGT 8 #define MAX_LUN 1 /* * SCSIDRV return values */ #define CHECKCONDITION 2L #define NOSCSIERROR 0L #define SELECTERROR -1L #define STATUSERROR -2L #define PHASEERROR -3L #define BSYERROR -4L #define BUSERROR -5L #define TRANSERROR -6L #define FREEERROR -7L #define TIMEOUTERROR -8L #define DATATOOLONG -9L #define LINKERROR -10L #define TIMEOUTARBIT -11L #define PENDINGERROR -12L #define PARITYERROR -13L LOCAL char _scg_trans_version[] = "scsi-atari.c-1.3"; /* The version for this transport */ LOCAL char _scg_auth[] = "Yvan Doyeux"; typedef struct { unsigned long hi; unsigned long lo; } DLONG; typedef struct { unsigned long BusIds; BYTE resrvd[28]; } tPrivate; typedef WORD *tHandle; typedef struct { tHandle Handle; BYTE *Cmd; UWORD CmdLen; void *Buffer; unsigned long TransferLen; BYTE *SenseBuffer; unsigned long Timeout; UWORD Flags; #define Disconnect 0x10 } tSCSICmd; typedef tSCSICmd *tpSCSICmd; typedef struct { tPrivate Private; char BusName[20]; UWORD BusNo; UWORD Features; #define cArbit 0x01 #define cAllCmds 0x02 #define cTargCtrl 0x04 #define cTarget 0x08 #define cCanDisconnect 0x10 #define cScatterGather 0x20 unsigned long MaxLen; } tBusInfo; typedef struct { BYTE Private[32]; DLONG SCSIId; } tDevInfo; typedef struct ttargethandler { struct ttargethandler *next; BOOLEAN cdecl (*TSel) (WORD bus, UWORD CSB, UWORD CSD); BOOLEAN cdecl (*TCmd) (WORD bus, BYTE *Cmd); UWORD cdecl (*TCmdLen) (WORD bus, UWORD Cmd); void cdecl (*TReset) (UWORD bus); void cdecl (*TEOP) (UWORD bus); void cdecl (*TPErr) (UWORD bus); void cdecl (*TPMism) (UWORD bus); void cdecl (*TBLoss) (UWORD bus); void cdecl (*TUnknownInt) (UWORD bus); } tTargetHandler; typedef tTargetHandler *tpTargetHandler; typedef BYTE tReqData[18]; typedef struct { UWORD Version; /* Routinen als Initiator */ long cdecl (*In) (tpSCSICmd Parms); long cdecl (*Out) (tpSCSICmd Parms); long cdecl (*InquireSCSI) (WORD what, tBusInfo *Info); #define cInqFirst 0 #define cInqNext 1 long cdecl (*InquireBus) (WORD what, WORD BusNo, tDevInfo *Dev); long cdecl (*CheckDev) (WORD BusNo, const DLONG *SCSIId, char *Name, UWORD *Features); long cdecl (*RescanBus) (WORD BusNo); long cdecl (*Open) (WORD BusNo, const DLONG *SCSIId, unsigned long *MaxLen); long cdecl (*Close) (tHandle handle); long cdecl (*Error) (tHandle handle, WORD rwflag, WORD ErrNo); #define cErrRead 0 #define cErrWrite 1 #define cErrMediach 0 #define cErrReset 1 long cdecl (*Install) (WORD bus, tpTargetHandler Handler); long cdecl (*Deinstall) (WORD bus, tpTargetHandler Handler); long cdecl (*GetCmd) (WORD bus, BYTE *Cmd); long cdecl (*SendData) (WORD bus, BYTE *Buffer, unsigned long Len); long cdecl (*GetData) (WORD bus, void *Buffer, unsigned long Len); long cdecl (*SendStatus) (WORD bus, UWORD Status); long cdecl (*SendMsg) (WORD bus, UWORD Msg); long cdecl (*GetMsg) (WORD bus, UWORD *Msg); tReqData *ReqData; } tScsiCall; typedef tScsiCall *tpScsiCall; LOCAL int num_handle_opened = 0; LOCAL tHandle tab_handle_opened[MAX_SCSIDRV_HANDLE]; LOCAL SCSI *scgp_local; tpScsiCall scsicall; struct bus_struct { tBusInfo bus; tDevInfo *dev[MAX_TGT]; }; struct scg_local { struct bus_struct *atari_bus[MAX_SCG]; }; #define scglocal(p) ((struct scg_local *)((p)->local)) /* * Return version information for the low level SCSI transport code. * This has been introduced to make it easier to trace down problems * in applications. */ LOCAL char * scgo_version(scgp, what) SCSI *scgp; int what; { if (scgp != (SCSI *)0) { switch (what) { case SCG_VERSION: return (_scg_trans_version); /* * If you changed this source, you are not allowed to * return "schily" for the SCG_AUTHOR request. */ case SCG_AUTHOR: return (_scg_auth); case SCG_SCCS_ID: return (__sccsid); } } return ((char *)0); } LOCAL int scgo_help(scgp, f) SCSI *scgp; FILE *f; { __scg_help(f, "SCSIDRV Interface", "Atari generic SCSI implementation", "", "bus,target,lun", "1,2,0", TRUE, FALSE); printf("\nSCSIDRV driver is needed to enable libscg on Atari computers.\n"); printf("If you are using HDDRIVER, it already includes the SCSIDRV\n"); printf("programming interface otherwise you must run SCSIDRV.PRG before.\n"); printf("The following bus-numbers are reserved for this:\n"); printf("0 (ACSI)\n"); printf("1 (Standard SCSI: Falcon, TT, Medusa, MagicMac...)\n"); printf("2 (IDE in Atari-compatibles)\n"); printf("For further information, see packages SCSIDRV.ZIP and CBHD502.ZIP.\n"); return (0); } LOCAL void scsidrv_close() { long rc; rc = (long)scsicall->Close; { register long retvalue __asm__("d0"); /* BEGIN CSTYLED */ __asm__ volatile (" movl %1,sp@-; jbsr (%2); lea sp@(4),sp " : "=r"(retvalue) /* outputs */ : "r"((tHandle) scgp_local->fd),"a"(rc) /* inputs */ : "d0", "d1", "d2", "a0", "a1", "a2" /* clobbered regs */ AND_MEMORY); /* END CSTYLED */ } num_handle_opened = num_handle_opened - 1; if ((num_handle_opened-1) >= 0) scgp_local->fd = (int)tab_handle_opened[num_handle_opened-1]; else scgp_local->fd = 0; } LOCAL void atari_free_scsi_all() { int i; int j; for (j = 0; j < MAX_SCG; j++) { if (scglocal(scgp_local)->atari_bus[j] != (struct bus_struct *)-1) { for (i = 0; i < MAX_TGT; i++) { if (scglocal(scgp_local)->atari_bus[j]->dev[i] != (tDevInfo *)-1) { free(scglocal(scgp_local)->atari_bus[j]->dev[i]); } } free(scglocal(scgp_local)->atari_bus[j]); } } if (scgp_local->local) { free(scgp_local->local); } scgo_freebuf(scgp_local); } LOCAL int scgo_open(scgp, device) SCSI *scgp; char *device; { /* SCSIDRV present ? */ BOOL scsidrv = FALSE; BOOL nopen = FALSE; void *oldstack; long rc_bus; int i; int j; tBusInfo Bus; WORD Inq_bus = cInqFirst; long cookie_value; scsicall = NULL; scgp_local = scgp; if (Getcookie(0x53435349L, &cookie_value) == 0) { scsicall = (tpScsiCall)cookie_value; printf("SCSIDRV found on your ATARI!\n"); scsidrv = TRUE; } else { printf("I can't find SCSIDRV on your ATARI...\n"); scsidrv = FALSE; nopen = FALSE; } if (scsidrv == TRUE) { if (scgp->local == NULL) { scgp->local = malloc(sizeof (struct scg_local)); if (scgp->local == NULL) return (0); for (j = 0; j < MAX_SCG; j++) { scglocal(scgp)->atari_bus[j] = (struct bus_struct *)-1; } } oldstack = (void *)Super(NULL); rc_bus = (long)scsicall->InquireSCSI; /* busses available */ /* * InquireSCSI */ { register long ret_bus_value __asm__("d0") = 0; /* Return of InquireSCSI */ long ret_bus = 0; while (ret_bus == 0) { /* BEGIN CSTYLED */ __asm__ volatile (" movl %1,sp@-; /* Pointer of bus structure (32-bit long)*/ movw %3,sp@-; /* Inq_bus argument (16-bit WORD) */ jbsr (%2); /* Call to InquireSCSI SCSIDRV function */ lea sp@(6),sp /* Fix the stack */ " : "=r"(ret_bus_value) /* outputs */ : "r"(&Bus),"a"(rc_bus), "r"(Inq_bus) /* inputs */ : "d0", "d1", "d2", "a0", "a1", "a2" /* clobbered regs */ AND_MEMORY); /* END CSTYLED */ ret_bus = ret_bus_value; if (ret_bus == 0) { scglocal(scgp)->atari_bus[Bus.BusNo] = malloc(sizeof (struct bus_struct)); memcpy(&scglocal(scgp)->atari_bus[Bus.BusNo]->bus, &Bus, sizeof (tBusInfo)); for (i = 0; i < MAX_TGT; i++) { scglocal(scgp)->atari_bus[Bus.BusNo]->dev[i] = (tDevInfo *)-1; } { long rc_dev; WORD Inq_dev = cInqFirst; register long ret_dev_value __asm__("d0") = 0; long ret_dev = 0; tDevInfo Dev; rc_dev = (long)scsicall->InquireBus; /* we inquiry for devices in bus Bus.BusNo */ while (ret_dev == 0) { /* BEGIN CSTYLED */ __asm__ volatile (" movl %2,sp@-; movw %1,sp@-; movw %4,sp@-; jbsr (%3); lea sp@(8),sp " : "=r"(ret_dev_value) /* outputs */ : "r"(Bus.BusNo),"r"(&Dev),"a"(rc_dev), "r"(Inq_dev) /* inputs */ : "d0", "d1", "d2", "a0", "a1", "a2" /* clobbered regs */ AND_MEMORY); /* END CSTYLED */ ret_dev = ret_dev_value; if (ret_dev == 0) { scglocal(scgp)->atari_bus[Bus.BusNo]->dev[(int)Dev.SCSIId.lo] = malloc(sizeof (tDevInfo)); memcpy(scglocal(scgp)->atari_bus[Bus.BusNo]->dev[(int)Dev.SCSIId.lo], &Dev, sizeof (tDevInfo)); } Inq_dev = cInqNext; } } } Inq_bus = cInqNext; } } Super(oldstack); atexit(scsidrv_close); atexit(atari_free_scsi_all); nopen = TRUE; } return (nopen); } LOCAL int scgo_close(SCSI * scgp) { scsidrv_close(); atari_free_scsi_all(); return (0); } LOCAL long scgo_maxdma(scgp, amt) SCSI *scgp; long amt; { /* return (63*1024); */ return (256*1024); } LOCAL void * scgo_getbuf(scgp, amt) SCSI *scgp; long amt; { if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "scgo_getbuf: %ld bytes\n", amt); } scgp->bufbase = malloc((size_t)(amt)); return (scgp->bufbase); } LOCAL void scgo_freebuf(scgp) SCSI *scgp; { if (scgp->bufbase) free(scgp->bufbase); scgp->bufbase = NULL; } LOCAL int scgo_numbus(scgp) SCSI *scgp; { return (MAX_SCG); } LOCAL BOOL scgo_havebus(scgp, busno) SCSI *scgp; int busno; { return (TRUE); } LOCAL int scgo_fileno(scgp, busno, tgt, tlun) SCSI *scgp; int busno; int tgt; int tlun; { long rc; unsigned long MaxLen; int fd = 0; tBusInfo Bus; void *oldstack; if (scgp_local->fd > 0) { /* handle exists ? */ scsidrv_close(); } if (busno < 0 || busno >= MAX_SCG || tgt < 0 || tgt >= MAX_TGT || tlun < 0 || tlun >= MAX_LUN) { errno = EINVAL; return (-1); } if (scglocal(scgp)->atari_bus[busno] == (struct bus_struct *)-1) { errno = EINVAL; return (-1); } if ((struct tDevInfo *) scglocal(scgp)->atari_bus[busno]->dev[tgt] == (struct tDevInfo *)-1) { errno = EINVAL; return (-1); } oldstack = (void *)Super(NULL); rc = (long)scsicall->Open; { register long retvalue __asm__("d0") = 0; /* BEGIN CSTYLED */ __asm__ volatile (" movl %3,sp@-; movl %2,sp@-; movw %1,sp@-; jbsr (%4); lea sp@(10),sp " : "=r"(retvalue) /* outputs */ : "r"(scglocal(scgp)->atari_bus[busno]->bus.BusNo), "r"(&scglocal(scgp)->atari_bus[busno]->dev[tgt]->SCSIId),"r"(&MaxLen),"a"(rc) /* inputs */ : "d0", "d1", "d2", "a0", "a1", "a2" /* clobbered regs */ AND_MEMORY); /* END CSTYLED */ fd = (int)retvalue; } Super(oldstack); if (fd > 0) { tab_handle_opened[num_handle_opened] = (tHandle)fd; num_handle_opened = num_handle_opened + 1; return (fd); } else { return (-1); } } LOCAL int scgo_initiator_id(scgp) SCSI *scgp; { return (-1); } LOCAL int scgo_isatapi(scgp) SCSI *scgp; { return (FALSE); } LOCAL int scgo_reset(scgp, what) SCSI *scgp; int what; { /* XXX synchronous reset command - is this wise? */ errno = EINVAL; return (-1); } LOCAL int scgo_send(scgp) SCSI *scgp; { long rc; void *oldstack; tSCSICmd cmd; tBusInfo Bus; tDevInfo Dev; unsigned long MaxLen; long ret = 0; BYTE reqbuff[18]; scgp->scmd->ux_errno = 0; if (scgp->fd <= 0) { scgp->scmd->error = SCG_FATAL; return (0); } oldstack = (void *)Super(NULL); cmd.Handle = (tHandle) scgp->fd; cmd.Cmd = malloc(scgp->scmd->cdb_len); movebytes(&scgp->scmd->cdb, cmd.Cmd, scgp->scmd->cdb_len); cmd.CmdLen = /* (UWORD) */ scgp->scmd->cdb_len; cmd.Buffer = scgp->scmd->addr; cmd.TransferLen = scgp->scmd->size; cmd.SenseBuffer = scgp->scmd->u_sense.cmd_sense; fillbytes(cmd.SenseBuffer, sizeof (reqbuff), '\0'); cmd.Timeout = scgp->scmd->timeout * 200; cmd.Flags = scgp->scmd->flags & SCG_RECV_DATA; scgp->scmd->u_scb.cmd_scb[0] = 0; if ((scgp->scmd->flags & SCG_RECV_DATA)) { rc = (long)scsicall->In; { register long retvalue __asm__("d0"); /* BEGIN CSTYLED */ __asm__ volatile (" movl %1,sp@-; jbsr (%2); lea sp@(4),sp " : "=r"(retvalue) /* outputs */ : "r"(&cmd),"a"(rc) /* inputs */ : "d0", "d1", "d2", "a0", "a1", "a2" /* clobbered regs */ AND_MEMORY); /* END CSTYLED */ ret = retvalue; } } else if (scgp->scmd->size > 0) { rc = (long)scsicall->Out; { register long retvalue __asm__("d0"); /* BEGIN CSTYLED */ __asm__ volatile (" movl %1,sp@-; jbsr (%2); lea sp@(4),sp " : "=r"(retvalue) /* outputs */ : "r"(&cmd),"a"(rc) /* inputs */ : "d0", "d1", "d2", "a0", "a1", "a2" /* clobbered regs */ AND_MEMORY); /* END CSTYLED */ ret = retvalue; } } else { rc = (long)scsicall->Out; { register long retvalue __asm__("d0"); /* BEGIN CSTYLED */ __asm__ volatile (" movl %1,sp@-; jbsr (%2); lea sp@(4),sp " : "=r"(retvalue) /* outputs */ : "r"(&cmd),"a"(rc) /* inputs */ : "d0", "d1", "d2", "a0", "a1", "a2" /* clobbered regs */ AND_MEMORY); /* BEGIN CSTYLED */ ret = retvalue; } } Super(oldstack); scgp->scmd->resid = 0; scgp->scmd->sense_count = sizeof (reqbuff); /* 18 */ movebytes(cmd.SenseBuffer, &scgp->scmd->u_sense.cmd_sense, sizeof (reqbuff)); free(cmd.Cmd); scgp->scmd->u_scb.cmd_scb[0] = ret; switch (ret) { case CHECKCONDITION: scgp->scmd->ux_errno = geterrno(); scgp->scmd->error = SCG_NO_ERROR; break; case TIMEOUTERROR: scgp->scmd->ux_errno = geterrno(); scgp->scmd->error = SCG_TIMEOUT; break; default: if (ret < 0) { scgp->scmd->ux_errno = geterrno(); scgp->scmd->error = SCG_FATAL; } else scgp->scmd->error = SCG_NO_ERROR; break; } return (ret); }