Annotation of hatari/src/hdc.c, revision 1.1.1.20

1.1       root        1: /*
1.1.1.3   root        2:   Hatari - hdc.c
1.1       root        3: 
1.1.1.15  root        4:   This file is distributed under the GNU General Public License, version 2
                      5:   or at your option any later version. Read the file gpl.txt for details.
1.1       root        6: 
1.1.1.3   root        7:   Low-level hard drive emulation
1.1       root        8: */
1.1.1.10  root        9: const char HDC_fileid[] = "Hatari hdc.c : " __DATE__ " " __TIME__;
1.1       root       10: 
1.1.1.17  root       11: #include <errno.h>
1.1.1.20! root       12: #include <SDL_endian.h>
1.1.1.17  root       13: 
1.1       root       14: #include "main.h"
1.1.1.3   root       15: #include "configuration.h"
1.1.1.6   root       16: #include "debugui.h"
1.1.1.7   root       17: #include "file.h"
1.1       root       18: #include "fdc.h"
                     19: #include "hdc.h"
1.1.1.14  root       20: #include "ioMem.h"
1.1.1.7   root       21: #include "log.h"
1.1       root       22: #include "memorySnapShot.h"
                     23: #include "mfp.h"
1.1.1.20! root       24: #include "ncr5380.h"
1.1       root       25: #include "stMemory.h"
1.1.1.6   root       26: #include "tos.h"
1.1.1.9   root       27: #include "statusbar.h"
                     28: 
1.1       root       29: 
                     30: /*
                     31:   ACSI emulation: 
                     32:   ACSI commands are six byte-packets sent to the
                     33:   hard drive controller (which is on the HD unit, not in the ST)
                     34: 
                     35:   While the hard drive is busy, DRQ is high, polling the DRQ during
                     36:   operation interrupts the current operation. The DRQ status can
                     37:   be polled non-destructively in GPIP.
1.1.1.7   root       38: 
1.1.1.15  root       39:   (For simplicity, the operation is finished immediately,
1.1       root       40:   this is a potential bug, but I doubt it is significant,
                     41:   we just appear to have a very fast hard drive.)
                     42: 
                     43:   The ACSI command set is a subset of the SCSI standard.
                     44:   (for details, see the X3T9.2 SCSI draft documents
                     45:   from 1985, for an example of writing ACSI commands,
                     46:   see the TOS DMA boot code) 
                     47: */
                     48: 
1.1.1.7   root       49: // #define DISALLOW_HDC_WRITE
                     50: // #define HDC_VERBOSE           /* display operations */
1.1       root       51: 
1.1.1.14  root       52: #define HDC_ReadInt16(a, i) (((unsigned) a[i] << 8) | a[i + 1])
                     53: #define HDC_ReadInt24(a, i) (((unsigned) a[i] << 16) | ((unsigned) a[i + 1] << 8) | a[i + 2])
                     54: #define HDC_ReadInt32(a, i) (((unsigned) a[i] << 24) | ((unsigned) a[i + 1] << 16) | ((unsigned) a[i + 2] << 8) | a[i + 3])
                     55: 
1.1       root       56: /* HDC globals */
1.1.1.17  root       57: static SCSI_CTRLR AcsiBus;
                     58: int nAcsiPartitions = 0;
1.1.1.11  root       59: bool bAcsiEmuOn = false;
1.1.1.7   root       60: 
1.1       root       61: /* Our dummy INQUIRY response data */
1.1.1.7   root       62: static unsigned char inquiry_bytes[] =
1.1       root       63: {
1.1.1.7   root       64:        0,                /* device type 0 = direct access device */
                     65:        0,                /* device type qualifier (nonremovable) */
1.1.1.16  root       66:        1,                /* ACSI/SCSI version */
1.1.1.7   root       67:        0,                /* reserved */
1.1.1.16  root       68:        31,               /* length of the following data */
                     69:        0, 0, 0,          /* Vendor specific data */
                     70:        'H','a','t','a','r','i',' ',' ',    /* Vendor ID */
                     71:        'E','m','u','l','a','t','e','d',    /* Product ID 1 */
                     72:        'H','a','r','d','d','i','s','k',    /* Product ID 2 */
                     73:        '0','1','8','0',                    /* Revision */
1.1       root       74: };
                     75: 
1.1.1.7   root       76: 
1.1       root       77: /*---------------------------------------------------------------------*/
1.1.1.8   root       78: /**
1.1.1.16  root       79:  * Return the LUN (logical unit number) specified in the current
                     80:  * ACSI/SCSI command block.
1.1.1.14  root       81:  */
1.1.1.16  root       82: static unsigned char HDC_GetLUN(SCSI_CTRLR *ctr)
1.1.1.14  root       83: {
1.1.1.16  root       84:        return (ctr->command[1] & 0xE0) >> 5;
1.1.1.14  root       85: }
                     86: 
                     87: /**
1.1.1.16  root       88:  * Return the start sector (logical block address) specified in the
                     89:  * current ACSI/SCSI command block.
1.1.1.8   root       90:  */
1.1.1.16  root       91: static unsigned long HDC_GetLBA(SCSI_CTRLR *ctr)
1.1.1.4   root       92: {
1.1.1.20! root       93:        /* offset = logical block address * physical sector size */
1.1.1.16  root       94:        if (ctr->opcode < 0x20)                         /* Class 0? */
                     95:                return HDC_ReadInt24(ctr->command, 1) & 0x1FFFFF;
                     96:        else
                     97:                return HDC_ReadInt32(ctr->command, 2);  /* Class 1 */
1.1.1.14  root       98: }
1.1       root       99: 
1.1.1.14  root      100: /**
                    101:  * Return the count specified in the current ACSI command block.
                    102:  */
1.1.1.16  root      103: static int HDC_GetCount(SCSI_CTRLR *ctr)
1.1.1.14  root      104: {
1.1.1.16  root      105:        if (ctr->opcode < 0x20)
                    106:                return ctr->command[4];                 /* Class 0 */
                    107:        else
                    108:                return HDC_ReadInt16(ctr->command, 7);  /* Class 1 */
1.1.1.14  root      109: }
1.1.1.7   root      110: 
1.1.1.14  root      111: /**
                    112:  * Return the control byte specified in the current ACSI command block.
                    113:  */
1.1.1.16  root      114: static inline Uint8 HDC_GetControl(SCSI_CTRLR *ctr)
1.1.1.14  root      115: {
1.1.1.16  root      116:        if (ctr->opcode < 0x20)
                    117:                return ctr->command[5];                 /* Class 0 */
                    118:        else
                    119:                return ctr->command[9];                 /* Class 1 */
                    120: }
                    121: 
                    122: /**
1.1.1.17  root      123:  * Get pointer to response buffer, set up size indicator - and allocate
                    124:  * a new buffer if it is not big enough
                    125:  */
                    126: static Uint8 *HDC_PrepRespBuf(SCSI_CTRLR *ctr, int size)
                    127: {
1.1.1.20! root      128:        ctr->data_len = size;
        !           129:        ctr->offset = 0;
1.1.1.17  root      130: 
1.1.1.20! root      131:        if (size > ctr->buffer_size)
1.1.1.17  root      132:        {
1.1.1.20! root      133:                ctr->buffer_size = size;
        !           134:                ctr->buffer = realloc(ctr->buffer, size);
1.1.1.17  root      135:        }
                    136: 
1.1.1.20! root      137:        return ctr->buffer;
1.1.1.17  root      138: }
                    139: 
                    140: /**
1.1.1.16  root      141:  * Get info string for SCSI/ACSI command packets.
                    142:  */
                    143: static inline char *HDC_CmdInfoStr(SCSI_CTRLR *ctr)
                    144: {
                    145:        static char str[80];
                    146: 
1.1.1.20! root      147:        snprintf(str, sizeof(str),
        !           148:                 "%s, t=%i, lun=%i, opc=0x%x, cnt=0x%x, ctrl=0x%x",
        !           149:                 ctr->typestr, ctr->target, HDC_GetLUN(ctr), ctr->opcode,
        !           150:                 HDC_GetCount(ctr), HDC_GetControl(ctr));
1.1.1.16  root      151: 
                    152:        return str;
1.1       root      153: }
                    154: 
