/* Format a floppy disk (880k drive). * Author: Mark R. Rinfret * Date: 06/28/87 * Description: * This set of routines may be incorporated into a program which * has need of formatting a floppy disk. I wrote it to support my * hard disk backup utility. * * History: (most recent change first) * * 12/08/87 -MRR- My floppy drive (at least) will occaisionally "hang" * during formatting. I don't know what the source of * the problem is, but for now I've added a timer and * switched to a SendIO/Wait combo to handle this. * * 08/26/87 -MRR- Modified FormatDisk to delay 5 seconds after * uninhibiting the drive. This should give enough time * for the validator to do its thing and prevent the * "Insert disk..." requester from rearing its ugly head * if the application attempts to access the disk as * soon as we return. */ #include #include #include #include #include #include #include ":src/lib/AmigaFunctions.h" #include ":src/lib/Timer.h" extern struct IOExtTD *CreateExtIO(); static int CkIOErr(); #define MAX_NAME 30L #define TD_WRITE CMD_WRITE #define TRACKSIZE NUMSECS * TD_SECTOR /* Format a floppy disk - hardwired for the 3.5" 880k floppy drives. * Called with: * drivename: device name (DF0, etc.) * name: new volume name * Returns: * Zero on success, 1 on failure * Note: * This routine does not currently perform a verification, as * recommended by the RKM. Perhaps later... * I also discovered that there's some erroneous crap in * "The Amiga Programmer's Workbook, Vol. II", by * Eugene P. Mortimore. On page 339, he states that only 512 * bytes of track data are required for formatting. The RKM * correctly states that a "track's worth of data" is required. * It took some playing with DiskEd to discover this error. */ int FormatDisk(drivename,name) char *drivename; char *name; { long checkSum; char *dosID = "DOS"; long dosWord = 0; short error; long diskBit, timerBit; struct MsgPort *diskPort = NULL; struct IOExtTD *diskRequest = NULL; ULONG diskChangeCount; USHORT retries, status = 0, i, timeout, track; long signals; /* signal bits from Wait() */ struct timerequest *timeRequest; /* timeout timer request */ int unit; char *volname; char *diskBuffer; ULONG *diskBlock; /* alias for diskBuffer, ULONG type */ if (strlen(name) >= MAX_NAME) { #ifdef DEBUG printf("Disk name is too long!\n"); #endif status = ERROR_INVALID_COMPONENT_NAME; goto cleanup; } if ((unit = (drivename[2]-'0')) < 0 || unit >= NUMUNITS) { #ifdef DEBUG printf("FormatDisk: invalid drive specification!\n"); #endif status = ERROR_INVALID_COMPONENT_NAME; goto cleanup; } if (!(diskBuffer = AllocMem((long) TRACKSIZE, MEMF_PUBLIC | MEMF_CHIP))) { status = ERROR_NO_FREE_STORE; goto cleanup; } timeRequest = CreateTimer(0); /* create uHERZ timer */ if (timeRequest == NULL) { status = ERROR_NO_FREE_STORE; /* would IoError be valid? */ goto cleanup; } /* Store DOS "magic word" in disk block to be written during * formatting. */ diskBlock = (ULONG *) diskBuffer;/* we'll need this later */ for (i = 0; i < 3; ++i) dosWord = (dosWord << 8) | dosID[i]; dosWord = dosWord << 8; #ifdef DEBUG printf("dosWord is %lx\n",dosWord); #endif for (i = 0; i < TRACKSIZE / 4; ++i) diskBlock[i] = (dosWord | (long) (i & 0xff)); if ((diskPort = CreatePort(0L, 0L)) == NULL) { #ifdef DEBUG printf("FormatDisk can't create port!\n"); #endif status = 1; /* is there a better error code? */ goto cleanup; } if (!(diskRequest = (struct IOExtTD *) CreateExtIO(diskPort, (long) sizeof(struct IOExtTD)))) { status = 1; goto cleanup; } if (status = OpenDevice(TD_NAME, (long) unit, diskRequest, 0L)) { #ifdef DEBUG printf("FormatDisk: OpenDevice error: %d\n",error); #endif goto cleanup; } if (status = Inhibit(drivename, 1)) { #ifdef DEBUG printf("FormatDisk: unable to inhibit drive!\n"); #endif goto cleanup; } /* Get the current disk change count. This allows the trackdisk * driver to detect unwanted disk changes later on. */ diskRequest->iotd_Req.io_Command = TD_CHANGENUM; DoIO(diskRequest); /* Save a copy of the disk change count. */ diskChangeCount = diskRequest->iotd_Req.io_Actual; #ifdef DEBUG printf("Current disk change count is %ld\n", diskChangeCount); #endif /* Format the disk, one track at a time. This operation seems * susceptible to timing out (infrequently) on my system, so we'll * use a combination of SendIO and Wait, rather than DoIO. */ diskBit = 1L << diskRequest->iotd_Req.io_Message.mn_ReplyPort->mp_SigBit; timerBit = 1L << timeRequest->tr_node.io_Message.mn_ReplyPort->mp_SigBit; #ifdef DEBUG printf("diskBit => %08lx, timerBit => %08lx\n", diskBit, timerBit); #endif for (track = 0; track < NUMTRACKS; ++track) { retries = 0; retry: diskRequest->iotd_Req.io_Command = TD_FORMAT; diskRequest->iotd_Req.io_Flags = 0; diskRequest->iotd_Req.io_Data = (APTR) diskBuffer; diskRequest->iotd_Count = diskChangeCount; diskRequest->iotd_Req.io_Length = NUMSECS * TD_SECTOR; diskRequest->iotd_Req.io_Offset = track * NUMSECS * TD_SECTOR; SendIO(diskRequest); StartTimer(timeRequest, 5L, 0L); /* Start 5 second disk timer. */ #ifdef DEBUG if (timeRequest->tr_node.io_Error) { printf("Error on StartTimer: %d\n", timeRequest->tr_node.io_Error); } #endif while (TRUE) { timeout = 0; signals = Wait(diskBit | timerBit); if (signals & timerBit) { if (CheckIO(timeRequest)) { /* timer I/O complete? */ WaitIO(timeRequest); /* complete timer processing */ AbortIO(diskRequest); /* kill disk I/O */ timeout = 1; #ifdef DEBUG puts("--TIMEOUT--"); #endif break; } } if (signals & diskBit) { /* Did disk complete? */ if (CheckIO(diskRequest)) { WaitIO(diskRequest); /* complete disk processing */ StopTimer(timeRequest); break; } } } /* end while(TRUE) */ /* Retry a timeout 3 times before giving up. */ if (timeout && (++retries < 4) ) goto retry; if (status = CkIOErr(diskRequest,"Formatting error")) { #ifdef DEBUG printf(" Track: %d\n",track); #endif goto cleanup; } } /* Now comes some real KLUDGING. Fill in the root block and the * first hash block. The information for this was gathered from * the "AmigaDos Technical Reference Manual" and some sleuthing * with DiskEd. */ for (i = 0; i < 128; ++i) diskBlock[i] = 0; diskBlock[0] = 2; /* T.SHORT (type) */ diskBlock[3] = 128 - 56; /* hashtable size */ diskBlock[78] = 0xffffffff; /* BMFLAG */ diskBlock[79] = 881; /* first bitmap block */ DateStamp(&diskBlock[105]); /* volume last altered date/time */ DateStamp(&diskBlock[121]); /* volume creation date/time */ volname = (char *) &diskBlock[108]; /* convert input name to BSTR */ *volname = strlen(name); for (i = 0; i < *volname; ++i) *(volname + 1 + i) = *(name + i); diskBlock[127] = 1; /* ST.ROOT (secondary type) */ checkSum = 0; for (i = 0; i < 128; ++i) checkSum += diskBlock[i]; diskBlock[5] = - checkSum; /* Write the root block out to the disk. */ diskRequest->iotd_Req.io_Command = TD_WRITE; diskRequest->iotd_Req.io_Length = TD_SECTOR; diskRequest->iotd_Req.io_Offset = TD_SECTOR * 880L; DoIO(diskRequest); if (status = CkIOErr(diskRequest, "Error writing root block")) { goto cleanup; } /* Write the first bitmap block. */ for (i = 0; i < 56; ++i) diskBlock[i] = 0xffffffff; for (i = 56; i < 128; ++i) diskBlock[i] = 0; diskBlock[0] = 0xc000c037; /* hint: x37 = 55 (last word of map?) */ diskBlock[28] = 0xffff3fff; /* blocks 880, 881 used */ diskBlock[55] = 0x3fffffff; /* blocks 1760, 1761 used? */ diskRequest->iotd_Req.io_Length = TD_SECTOR; diskRequest->iotd_Req.io_Offset = 881L * TD_SECTOR; DoIO(diskRequest); /* write out the bitmap */ if (status = CkIOErr(diskRequest, "Error writing bitmap")) { goto cleanup; } diskRequest->iotd_Req.io_Command = ETD_UPDATE; diskRequest->iotd_Req.io_Flags = 0; DoIO(diskRequest); /* Turn the disk motor off. */ diskRequest->iotd_Req.io_Command = TD_MOTOR; diskRequest->iotd_Req.io_Length = 0; DoIO(diskRequest); Inhibit(drivename, 0); /* enable disk validator */ Delay(3L * TICKS_PER_SECOND); /* Give it a chance */ cleanup: CloseDevice(diskRequest); if (diskBuffer) FreeMem(diskBuffer, (long) TRACKSIZE); if (diskRequest) DeleteExtIO(diskRequest, (long) sizeof(*diskRequest)); if (diskPort) DeletePort(diskPort); if (timeRequest) DeleteTimer(timeRequest); return status; } /* Check the disk request block for an error code. If an error * occurred, print the argument string. * Called with: * req: pointer to I/O request structure * msg: error message string * Returns: * error code from request structure */ static int CkIOErr(req, msg) struct IOStdReq *req; char *msg; { register int code; if (code = req->io_Error) { #ifdef DEBUG printf("%s, code: %d\n",msg,code); #endif } return code; } #ifdef DEBUG main(argc, argv) int argc; char *argv[]; { char *diskname; char *volname; int unit; if (argc < 3) volname = "GoodJob!"; else volname = argv[2]; if (argc < 2) diskname = "DF1:"; else { diskname = argv[1]; if (strlen(diskname) != 4 || (strncmp(diskname,"df",2) && strncmp(diskname,"DF",2))) { bad_drive: printf("Drive name may only be df0: through df3:!\n"); exit(1); } if ((unit = (diskname[2] - '0')) < 0 || unit > 3) goto bad_drive; } printf("Insert disk in %s, then hit return\n",diskname); while (getchar() != '\n'); if (FormatDisk(diskname,volname)) printf("FormatDisk failed\n"); else { printf("FormatDisk succeeded\n"); } } #endif