1.1.1.7   root      155: 
1.1.1.8   root      156: /**
                    157:  * Seek - move to a sector
                    158:  */
1.1.1.16  root      159: static void HDC_Cmd_Seek(SCSI_CTRLR *ctr)
1.1       root      160: {
1.1.1.16  root      161:        SCSI_DEV *dev = &ctr->devs[ctr->target];
                    162: 
                    163:        dev->nLastBlockAddr = HDC_GetLBA(ctr);
                    164: 
1.1.1.17  root      165:        LOG_TRACE(TRACE_SCSI_CMD, "HDC: SEEK (%s), LBA=%i",
1.1.1.16  root      166:                  HDC_CmdInfoStr(ctr), dev->nLastBlockAddr);
1.1       root      167: 
1.1.1.16  root      168:        if (dev->nLastBlockAddr < dev->hdSize &&
1.1.1.20! root      169:            fseeko(dev->image_file, (off_t)dev->nLastBlockAddr * dev->blockSize, SEEK_SET) == 0)
1.1.1.7   root      170:        {
1.1.1.17  root      171:                LOG_TRACE(TRACE_SCSI_CMD, " -> OK\n");
1.1.1.20! root      172:                ctr->status = HD_STATUS_OK;
1.1.1.16  root      173:                dev->nLastError = HD_REQSENS_OK;
1.1.1.7   root      174:        }
                    175:        else
                    176:        {
1.1.1.17  root      177:                LOG_TRACE(TRACE_SCSI_CMD, " -> ERROR\n");
1.1.1.20! root      178:                ctr->status = HD_STATUS_ERROR;
1.1.1.16  root      179:                dev->nLastError = HD_REQSENS_INVADDR;
1.1.1.7   root      180:        }
1.1       root      181: 
1.1.1.16  root      182:        dev->bSetLastBlockAddr = true;
1.1       root      183: }
                    184: 
1.1.1.7   root      185: 
1.1.1.8   root      186: /**
                    187:  * Inquiry - return some disk information
                    188:  */
1.1.1.16  root      189: static void HDC_Cmd_Inquiry(SCSI_CTRLR *ctr)
1.1.1.7   root      190: {
1.1.1.16  root      191:        SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1.1.17  root      192:        Uint8 *buf;
1.1.1.12  root      193:        int count;
                    194: 
1.1.1.16  root      195:        count = HDC_GetCount(ctr);
1.1.1.12  root      196: 
1.1.1.20! root      197:        LOG_TRACE(TRACE_SCSI_CMD, "HDC: INQUIRY (%s)\n", HDC_CmdInfoStr(ctr));
1.1.1.17  root      198: 
                    199:        buf = HDC_PrepRespBuf(ctr, count);
1.1.1.12  root      200:        if (count > (int)sizeof(inquiry_bytes))
1.1.1.20! root      201:        {
        !           202:                memset(&buf[sizeof(inquiry_bytes)], 0, count - sizeof(inquiry_bytes));
1.1.1.12  root      203:                count = sizeof(inquiry_bytes);
1.1.1.20! root      204:        }
        !           205:        memcpy(buf, inquiry_bytes, count);
1.1.1.12  root      206: 
1.1.1.16  root      207:        /* For unsupported LUNs set the Peripheral Qualifier and the
                    208:         * Peripheral Device Type according to the SCSI standard */
1.1.1.17  root      209:        buf[0] = HDC_GetLUN(ctr) == 0 ? 0 : 0x7F;
1.1.1.16  root      210: 
1.1.1.17  root      211:        buf[4] = count - 5;
1.1.1.12  root      212: 
1.1.1.20! root      213:        ctr->status = HD_STATUS_OK;
1.1.1.7   root      214: 
1.1.1.16  root      215:        dev->nLastError = HD_REQSENS_OK;
                    216:        dev->bSetLastBlockAddr = false;
1.1.1.7   root      217: }
                    218: 
                    219: 
1.1.1.8   root      220: /**
                    221:  * Request sense - return some disk information
                    222:  */
1.1.1.16  root      223: static void HDC_Cmd_RequestSense(SCSI_CTRLR *ctr)
1.1       root      224: {
1.1.1.16  root      225:        SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1.1.7   root      226:        int nRetLen;
1.1.1.17  root      227:        Uint8 *retbuf;
1.1.1.7   root      228: 
1.1.1.16  root      229:        nRetLen = HDC_GetCount(ctr);
1.1.1.7   root      230: 
1.1.1.16  root      231:        LOG_TRACE(TRACE_SCSI_CMD, "HDC: REQUEST SENSE (%s).\n", HDC_CmdInfoStr(ctr));
1.1.1.7   root      232: 
                    233:        if ((nRetLen < 4 && nRetLen != 0) || nRetLen > 22)
                    234:        {
1.1.1.9   root      235:                Log_Printf(LOG_WARN, "HDC: *** Strange REQUEST SENSE ***!\n");
1.1.1.7   root      236:        }
                    237: 
                    238:        /* Limit to sane length */
                    239:        if (nRetLen <= 0)
                    240:        {
                    241:                nRetLen = 4;
                    242:        }
                    243:        else if (nRetLen > 22)
                    244:        {
                    245:                nRetLen = 22;
                    246:        }
                    247: 
1.1.1.17  root      248:        retbuf = HDC_PrepRespBuf(ctr, nRetLen);
1.1.1.7   root      249:        memset(retbuf, 0, nRetLen);
                    250: 
                    251:        if (nRetLen <= 4)
                    252:        {
1.1.1.16  root      253:                retbuf[0] = dev->nLastError;
                    254:                if (dev->bSetLastBlockAddr)
1.1.1.7   root      255:                {
                    256:                        retbuf[0] |= 0x80;
1.1.1.16  root      257:                        retbuf[1] = dev->nLastBlockAddr >> 16;
                    258:                        retbuf[2] = dev->nLastBlockAddr >> 8;
                    259:                        retbuf[3] = dev->nLastBlockAddr;
1.1.1.7   root      260:                }
                    261:        }
                    262:        else
                    263:        {
                    264:                retbuf[0] = 0x70;
1.1.1.16  root      265:                if (dev->bSetLastBlockAddr)
1.1.1.7   root      266:                {
                    267:                        retbuf[0] |= 0x80;
1.1.1.16  root      268:                        retbuf[4] = dev->nLastBlockAddr >> 16;
                    269:                        retbuf[5] = dev->nLastBlockAddr >> 8;
                    270:                        retbuf[6] = dev->nLastBlockAddr;
1.1.1.7   root      271:                }
1.1.1.16  root      272:                switch (dev->nLastError)
1.1.1.7   root      273:                {
                    274:                 case HD_REQSENS_OK:  retbuf[2] = 0; break;
                    275:                 case HD_REQSENS_OPCODE:  retbuf[2] = 5; break;
                    276:                 case HD_REQSENS_INVADDR:  retbuf[2] = 5; break;
                    277:                 case HD_REQSENS_INVARG:  retbuf[2] = 5; break;
1.1.1.16  root      278:                 case HD_REQSENS_INVLUN:  retbuf[2] = 5; break;
1.1.1.7   root      279:                 default: retbuf[2] = 4; break;
                    280:                }
                    281:                retbuf[7] = 14;
1.1.1.16  root      282:                retbuf[12] = dev->nLastError;
                    283:                retbuf[19] = dev->nLastBlockAddr >> 16;
                    284:                retbuf[20] = dev->nLastBlockAddr >> 8;
                    285:                retbuf[21] = dev->nLastBlockAddr;
1.1.1.7   root      286:        }
                    287: 
                    288:        /*
                    289:        fprintf(stderr,"*** Requested sense packet:\n");
                    290:        int i;
                    291:        for (i = 0; i<nRetLen; i++) fprintf(stderr,"%2x ",retbuf[i]);
                    292:        fprintf(stderr,"\n");
                    293:        */
                    294: 
1.1.1.20! root      295:        ctr->status = HD_STATUS_OK;
1.1.1.7   root      296: }
                    297: 
                    298: 
1.1.1.8   root      299: /**
1.1.1.20! root      300:  * Mode sense - Vendor specific page 00h.
1.1.1.8   root      301:  * (Just enough to make the HDX tool from AHDI 5.0 happy)
                    302:  */
1.1.1.20! root      303: static void HDC_CmdModeSense0x00(SCSI_DEV *dev, SCSI_CTRLR *ctr)
1.1.1.7   root      304: {
1.1.1.20! root      305:        Uint8 *buf = HDC_PrepRespBuf(ctr, 16);
1.1.1.17  root      306: 
                    307:        buf[0] = 0;
1.1.1.20! root      308:        buf[1] = 14;
1.1.1.17  root      309:        buf[2] = 0;
                    310:        buf[3] = 8;
                    311:        buf[4] = 0;
                    312: 
                    313:        buf[5] = dev->hdSize >> 16;  // Number of blocks, high
                    314:        buf[6] = dev->hdSize >> 8;   // Number of blocks, med
                    315:        buf[7] = dev->hdSize;        // Number of blocks, low
                    316: 
                    317:        buf[8] = 0;
                    318: 
                    319:        buf[9] = 0;      // Block size in bytes, high
                    320:        buf[10] = 2;     // Block size in bytes, med
                    321:        buf[11] = 0;     // Block size in bytes, low
                    322: 
                    323:        buf[12] = 0;
                    324:        buf[13] = 0;
                    325:        buf[14] = 0;
                    326:        buf[15] = 0;
1.1.1.20! root      327: }
        !           328: 
        !           329: 
        !           330: /**
        !           331:  * Mode sense - Rigid disk geometry page (requested by ASV).
        !           332:  */
        !           333: static void HDC_CmdModeSense0x04(SCSI_DEV *dev, SCSI_CTRLR *ctr)
        !           334: {
        !           335:        Uint8 *buf = HDC_PrepRespBuf(ctr, 24);
        !           336: 
        !           337:        buf[0] = 4;
        !           338:        buf[1] = 22;
        !           339: 
        !           340:        buf[2] = dev->hdSize >> 23;  // Number of cylinders, high
        !           341:        buf[3] = dev->hdSize >> 15;  // Number of cylinders, med
        !           342:        buf[4] = dev->hdSize >> 7;   // Number of cylinders, low
        !           343: 
        !           344:        buf[5] = 128;    // Number of heads
        !           345: 
        !           346:        buf[6] = 0;
        !           347:        buf[7] = 0;
        !           348:        buf[8] = 0;
        !           349: 
        !           350:        buf[9] = 0;
        !           351:        buf[10] = 0;
        !           352:        buf[11] = 0;
        !           353: 
        !           354:        buf[12] = 0;
        !           355:        buf[13] = 0;
        !           356: 
        !           357:        buf[14] = 0;
        !           358:        buf[15] = 0;
        !           359:        buf[16] = 0;
        !           360: 
        !           361:        buf[17] = 0;
        !           362: 
        !           363:        buf[18] = 0;
        !           364: 
        !           365:        buf[19] = 0;
        !           366: 
        !           367:        buf[20] = 0;
        !           368:        buf[21] = 0;
        !           369: 
        !           370:        buf[22] = 0;
        !           371:        buf[23] = 0;
        !           372: }
        !           373: 
        !           374: 
        !           375: /**
        !           376:  * Mode sense - Get parameters from disk.
        !           377:  */
        !           378: static void HDC_Cmd_ModeSense(SCSI_CTRLR *ctr)
        !           379: {
        !           380:        SCSI_DEV *dev = &ctr->devs[ctr->target];
        !           381: 
        !           382:        LOG_TRACE(TRACE_SCSI_CMD, "HDC: MODE SENSE (%s).\n", HDC_CmdInfoStr(ctr));
        !           383: 
        !           384:        dev->bSetLastBlockAddr = false;
        !           385: 
        !           386:        switch(ctr->command[2])
        !           387:        {
        !           388:         case 0x00:
        !           389:                HDC_CmdModeSense0x00(dev, ctr);
        !           390:                break;
        !           391: 
        !           392:         case 0x04:
        !           393:                HDC_CmdModeSense0x04(dev, ctr);
        !           394:                break;
        !           395: 
        !           396:         default:
        !           397:                Log_Printf(LOG_TODO, "HDC: Unsupported MODE SENSE command\n");
        !           398:                ctr->status = HD_STATUS_ERROR;
        !           399:                dev->nLastError = HD_REQSENS_INVARG;
        !           400:                return;
        !           401:        }
1.1.1.17  root      402: 
1.1.1.20! root      403:        ctr->status = HD_STATUS_OK;
1.1.1.17  root      404:        dev->nLastError = HD_REQSENS_OK;
1.1.1.7   root      405: }
                    406: 
                    407: 
1.1.1.8   root      408: /**
                    409:  * Format drive.
                    410:  */
1.1.1.16  root      411: static void HDC_Cmd_FormatDrive(SCSI_CTRLR *ctr)
1.1.1.7   root      412: {
1.1.1.16  root      413:        SCSI_DEV *dev = &ctr->devs[ctr->target];
                    414: 
                    415:        LOG_TRACE(TRACE_SCSI_CMD, "HDC: FORMAT DRIVE (%s).\n", HDC_CmdInfoStr(ctr));
1.1.1.7   root      416: 
                    417:        /* Should erase the whole image file here... */
                    418: 
1.1.1.20! root      419:        ctr->status = HD_STATUS_OK;
1.1.1.16  root      420:        dev->nLastError = HD_REQSENS_OK;
                    421:        dev->bSetLastBlockAddr = false;
1.1       root      422: }
                    423: 
1.1.1.16  root      424: 
1.1.1.14  root      425: /**
                    426:  * Read capacity of our disk.
                    427:  */
1.1.1.16  root      428: static void HDC_Cmd_ReadCapacity(SCSI_CTRLR *ctr)
1.1.1.14  root      429: {
1.1.1.16  root      430:        SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1.1.17  root      431:        int nSectors = dev->hdSize - 1;
                    432:        Uint8 *buf;
1.1.1.14  root      433: 
1.1.1.17  root      434:        LOG_TRACE(TRACE_SCSI_CMD, "HDC: READ CAPACITY (%s)\n", HDC_CmdInfoStr(ctr));
1.1.1.14  root      435: 
1.1.1.17  root      436:        buf = HDC_PrepRespBuf(ctr, 8);
1.1.1.14  root      437: 
1.1.1.17  root      438:        buf[0] = (nSectors >> 24) & 0xFF;
                    439:        buf[1] = (nSectors >> 16) & 0xFF;
                    440:        buf[2] = (nSectors >> 8) & 0xFF;
1.1.1.20! root      441:        buf[3] = nSectors & 0xFF;
        !           442:        buf[4] = (dev->blockSize >> 24) & 0xFF;
        !           443:        buf[5] = (dev->blockSize >> 16) & 0xFF;
        !           444:        buf[6] = (dev->blockSize >> 8) & 0xFF;
        !           445:        buf[7] = dev->blockSize & 0xFF;
1.1.1.14  root      446: 
1.1.1.20! root      447:        ctr->status = HD_STATUS_OK;
1.1.1.17  root      448:        dev->nLastError = HD_REQSENS_OK;
1.1.1.16  root      449:        dev->bSetLastBlockAddr = false;
1.1.1.14  root      450: }
                    451: 
1.1.1.7   root      452: 
1.1.1.8   root      453: /**
                    454:  * Write a sector off our disk - (seek implied)
                    455:  */
1.1.1.16  root      456: static void HDC_Cmd_WriteSector(SCSI_CTRLR *ctr)
1.1       root      457: {
1.1.1.16  root      458:        SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1.1.11  root      459: 
1.1.1.16  root      460:        dev->nLastBlockAddr = HDC_GetLBA(ctr);
                    461: 
1.1.1.20! root      462:        LOG_TRACE(TRACE_SCSI_CMD, "HDC: WRITE SECTOR (%s) with LBA 0x%x",
        !           463:                  HDC_CmdInfoStr(ctr), dev->nLastBlockAddr);
1.1       root      464: 
1.1.1.7   root      465:        /* seek to the position */
1.1.1.16  root      466:        if (dev->nLastBlockAddr >= dev->hdSize ||
1.1.1.20! root      467:            fseeko(dev->image_file, (off_t)dev->nLastBlockAddr * dev->blockSize, SEEK_SET) != 0)
1.1.1.7   root      468:        {
1.1.1.20! root      469:                ctr->status = HD_STATUS_ERROR;
1.1.1.16  root      470:                dev->nLastError = HD_REQSENS_INVADDR;
1.1.1.7   root      471:        }
                    472:        else
                    473:        {
1.1.1.20! root      474:                ctr->data_len = HDC_GetCount(ctr) * dev->blockSize;
        !           475:                if (ctr->data_len)
1.1.1.11  root      476:                {
1.1.1.20! root      477:                        HDC_PrepRespBuf(ctr, ctr->data_len);
        !           478:                        ctr->dmawrite_to_fh = dev->image_file;
        !           479:                        ctr->status = HD_STATUS_OK;
1.1.1.16  root      480:                        dev->nLastError = HD_REQSENS_OK;
1.1.1.11  root      481:                }
                    482:                else
                    483:                {
1.1.1.20! root      484:                        ctr->status = HD_STATUS_ERROR;
1.1.1.16  root      485:                        dev->nLastError = HD_REQSENS_WRITEERR;
1.1.1.11  root      486:                }
1.1.1.7   root      487:        }
1.1.1.17  root      488:        LOG_TRACE(TRACE_SCSI_CMD, " -> %s (%d)\n",
1.1.1.20! root      489:                  ctr->status == HD_STATUS_OK ? "OK" : "ERROR",
1.1.1.17  root      490:                  dev->nLastError);
1.1       root      491: 
1.1.1.16  root      492:        dev->bSetLastBlockAddr = true;
1.1       root      493: }
                    494: 
1.1.1.7   root      495: 
1.1.1.8   root      496: /**
                    497:  * Read a sector off our disk - (implied seek)
                    498:  */
1.1.1.16  root      499: static void HDC_Cmd_ReadSector(SCSI_CTRLR *ctr)
1.1       root      500: {
1.1.1.16  root      501:        SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1.1.17  root      502:        Uint8 *buf;
1.1.1.11  root      503:        int n;
                    504: 
1.1.1.16  root      505:        dev->nLastBlockAddr = HDC_GetLBA(ctr);
1.1       root      506: 
1.1.1.17  root      507:        LOG_TRACE(TRACE_SCSI_CMD, "HDC: READ SECTOR (%s) with LBA 0x%x",
                    508:                  HDC_CmdInfoStr(ctr), dev->nLastBlockAddr);
1.1       root      509: 
1.1.1.7   root      510:        /* seek to the position */
1.1.1.16  root      511:        if (dev->nLastBlockAddr >= dev->hdSize ||
1.1.1.20! root      512:            fseeko(dev->image_file, (off_t)dev->nLastBlockAddr * dev->blockSize, SEEK_SET) != 0)
1.1.1.7   root      513:        {
1.1.1.20! root      514:                ctr->status = HD_STATUS_ERROR;
1.1.1.16  root      515:                dev->nLastError = HD_REQSENS_INVADDR;
1.1.1.7   root      516:        }
                    517:        else
                    518:        {
1.1.1.20! root      519:                buf = HDC_PrepRespBuf(ctr, dev->blockSize * HDC_GetCount(ctr));
        !           520:                n = fread(buf, dev->blockSize, HDC_GetCount(ctr), dev->image_file);
1.1.1.16  root      521:                if (n == HDC_GetCount(ctr))
1.1.1.11  root      522:                {
1.1.1.20! root      523:                        ctr->status = HD_STATUS_OK;
1.1.1.16  root      524:                        dev->nLastError = HD_REQSENS_OK;
1.1.1.11  root      525:                }
                    526:                else
                    527:                {
1.1.1.20! root      528:                        ctr->status = HD_STATUS_ERROR;
1.1.1.16  root      529:                        dev->nLastError = HD_REQSENS_NOSECTOR;
1.1.1.11  root      530:                }
1.1.1.7   root      531:        }
1.1.1.17  root      532:        LOG_TRACE(TRACE_SCSI_CMD, " -> %s (%d)\n",
1.1.1.20! root      533:                  ctr->status == HD_STATUS_OK ? "OK" : "ERROR",
1.1.1.17  root      534:                  dev->nLastError);
1.1       root      535: 
1.1.1.16  root      536:        dev->bSetLastBlockAddr = true;
1.1       root      537: }
                    538: 
1.1.1.7   root      539: 
1.1.1.8   root      540: /**
1.1.1.12  root      541:  * Test unit ready
                    542:  */
1.1.1.16  root      543: static void HDC_Cmd_TestUnitReady(SCSI_CTRLR *ctr)
1.1.1.12  root      544: {
1.1.1.16  root      545:        LOG_TRACE(TRACE_SCSI_CMD, "HDC: TEST UNIT READY (%s).\n", HDC_CmdInfoStr(ctr));
1.1.1.20! root      546:        ctr->status = HD_STATUS_OK;
1.1.1.12  root      547: }
                    548: 
                    549: 
                    550: /**
1.1.1.8   root      551:  * Emulation routine for HDC command packets.
                    552:  */
1.1.1.16  root      553: static void HDC_EmulateCommandPacket(SCSI_CTRLR *ctr)
1.1       root      554: {
1.1.1.16  root      555:        SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1       root      556: 
1.1.1.20! root      557:        ctr->data_len = 0;
1.1.1.17  root      558: 
1.1.1.16  root      559:        switch (ctr->opcode)
1.1.1.7   root      560:        {
1.1.1.12  root      561:         case HD_TEST_UNIT_RDY:
1.1.1.16  root      562:                HDC_Cmd_TestUnitReady(ctr);
1.1.1.12  root      563:                break;
                    564: 
1.1.1.16  root      565:         case HD_READ_CAPACITY1:
                    566:                HDC_Cmd_ReadCapacity(ctr);
1.1.1.14  root      567:                break;
                    568: 
1.1.1.7   root      569:         case HD_READ_SECTOR:
1.1.1.14  root      570:         case HD_READ_SECTOR1:
1.1.1.16  root      571:                HDC_Cmd_ReadSector(ctr);
1.1.1.7   root      572:                break;
1.1.1.14  root      573: 
1.1.1.7   root      574:         case HD_WRITE_SECTOR:
1.1.1.14  root      575:         case HD_WRITE_SECTOR1:
1.1.1.16  root      576:                HDC_Cmd_WriteSector(ctr);
1.1.1.7   root      577:                break;
                    578: 
                    579:         case HD_INQUIRY:
1.1.1.16  root      580:                HDC_Cmd_Inquiry(ctr);
1.1.1.7   root      581:                break;
                    582: 
                    583:         case HD_SEEK:
1.1.1.16  root      584:                HDC_Cmd_Seek(ctr);
1.1.1.7   root      585:                break;
                    586: 
                    587:         case HD_SHIP:
1.1.1.16  root      588:                LOG_TRACE(TRACE_SCSI_CMD, "HDC: SHIP (%s).\n", HDC_CmdInfoStr(ctr));
1.1.1.20! root      589:                ctr->status = 0xFF;
1.1.1.7   root      590:                break;
                    591: 
                    592:         case HD_REQ_SENSE:
1.1.1.16  root      593:                HDC_Cmd_RequestSense(ctr);
1.1.1.7   root      594:                break;
                    595: 
                    596:         case HD_MODESELECT:
1.1.1.16  root      597:                LOG_TRACE(TRACE_SCSI_CMD, "HDC: MODE SELECT (%s) TODO!\n", HDC_CmdInfoStr(ctr));
1.1.1.20! root      598:                ctr->status = HD_STATUS_OK;
1.1.1.16  root      599:                dev->nLastError = HD_REQSENS_OK;
                    600:                dev->bSetLastBlockAddr = false;
1.1.1.7   root      601:                break;
                    602: 
                    603:         case HD_MODESENSE:
1.1.1.16  root      604:                HDC_Cmd_ModeSense(ctr);
1.1.1.7   root      605:                break;
                    606: 
                    607:         case HD_FORMAT_DRIVE:
1.1.1.16  root      608:                HDC_Cmd_FormatDrive(ctr);
1.1.1.7   root      609:                break;
                    610: 
                    611:         /* as of yet unsupported commands */
                    612:         case HD_VERIFY_TRACK:
                    613:         case HD_FORMAT_TRACK:
                    614:         case HD_CORRECTION:
                    615: 
                    616:         default:
1.1.1.16  root      617:                LOG_TRACE(TRACE_SCSI_CMD, "HDC: Unsupported command (%s)!\n", HDC_CmdInfoStr(ctr));
1.1.1.20! root      618:                ctr->status = HD_STATUS_ERROR;
1.1.1.16  root      619:                dev->nLastError = HD_REQSENS_OPCODE;
                    620:                dev->bSetLastBlockAddr = false;
1.1.1.7   root      621:                break;
                    622:        }
1.1.1.9   root      623: 
                    624:        /* Update the led each time a command is processed */
1.1.1.15  root      625:        Statusbar_EnableHDLed( LED_STATE_ON );
1.1       root      626: }
                    627: 
1.1.1.7   root      628: 
1.1       root      629: /*---------------------------------------------------------------------*/
1.1.1.8   root      630: /**
1.1.1.20! root      631:  * Return given image file (primary) partition count.
        !           632:  * With tracing enabled, print also partition table.
        !           633:  *
        !           634:  * Supports both DOS and Atari master boot record partition
        !           635:  * tables (with 4 entries).
1.1.1.17  root      636:  *
1.1.1.20! root      637:  * Atari partition type names used for checking
        !           638:  * whether drive is / needs to be byte-swapped:
        !           639:  *   GEM     GEMDOS partition < 16MB
        !           640:  *   BGM     GEMDOS partition > 16MB
        !           641:  *   RAW     No file system
        !           642:  *   F32     TOS compatible FAT32 partition
        !           643:  *   LNX     Linux Ext2 partition, not supported by TOS
        !           644:  *   MIX     Minix partition, not supported by TOS
        !           645:  *   SWP     Swap partition, not supported by TOS
        !           646:  *   UNX     ASV (Atari System V) partition, not supported by TOS
        !           647:  *   XGM     Extended partion
        !           648:  *
        !           649:  * Other partition types (listed in XHDI spec):
        !           650:  *   MAC     MAC HFS partition, not supported by TOS
        !           651:  *   QWA     Sinclair QL QDOS partition, not supported by TOS
        !           652:  * (These haven't been found in the wild.)
1.1.1.17  root      653:  *
                    654:  * TODO:
1.1.1.20! root      655:  * - Support also Atari ICD (12 entries, at offset 0x156)
        !           656:  *   and extended partition schemes
        !           657:  *
        !           658:  * Linux kernel has code for both:
        !           659:  *     https://elixir.bootlin.com/linux/v4.0/source/block/partitions/atari.c
        !           660:  *
        !           661:  * Extended partition tables are described in AHDI release notes:
        !           662:  *     https://www.dev-docs.org/docs/htm/search.php?find=AHDI
1.1.1.8   root      663:  */
1.1.1.20! root      664: int HDC_PartitionCount(FILE *fp, const Uint64 tracelevel, int *pIsByteSwapped)
1.1       root      665: {
1.1.1.17  root      666:        unsigned char *pinfo, bootsector[512];
                    667:        Uint32 start, sectors, total = 0;
                    668:        int i, parts = 0;
                    669:        off_t offset;
1.1       root      670: 
1.1.1.17  root      671:        if (!fp)
                    672:                return 0;
                    673:        offset = ftello(fp);
1.1.1.7   root      674: 
1.1.1.17  root      675:        if (fseeko(fp, 0, SEEK_SET) != 0
                    676:            || fread(bootsector, sizeof(bootsector), 1, fp) != 1)
1.1.1.11  root      677:        {
1.1.1.17  root      678:                perror("HDC_PartitionCount");
                    679:                return 0;
1.1.1.11  root      680:        }
1.1       root      681: 
1.1.1.20! root      682:        /* Try to guess whether we've got to swap the image? */
        !           683:        if (pIsByteSwapped)
        !           684:        {
        !           685:                *pIsByteSwapped = (bootsector[0x1fe] == 0xaa && bootsector[0x1ff] == 0x55)
        !           686:                    || (bootsector[0x1c6] == 'G' && bootsector[0x1c8] == 'M' && bootsector[0x1c9] == 'E')
        !           687:                    || (bootsector[0x1c6] == 'B' && bootsector[0x1c8] == 'M' && bootsector[0x1c9] == 'G')
        !           688:                    || (bootsector[0x1c6] == 'X' && bootsector[0x1c8] == 'M' && bootsector[0x1c9] == 'G')
        !           689:                    || (bootsector[0x1c6] == 'L' && bootsector[0x1c8] == 'X' && bootsector[0x1c9] == 'N')
        !           690:                    || (bootsector[0x1c6] == 'R' && bootsector[0x1c8] == 'W' && bootsector[0x1c9] == 'A')
        !           691:                    || (bootsector[0x1c6] == 'F' && bootsector[0x1c8] == '2' && bootsector[0x1c9] == '3')
        !           692:                    || (bootsector[0x1c6] == 'U' && bootsector[0x1c8] == 'X' && bootsector[0x1c9] == 'N')
        !           693:                    || (bootsector[0x1c6] == 'M' && bootsector[0x1c8] == 'X' && bootsector[0x1c9] == 'I')
        !           694:                    || (bootsector[0x1c6] == 'S' && bootsector[0x1c8] == 'P' && bootsector[0x1c9] == 'W');
        !           695: 
        !           696:                if (*pIsByteSwapped)
        !           697:                {
        !           698:                        for (i = 0; i < (int)sizeof(bootsector); i += 2)
        !           699:                        {
        !           700:                                uint8_t b = bootsector[i];
        !           701:                                bootsector[i] = bootsector[i + 1];
        !           702:                                bootsector[i + 1] = b;
        !           703:                        }
        !           704:                }
        !           705:        }
        !           706: 
1.1.1.17  root      707:        if (bootsector[0x1FE] == 0x55 && bootsector[0x1FF] == 0xAA)
                    708:        {
                    709:                int ptype, boot;
1.1       root      710: 
1.1.1.17  root      711:                LOG_TRACE(tracelevel, "DOS MBR:\n");
                    712:                /* first partition table entry */
                    713:                pinfo = bootsector + 0x1BE;
                    714:                for (i = 0; i < 4; i++, pinfo += 16)
                    715:                {
                    716:                        boot = pinfo[0];
                    717:                        ptype = pinfo[4];
1.1.1.18  root      718:                        start = SDL_SwapLE32(*(long*)(pinfo+8));
                    719:                        sectors = SDL_SwapLE32(*(long*)(pinfo+12));
1.1.1.17  root      720:                        total += sectors;
1.1.1.18  root      721:                        LOG_TRACE(tracelevel, "- Partition %d: type=0x%02x, start=0x%08x, size=%.1f MB %s%s\n",
                    722:                                  i, ptype, start, sectors/2048.0, boot ? "(boot)" : "", sectors ? "" : "(invalid)");
1.1.1.17  root      723:                        if (ptype)
                    724:                                parts++;
                    725:                }
1.1.1.18  root      726:                LOG_TRACE(tracelevel, "- Total size: %.1f MB in %d partitions\n", total/2048.0, parts);
1.1.1.17  root      727:        }
                    728:        else
                    729:        {
                    730:                /* Partition table contains hd size + 4 partition entries
                    731:                 * (composed of flag byte, 3 char ID, start offset
                    732:                 * and size), this is followed by bad sector list +
                    733:                 * count and the root sector checksum. Before this
                    734:                 * there's the boot code.
                    735:                 */
                    736:                char c, pid[4];
                    737:                int j, flags;
1.1.1.18  root      738:                bool extended;
1.1.1.17  root      739: 
                    740:                LOG_TRACE(tracelevel, "ATARI MBR:\n");
                    741:                pinfo = bootsector + 0x1C6;
                    742:                for (i = 0; i < 4; i++, pinfo += 12)
                    743:                {
                    744:                        flags = pinfo[0];
                    745:                        for (j = 0; j < 3; j++)
                    746:                        {
                    747:                                c = pinfo[j+1];
                    748:                                if (c < 32 || c >= 127)
                    749:                                        c = '.';
                    750:                                pid[j] = c;
                    751:                        }
                    752:                        pid[3] = '\0';
1.1.1.18  root      753:                        extended = strcmp("XGM", pid) == 0;
1.1.1.17  root      754:                        start = HDC_ReadInt32(pinfo, 4);
                    755:                        sectors = HDC_ReadInt32(pinfo, 8);
1.1.1.18  root      756:                        LOG_TRACE(tracelevel,
                    757:                                  "- Partition %d: ID=%s, start=0x%08x, size=%.1f MB, flags=0x%x %s%s\n",
                    758:                                  i, pid, start, sectors/2048.0, flags,
                    759:                                  (flags & 0x80) ? "(boot)": "",
                    760:                                  extended ? "(extended)" : "");
1.1.1.17  root      761:                        if (flags & 0x1)
                    762:                                parts++;
                    763:                }
                    764:                total = HDC_ReadInt32(bootsector, 0x1C2);
1.1.1.18  root      765:                LOG_TRACE(tracelevel, "- Total size: %.1f MB in %d partitions\n", total/2048.0, parts);
1.1.1.17  root      766:        }
1.1       root      767: 
1.1.1.17  root      768:        if (fseeko(fp, offset, SEEK_SET) != 0)
                    769:                perror("HDC_PartitionCount");
                    770:        return parts;
1.1       root      771: }
                    772: 
1.1.1.17  root      773: /**
1.1.1.18  root      774:  * Check file size for sane values (non-zero, multiple of 512),
                    775:  * and return the size
1.1.1.17  root      776:  */
1.1.1.20! root      777: off_t HDC_CheckAndGetSize(const char *filename, unsigned long blockSize)
1.1.1.17  root      778: {
                    779:        off_t filesize;
1.1.1.18  root      780:        char shortname[48];
1.1.1.17  root      781: 
1.1.1.19  root      782:        File_ShrinkName(shortname, filename, sizeof(shortname) - 1);
1.1.1.17  root      783: 
                    784:        filesize = File_Length(filename);
                    785:        if (filesize < 0)
                    786:        {
1.1.1.18  root      787:                Log_AlertDlg(LOG_ERROR, "Unable to get size of HD image file\n'%s'!",
                    788:                             shortname);
                    789:                if (sizeof(off_t) < 8)
                    790:                {
                    791:                        Log_Printf(LOG_ERROR, "Note: This version of Hatari has been built"
                    792:                                              " _without_ support for large files,\n"
                    793:                                              "      so you can not use HD images > 2 GB.\n");
                    794:                }
                    795:                return -EFBIG;
                    796:        }
                    797:        if (filesize == 0)
                    798:        {
                    799:                Log_AlertDlg(LOG_ERROR, "Can not use HD image file\n'%s'\n"
                    800:                                        "since the file is empty.",
                    801:                             shortname);
1.1.1.17  root      802:                return -EINVAL;
                    803:        }
1.1.1.20! root      804:        if ((filesize & (blockSize - 1)) != 0)
1.1.1.17  root      805:        {
1.1.1.18  root      806:                Log_AlertDlg(LOG_ERROR, "Can not use the hard disk image file\n"
                    807:                                        "'%s'\nsince its size is not a multiple"
1.1.1.20! root      808:                                        " of %ld.", shortname, blockSize);
1.1.1.17  root      809:                return -EINVAL;
                    810:        }
                    811: 
1.1.1.18  root      812:        return filesize;
                    813: }
                    814: 
                    815: /**
                    816:  * Open a disk image file
                    817:  */
1.1.1.20! root      818: int HDC_InitDevice(SCSI_DEV *dev, char *filename, unsigned long blockSize)
1.1.1.18  root      819: {
                    820:        off_t filesize;
                    821:        FILE *fp;
                    822: 
                    823:        dev->enabled = false;
                    824:        Log_Printf(LOG_INFO, "Mounting hard drive image '%s'\n", filename);
                    825: 
                    826:        /* Check size for sanity */
1.1.1.20! root      827:        filesize = HDC_CheckAndGetSize(filename, blockSize);
1.1.1.18  root      828:        if (filesize < 0)
                    829:                return filesize;
                    830: 
1.1.1.17  root      831:        fp = fopen(filename, "rb+");
                    832:        if (fp == NULL)
                    833:        {
1.1.1.20! root      834:                Log_Printf(LOG_ERROR, "Cannot open HD file read/write!\n");
1.1.1.17  root      835:                return -ENOENT;
                    836:        }
                    837:        if (!File_Lock(fp))
                    838:        {
1.1.1.20! root      839:                Log_Printf(LOG_ERROR, "Cannot lock HD file for writing!\n");
1.1.1.17  root      840:                fclose(fp);
                    841:                return -ENOLCK;
                    842:        }
                    843: 
1.1.1.20! root      844:        dev->blockSize = blockSize;
        !           845:        dev->hdSize = filesize / dev->blockSize;
1.1.1.17  root      846:        dev->image_file = fp;
                    847:        dev->enabled = true;
                    848: 
                    849:        return 0;
                    850: }
1.1.1.7   root      851: 
1.1.1.8   root      852: /**
                    853:  * Open the disk image file, set partitions.
1.1       root      854:  */
1.1.1.12  root      855: bool HDC_Init(void)
1.1       root      856: {
1.1.1.16  root      857:        int i;
1.1       root      858: 
1.1.1.17  root      859:        /* ACSI */
                    860:        nAcsiPartitions = 0;
1.1.1.16  root      861:        bAcsiEmuOn = false;
1.1.1.17  root      862:        memset(&AcsiBus, 0, sizeof(AcsiBus));
1.1.1.20! root      863:        AcsiBus.typestr = "ACSI";
        !           864:        AcsiBus.buffer_size = 512;
        !           865:        AcsiBus.buffer = malloc(AcsiBus.buffer_size);
        !           866:        if (!AcsiBus.buffer)
1.1.1.17  root      867:        {
                    868:                perror("HDC_Init");
                    869:                return false;
                    870:        }
1.1.1.16  root      871:        for (i = 0; i < MAX_ACSI_DEVS; i++)
1.1.1.7   root      872:        {
1.1.1.16  root      873:                if (!ConfigureParams.Acsi[i].bUseDevice)
                    874:                        continue;
1.1.1.20! root      875:                if (HDC_InitDevice(&AcsiBus.devs[i], ConfigureParams.Acsi[i].sDeviceFile, ConfigureParams.Acsi[i].nBlockSize) == 0)
1.1.1.16  root      876:                {
1.1.1.17  root      877:                        bAcsiEmuOn = true;
1.1.1.20! root      878:                        nAcsiPartitions += HDC_PartitionCount(AcsiBus.devs[i].image_file, TRACE_SCSI_CMD, NULL);
1.1.1.16  root      879:                }
1.1.1.17  root      880:        }
1.1.1.16  root      881: 
1.1.1.20! root      882:        /* add SCSI partition count to ACSI ones
        !           883:         * to support GEMDOS HD emu partition skipping
        !           884:         */
        !           885:        nAcsiPartitions += Ncr5380_Init();
1.1.1.7   root      886: 
1.1.1.20! root      887:        /* set total number of partitions */
1.1.1.17  root      888:        nNumDrives += nAcsiPartitions;
1.1.1.7   root      889: 
1.1.1.16  root      890:        return bAcsiEmuOn;
1.1       root      891: }
1.1.1.3   root      892: 
1.1.1.7   root      893: 
1.1       root      894: /*---------------------------------------------------------------------*/
1.1.1.8   root      895: /**
                    896:  * HDC_UnInit - close image file
                    897:  *
1.1       root      898:  */
1.1.1.4   root      899: void HDC_UnInit(void)
1.1       root      900: {
1.1.1.16  root      901:        int i;
                    902: 
1.1.1.18  root      903:        for (i = 0; bAcsiEmuOn && i < MAX_ACSI_DEVS; i++)
1.1.1.16  root      904:        {
                    905:                if (!AcsiBus.devs[i].enabled)
                    906:                        continue;
1.1.1.17  root      907:                File_UnLock(AcsiBus.devs[i].image_file);
1.1.1.16  root      908:                fclose(AcsiBus.devs[i].image_file);
                    909:                AcsiBus.devs[i].image_file = NULL;
                    910:                AcsiBus.devs[i].enabled = false;
                    911:        }
1.1.1.20! root      912:        free(AcsiBus.buffer);
        !           913:        AcsiBus.buffer = NULL;
1.1.1.7   root      914: 
1.1.1.20! root      915:        Ncr5380_UnInit();
1.1.1.17  root      916: 
1.1.1.18  root      917:        if (bAcsiEmuOn)
                    918:                nNumDrives -= nAcsiPartitions;
1.1.1.17  root      919:        nAcsiPartitions = 0;
1.1.1.11  root      920:        bAcsiEmuOn = false;
1.1       root      921: }
                    922: 
1.1.1.7   root      923: 
1.1       root      924: /*---------------------------------------------------------------------*/
1.1.1.8   root      925: /**
1.1.1.14  root      926:  * Reset command status.
                    927:  */
                    928: void HDC_ResetCommandStatus(void)
                    929: {
1.1.1.18  root      930:        if (!Config_IsMachineFalcon())
1.1.1.20! root      931:                AcsiBus.status = 0;
1.1.1.14  root      932: }
                    933: 
                    934: 
                    935: /**
1.1.1.16  root      936:  * Process HDC command packets (SCSI/ACSI) bytes.
1.1.1.17  root      937:  * @return true if command has been executed.
1.1.1.14  root      938:  */
1.1.1.20! root      939: bool HDC_WriteCommandPacket(SCSI_CTRLR *ctr, Uint8 b)
1.1.1.14  root      940: {
1.1.1.17  root      941:        bool bDidCmd = false;
1.1.1.16  root      942:        SCSI_DEV *dev = &ctr->devs[ctr->target];
                    943: 
                    944:        /* Abort if the target device is not enabled */
                    945:        if (!dev->enabled)
                    946:        {
1.1.1.20! root      947:                ctr->status = HD_STATUS_ERROR;
1.1.1.17  root      948:                return false;
1.1.1.16  root      949:        }
                    950: 
                    951:        /* Extract ACSI/SCSI opcode */
                    952:        if (ctr->byteCount == 0)
                    953:        {
                    954:                ctr->opcode = b;
                    955:                ctr->bDmaError = false;
                    956:        }
1.1.1.14  root      957: 
1.1.1.16  root      958:        /* Successfully received one byte, and increase the byte-count */
                    959:        if (ctr->byteCount < (int)sizeof(ctr->command))
                    960:                ctr->command[ctr->byteCount] = b;
                    961:        ++ctr->byteCount;
                    962: 
                    963:        /* have we received a complete 6-byte class 0 or 10-byte class 1 packet yet? */
                    964:        if ((ctr->opcode < 0x20 && ctr->byteCount >= 6) ||
                    965:            (ctr->opcode < 0x60 && ctr->byteCount >= 10))
                    966:        {
                    967:                /* We currently only support LUN 0, however INQUIRY must
                    968:                 * always be handled, see SCSI standard */
                    969:                if (HDC_GetLUN(ctr) == 0 || ctr->opcode == HD_INQUIRY)
                    970:                {
                    971:                        HDC_EmulateCommandPacket(ctr);
1.1.1.17  root      972:                        bDidCmd = true;
1.1.1.16  root      973:                }
                    974:                else
                    975:                {
                    976:                        Log_Printf(LOG_WARN, "HDC: Access to non-existing LUN."
                    977:                                   " Command = 0x%02x\n", ctr->opcode);
                    978:                        dev->nLastError = HD_REQSENS_INVLUN;
                    979:                        /* REQUEST SENSE is still handled for invalid LUNs */
                    980:                        if (ctr->opcode == HD_REQ_SENSE)
1.1.1.17  root      981:                        {
1.1.1.16  root      982:                                HDC_Cmd_RequestSense(ctr);
1.1.1.17  root      983:                                bDidCmd = true;
                    984:                        }
1.1.1.16  root      985:                        else
1.1.1.17  root      986:                        {
1.1.1.20! root      987:                                ctr->status = HD_STATUS_ERROR;
1.1.1.17  root      988:                        }
1.1.1.16  root      989:                }
                    990: 
                    991:                ctr->byteCount = 0;
                    992:        }
                    993:        else if (ctr->opcode >= 0x60)
                    994:        {
                    995:                /* Commands >= 0x60 are not supported right now */
1.1.1.20! root      996:                ctr->status = HD_STATUS_ERROR;
1.1.1.16  root      997:                dev->nLastError = HD_REQSENS_OPCODE;
                    998:                dev->bSetLastBlockAddr = false;
                    999:                if (ctr->byteCount == 10)
                   1000:                {
                   1001:                        LOG_TRACE(TRACE_SCSI_CMD, "HDC: Unsupported command (%s).\n",
                   1002:                                  HDC_CmdInfoStr(ctr));
                   1003:                }
                   1004:        }
                   1005:        else
                   1006:        {
1.1.1.20! root     1007:                ctr->status = HD_STATUS_OK;
1.1.1.16  root     1008:        }
1.1.1.17  root     1009: 
                   1010:        return bDidCmd;
1.1.1.16  root     1011: }
1.1.1.14  root     1012: 
                   1013: /*---------------------------------------------------------------------*/
1.1.1.16  root     1014: 
1.1.1.20! root     1015: static void Acsi_DmaTransfer(void)
1.1.1.16  root     1016: {
1.1.1.20! root     1017:        Uint32 nDmaAddr = FDC_GetDMAAddress();
        !          1018:        Uint16 nDmaMode = FDC_DMA_GetMode();
1.1.1.16  root     1019: 
1.1.1.20! root     1020:        /* Don't do anything if no DMA to ACSI bus or nothing to transfer */
        !          1021:        if ((nDmaMode & 0xc0) != 0x00 || AcsiBus.data_len == 0)
        !          1022:                return;
1.1.1.14  root     1023: 
1.1.1.20! root     1024:        if ((AcsiBus.dmawrite_to_fh && (nDmaMode & 0x100) == 0)
        !          1025:            || (!AcsiBus.dmawrite_to_fh && (nDmaMode & 0x100) != 0))
1.1.1.14  root     1026:        {
1.1.1.20! root     1027:                Log_Printf(LOG_WARN, "DMA direction does not match SCSI command!\n");
        !          1028:                return;
1.1.1.14  root     1029:        }
1.1.1.12  root     1030: 
1.1.1.20! root     1031:        if (AcsiBus.dmawrite_to_fh)
1.1.1.12  root     1032:        {
1.1.1.20! root     1033:                /* write - if allowed */
        !          1034:                if (STMemory_CheckAreaType(nDmaAddr, AcsiBus.data_len, ABFLAG_RAM | ABFLAG_ROM))
        !          1035:                {
        !          1036: #ifndef DISALLOW_HDC_WRITE
        !          1037:                        int wlen = fwrite(&STRam[nDmaAddr], 1, AcsiBus.data_len, AcsiBus.dmawrite_to_fh);
        !          1038:                        if (wlen != AcsiBus.data_len)
        !          1039:                        {
        !          1040:                                Log_Printf(LOG_ERROR, "Could not write all bytes to hard disk image.\n");
        !          1041:                                AcsiBus.status = HD_STATUS_ERROR;
        !          1042:                        }
        !          1043: #endif
        !          1044:                }
1.1.1.16  root     1045:                else
1.1.1.20! root     1046:                {
        !          1047:                        Log_Printf(LOG_WARN, "HDC DMA write uses invalid RAM range 0x%x+%i\n",
        !          1048:                                   nDmaAddr, AcsiBus.data_len);
        !          1049:                        AcsiBus.bDmaError = true;
        !          1050:                }
        !          1051:                AcsiBus.dmawrite_to_fh = NULL;
        !          1052:        }
        !          1053:        else if (!STMemory_SafeCopy(nDmaAddr, AcsiBus.buffer, AcsiBus.data_len, "ACSI DMA"))
        !          1054:        {
        !          1055:                AcsiBus.bDmaError = true;
        !          1056:                AcsiBus.status = HD_STATUS_ERROR;
1.1.1.12  root     1057:        }
                   1058: 
1.1.1.20! root     1059:        FDC_WriteDMAAddress(nDmaAddr + AcsiBus.data_len);
        !          1060:        AcsiBus.data_len = 0;
1.1.1.7   root     1061: 
1.1.1.20! root     1062:        FDC_SetDMAStatus(AcsiBus.bDmaError);    /* Mark DMA error */
        !          1063:        FDC_SetIRQ(FDC_IRQ_SOURCE_HDC);
        !          1064: }
1.1.1.16  root     1065: 
                   1066: static void Acsi_WriteCommandByte(int addr, Uint8 byte)
                   1067: {
1.1.1.17  root     1068:        /* Clear IRQ initially (will be set again if byte has been accepted) */
                   1069:        FDC_ClearHdcIRQ();
                   1070: 
                   1071:        /* When the A1 pin is pushed to 0, we want to start a new command.
                   1072:         * We ignore the pin for the second byte in the packet since this
                   1073:         * seems to happen on real hardware too (some buggy driver relies
                   1074:         * on this behavior). */
                   1075:        if ((addr & 2) == 0 && AcsiBus.byteCount != 1)
1.1.1.7   root     1076:        {
1.1.1.16  root     1077:                AcsiBus.byteCount = 0;
                   1078:                AcsiBus.target = ((byte & 0xE0) >> 5);
                   1079:                /* Only process the first byte if it is not
                   1080:                 * an extended ICD command marker byte */
                   1081:                if ((byte & 0x1F) != 0x1F)
1.1.1.7   root     1082:                {
1.1.1.16  root     1083:                        HDC_WriteCommandPacket(&AcsiBus, byte & 0x1F);
1.1.1.7   root     1084:                }
                   1085:                else
                   1086:                {
1.1.1.20! root     1087:                        AcsiBus.status = HD_STATUS_OK;
1.1.1.16  root     1088:                        AcsiBus.bDmaError = false;
1.1.1.7   root     1089:                }
1.1.1.16  root     1090:        }
                   1091:        else
                   1092:        {
                   1093:                /* Process normal command byte */
1.1.1.17  root     1094:                bool bDidCmd = HDC_WriteCommandPacket(&AcsiBus, byte);
1.1.1.20! root     1095:                if (bDidCmd && AcsiBus.status == HD_STATUS_OK && AcsiBus.data_len)
1.1.1.17  root     1096:                {
1.1.1.20! root     1097:                        Acsi_DmaTransfer();
1.1.1.17  root     1098:                }
1.1.1.16  root     1099:        }
1.1.1.7   root     1100: 
1.1.1.16  root     1101:        if (AcsiBus.devs[AcsiBus.target].enabled)
                   1102:        {
                   1103:                FDC_SetDMAStatus(AcsiBus.bDmaError);    /* Mark DMA error */
                   1104:                FDC_SetIRQ(FDC_IRQ_SOURCE_HDC);
1.1.1.7   root     1105:        }
1.1       root     1106: }
1.1.1.16  root     1107: 
                   1108: /**
                   1109:  * Called when command bytes have been written to $FFFF8606 and
                   1110:  * the HDC (not the FDC) is selected.
                   1111:  */
                   1112: void HDC_WriteCommandByte(int addr, Uint8 byte)
                   1113: {
                   1114:        // fprintf(stderr, "HDC: Write cmd byte addr=%i, byte=%02x\n", addr, byte);
                   1115: 
1.1.1.18  root     1116:        if (Config_IsMachineFalcon())
1.1.1.16  root     1117:                Ncr5380_WriteByte(addr, byte);
                   1118:        else if (bAcsiEmuOn)
                   1119:                Acsi_WriteCommandByte(addr, byte);
                   1120: }
                   1121: 
                   1122: /**
                   1123:  * Get command byte.
                   1124:  */
                   1125: short int HDC_ReadCommandByte(int addr)
                   1126: {
                   1127:        Uint16 ret;
1.1.1.18  root     1128:        if (Config_IsMachineFalcon())
1.1.1.16  root     1129:                ret = Ncr5380_ReadByte(addr);
                   1130:        else
1.1.1.20! root     1131:                ret = AcsiBus.status;           /* ACSI status */
1.1.1.16  root     1132:        return ret;
                   1133: }
1.1.1.20! root     1134: 
        !          1135: /**
        !          1136:  * Called when DMA transfer has just been enabled for the hard disk in the
        !          1137:  * $FFFF8606 (DMA mode) register.
        !          1138:  */
        !          1139: void HDC_DmaTransfer(void)
        !          1140: {
        !          1141:        if (Config_IsMachineFalcon())
        !          1142:                Ncr5380_DmaTransfer_Falcon();
        !          1143:        else if (bAcsiEmuOn)
        !          1144:                Acsi_DmaTransfer();
        !          1145: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.