2 * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 2.0 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 * Mach Operating System
26 * Copyright (c) 1990 Carnegie-Mellon University
27 * Copyright (c) 1989 Carnegie-Mellon University
28 * All rights reserved. The CMU software License Agreement specifies
29 * the terms and conditions for use and redistribution.
33 * INTEL CORPORATION PROPRIETARY INFORMATION
35 * This software is supplied under the terms of a license agreement or
36 * nondisclosure agreement with Intel Corporation and may not be copied
37 * nor disclosed except in accordance with the terms of that agreement.
39 * Copyright 1988, 1989 Intel Corporation
43 * Copyright 1993 NeXT Computer, Inc.
44 * All rights reserved.
47 #include "bootstruct.h"
56 #include <IOKit/storage/IOApplePartitionScheme.h>
58 #define BPS 512 /* sector size of the device */
59 #define CD_BPS 2048 /* CD-ROM block size */
60 #define N_CACHE_SECS (BIOS_LEN / BPS) /* Must be a multiple of 4 for CD-ROMs */
61 #define UFS_FRONT_PORCH 0
62 #define kAPMSector 2 /* Sector number of Apple partition map */
63 #define kAPMCDSector 8 /* Translated sector of Apple partition map on a CD */
66 * trackbuf points to the start of the track cache. Biosread()
67 * will store the sectors read from disk to this memory area.
69 * biosbuf points to a sector within the track cache, and is
70 * updated by Biosread().
72 static char * const trackbuf
= (char *) ptov(BIOS_ADDR
);
73 static char * biosbuf
;
76 * Map a disk drive to bootable volumes contained within.
79 int biosdev
; // BIOS device number (unique)
80 BVRef bvr
; // chain of boot volumes on the disk
81 int bvrcnt
; // number of boot volumes
82 struct DiskBVMap
* next
; // linkage to next mapping
85 static struct DiskBVMap
* gDiskBVMap
= NULL
;
86 static struct disk_blk0
* gBootSector
= NULL
;
88 extern void spinActivityIndicator();
90 static void getVolumeDescription(BVRef bvr
, char * str
, long strMaxLen
);
92 //==========================================================================
94 static int getDriveInfo( int biosdev
, struct driveInfo
*dip
)
96 static struct driveInfo cached_di
;
99 if ( !cached_di
.valid
|| biosdev
!= cached_di
.biosdev
)
101 cc
= get_drive_info(biosdev
, &cached_di
);
104 return (-1); // BIOS call error
108 bcopy(&cached_di
, dip
, sizeof(cached_di
));
113 //==========================================================================
114 // Maps (E)BIOS return codes to message strings.
121 static const char * getNameForValue( const struct NamedValue
* nameTable
,
122 unsigned char value
)
124 const struct NamedValue
* np
;
126 for ( np
= nameTable
; np
->value
; np
++)
127 if (np
->value
== value
)
133 #define ECC_CORRECTED_ERR 0x11
135 static const struct NamedValue bios_errors
[] = {
136 { 0x10, "Media error" },
137 { 0x11, "Corrected ECC error" },
138 { 0x20, "Controller or device error" },
139 { 0x40, "Seek failed" },
140 { 0x80, "Device timeout" },
141 { 0xAA, "Drive not ready" },
145 static const char * bios_error(int errnum
)
147 static char errorstr
[] = "Error 0x00";
148 const char * errname
;
150 errname
= getNameForValue( bios_errors
, errnum
);
151 if ( errname
) return errname
;
153 sprintf(errorstr
, "Error 0x%02x", errnum
);
154 return errorstr
; // No string, print error code only
157 //==========================================================================
158 // Use BIOS INT13 calls to read the sector specified. This function will
159 // also perform read-ahead to cache a few subsequent sector to the sector
163 // 0 on success, or an error code from INT13/F2 or INT13/F42 BIOS call.
165 static BOOL cache_valid
= FALSE
;
167 static int Biosread( int biosdev
, unsigned int secno
)
169 static int xbiosdev
, xcyl
, xhead
;
170 static unsigned int xsec
, xnsecs
;
178 if (getDriveInfo(biosdev
, &di
) < 0) {
181 if (di
.no_emulation
) {
182 /* Always assume 2k block size; BIOS may lie about geometry */
185 bps
= di
.di
.params
.phys_nbps
;
192 DEBUG_DISK(("Biosread dev %x sec %d bps %d\n", biosdev
, secno
, bps
));
194 // To read the disk sectors, use EBIOS if we can. Otherwise,
195 // revert to the standard BIOS calls.
197 if ((biosdev
>= kBIOSDevTypeHardDrive
) &&
198 (di
.uses_ebios
& EBIOS_FIXED_DISK_ACCESS
))
201 (biosdev
== xbiosdev
) &&
203 ((unsigned int)secno
< (xsec
+ xnsecs
)))
205 biosbuf
= trackbuf
+ (BPS
* (secno
- xsec
));
209 xnsecs
= N_CACHE_SECS
;
210 xsec
= (secno
/ divisor
) * divisor
;
213 while ((rc
= ebiosread(biosdev
, secno
/ divisor
, xnsecs
/ divisor
)) && (++tries
< 5))
215 if (rc
== ECC_CORRECTED_ERR
) {
216 /* Ignore corrected ECC errors */
220 error(" EBIOS read error: %s\n", bios_error(rc
), rc
);
221 error(" Block %d Sectors %d\n", secno
, xnsecs
);
227 /* spc = spt * heads */
228 int spc
= (di
.di
.params
.phys_spt
* di
.di
.params
.phys_heads
);
230 head
= (secno
% spc
) / di
.di
.params
.phys_spt
;
231 sec
= secno
% di
.di
.params
.phys_spt
;
234 (biosdev
== xbiosdev
) &&
237 ((unsigned int)sec
>= xsec
) &&
238 ((unsigned int)sec
< (xsec
+ xnsecs
)))
240 // this sector is in trackbuf cache
241 biosbuf
= trackbuf
+ (BPS
* (sec
- xsec
));
245 // Cache up to a track worth of sectors, but do not cross a
251 xnsecs
= ((unsigned int)(sec
+ N_CACHE_SECS
) > di
.di
.params
.phys_spt
) ? (di
.di
.params
.phys_spt
- sec
) : N_CACHE_SECS
;
254 while ((rc
= biosread(biosdev
, cyl
, head
, sec
, xnsecs
)) &&
257 if (rc
== ECC_CORRECTED_ERR
) {
258 /* Ignore corrected ECC errors */
262 error(" BIOS read error: %s\n", bios_error(rc
), rc
);
263 error(" Block %d, Cyl %d Head %d Sector %d\n",
264 secno
, cyl
, head
, sec
);
269 // If the BIOS reported success, mark the sector cache as valid.
274 biosbuf
= trackbuf
+ (secno
% divisor
) * BPS
;
277 spinActivityIndicator();
282 //==========================================================================
284 static int readBytes( int biosdev
, unsigned int blkno
,
285 unsigned int byteCount
, void * buffer
)
288 char * cbuf
= (char *) buffer
;
292 DEBUG_DISK(("%s: dev %x block %x [%d] -> 0x%x...", __FUNCTION__
,
293 biosdev
, blkno
, byteCount
, (unsigned)cbuf
));
295 for ( ; byteCount
; cbuf
+= BPS
, blkno
++ )
297 error
= Biosread( biosdev
, blkno
);
300 DEBUG_DISK(("error\n"));
304 copy_len
= (byteCount
> BPS
) ? BPS
: byteCount
;
305 bcopy( biosbuf
, cbuf
, copy_len
);
306 byteCount
-= copy_len
;
309 DEBUG_DISK(("done\n"));
314 //==========================================================================
316 static int isExtendedFDiskPartition( const struct fdisk_part
* part
)
318 static unsigned char extParts
[] =
321 0x0f, /* Win95 extended */
322 0x85, /* Linux extended */
327 for (i
= 0; i
< sizeof(extParts
)/sizeof(extParts
[0]); i
++)
329 if (extParts
[i
] == part
->systid
) return 1;
334 //==========================================================================
336 static int getNextFDiskPartition( int biosdev
, int * partno
,
337 const struct fdisk_part
** outPart
)
339 static int sBiosdev
= -1;
340 static int sNextPartNo
;
341 static unsigned int sFirstBase
;
342 static unsigned int sExtBase
;
343 static unsigned int sExtDepth
;
344 static struct fdisk_part
* sExtPart
;
345 struct fdisk_part
* part
;
347 if ( sBiosdev
!= biosdev
|| *partno
< 0 )
350 if ( readBootSector( biosdev
, DISK_BLK0
, 0 ) ) return 0;
364 if ( sNextPartNo
< FDISK_NPART
)
366 part
= (struct fdisk_part
*) gBootSector
->parts
[sNextPartNo
];
370 unsigned int blkno
= sExtPart
->relsect
+ sFirstBase
;
372 // Save the block offset of the first extended partition.
374 if (sExtDepth
== 0) {
379 // Load extended partition table.
381 if ( readBootSector( biosdev
, blkno
, 0 ) == 0 )
388 // Fall through to part == NULL
391 if ( part
== NULL
) break; // Reached end of partition chain.
393 // Advance to next partition number.
397 if ( isExtendedFDiskPartition(part
) )
405 if ( part
->systid
== 0x00 )
410 // Change relative offset to an absolute offset.
411 part
->relsect
+= sExtBase
;
414 *partno
= sExtDepth
? (int)(sExtDepth
+ FDISK_NPART
) : sNextPartNo
;
419 return (part
!= NULL
);
422 //==========================================================================
424 static BVRef
newFDiskBVRef( int biosdev
, int partno
, unsigned int blkoff
,
425 const struct fdisk_part
* part
,
426 FSInit initFunc
, FSLoadFile loadFunc
,
428 FSGetDirEntry getdirFunc
,
429 FSGetFileBlock getBlockFunc
,
430 BVGetDescription getDescriptionFunc
,
431 int probe
, int type
)
433 BVRef bvr
= (BVRef
) malloc( sizeof(*bvr
) );
436 bzero(bvr
, sizeof(*bvr
));
438 bvr
->biosdev
= biosdev
;
439 bvr
->part_no
= partno
;
440 bvr
->part_boff
= blkoff
;
441 bvr
->part_type
= part
->systid
;
442 bvr
->fs_loadfile
= loadFunc
;
443 bvr
->fs_readfile
= readFunc
;
444 bvr
->fs_getdirentry
= getdirFunc
;
445 bvr
->fs_getfileblock
= getBlockFunc
;
446 bvr
->description
= getDescriptionFunc
?
447 getDescriptionFunc
: getVolumeDescription
;
450 if ( part
->bootid
& FDISK_ACTIVE
)
451 bvr
->flags
|= kBVFlagPrimary
;
453 // Probe the filesystem.
457 bvr
->flags
|= kBVFlagNativeBoot
;
459 if ( probe
&& initFunc( bvr
) != 0 )
461 // filesystem probe failed.
463 DEBUG_DISK(("%s: failed probe on dev %x part %d\n",
464 __FUNCTION__
, biosdev
, partno
));
470 else if ( readBootSector( biosdev
, blkoff
, (void *)0x7e00 ) == 0 )
472 bvr
->flags
|= kBVFlagForeignBoot
;
483 //==========================================================================
485 BVRef
newAPMBVRef( int biosdev
, int partno
, unsigned int blkoff
,
487 FSInit initFunc
, FSLoadFile loadFunc
,
489 FSGetDirEntry getdirFunc
,
490 FSGetFileBlock getBlockFunc
,
491 BVGetDescription getDescriptionFunc
,
492 int probe
, int type
)
494 BVRef bvr
= (BVRef
) malloc( sizeof(*bvr
) );
497 bzero(bvr
, sizeof(*bvr
));
499 bvr
->biosdev
= biosdev
;
500 bvr
->part_no
= partno
;
501 bvr
->part_boff
= blkoff
;
502 bvr
->fs_loadfile
= loadFunc
;
503 bvr
->fs_readfile
= readFunc
;
504 bvr
->fs_getdirentry
= getdirFunc
;
505 bvr
->fs_getfileblock
= getBlockFunc
;
506 bvr
->description
= getDescriptionFunc
?
507 getDescriptionFunc
: getVolumeDescription
;
509 strlcpy(bvr
->name
, part
->dpme_name
, DPISTRLEN
);
510 strlcpy(bvr
->type_name
, part
->dpme_type
, DPISTRLEN
);
513 if ( part->bootid & FDISK_ACTIVE )
514 bvr->flags |= kBVFlagPrimary;
517 // Probe the filesystem.
521 bvr
->flags
|= kBVFlagNativeBoot
;
523 if ( probe
&& initFunc( bvr
) != 0 )
525 // filesystem probe failed.
527 DEBUG_DISK(("%s: failed probe on dev %x part %d\n",
528 __FUNCTION__
, biosdev
, partno
));
535 else if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )
537 bvr->flags |= kBVFlagForeignBoot;
549 //==========================================================================
551 /* A note on partition numbers:
552 * IOKit makes the primary partitions numbers 1-4, and then
553 * extended partitions are numbered consecutively 5 and up.
554 * So, for example, if you have two primary partitions and
555 * one extended partition they will be numbered 1, 2, 5.
558 static BVRef
diskScanFDiskBootVolumes( int biosdev
, int * countPtr
)
560 const struct fdisk_part
* part
;
561 struct DiskBVMap
* map
;
564 BVRef booterUFS
= NULL
;
567 boot_drive_info_t
*dp
;
569 /* Initialize disk info */
570 if (getDriveInfo(biosdev
, &di
) != 0) {
574 spc
= (dp
->params
.phys_spt
* dp
->params
.phys_heads
);
576 /* This is probably a CD-ROM; punt on the geometry. */
581 // Create a new mapping.
583 map
= (struct DiskBVMap
*) malloc( sizeof(*map
) );
586 map
->biosdev
= biosdev
;
589 map
->next
= gDiskBVMap
;
592 // Create a record for each partition found on the disk.
594 while ( getNextFDiskPartition( biosdev
, &partno
, &part
) )
596 DEBUG_DISK(("%s: part %d [%x]\n", __FUNCTION__
,
597 partno
, part
->systid
));
600 switch ( part
->systid
)
605 part
->relsect
+ UFS_FRONT_PORCH
/BPS
,
614 kBIOSDevTypeHardDrive
);
629 kBIOSDevTypeHardDrive
);
633 booterUFS
= newFDiskBVRef(
635 ((part
->relsect
+ spc
- 1) / spc
) * spc
,
644 kBIOSDevTypeHardDrive
);
655 kBIOSDevTypeHardDrive
);
664 kBIOSDevTypeHardDrive
);
670 bvr
->next
= map
->bvr
;
676 // Booting from a CD with an UFS filesystem embedded
677 // in a booter partition.
681 if ( map
->bvrcnt
== 0 )
683 map
->bvr
= booterUFS
;
686 else free( booterUFS
);
692 * If no FDisk partition, then we will check for
693 * an Apple partition map elsewhere.
696 if (map
->bvrcnt
== 0) {
697 static struct fdisk_part cdpart
;
698 cdpart
.systid
= 0xCD;
700 /* Let's try assuming we are on a hybrid HFS/ISO9660 CD. */
711 kBIOSDevTypeHardDrive
);
712 bvr
->next
= map
->bvr
;
718 if (countPtr
) *countPtr
= map
? map
->bvrcnt
: 0;
720 return map
? map
->bvr
: NULL
;
723 //==========================================================================
725 static BVRef
diskScanAPMBootVolumes( int biosdev
, int * countPtr
)
727 struct DiskBVMap
* map
;
728 struct Block0
*block0_p
;
729 unsigned int blksize
;
731 void *buffer
= malloc(BPS
);
733 /* Check for alternate block size */
734 if (readBytes( biosdev
, 0, BPS
, buffer
) != 0) {
738 if (OSSwapBigToHostInt16(block0_p
->sbSig
) == BLOCK0_SIGNATURE
) {
739 blksize
= OSSwapBigToHostInt16(block0_p
->sbBlkSize
);
740 if (blksize
!= BPS
) {
742 buffer
= malloc(blksize
);
744 factor
= blksize
/ BPS
;
751 // Create a new mapping.
753 map
= (struct DiskBVMap
*) malloc( sizeof(*map
) );
757 DPME
*dpme_p
= (DPME
*)buffer
;
758 UInt32 i
, npart
= UINT_MAX
;
761 map
->biosdev
= biosdev
;
764 map
->next
= gDiskBVMap
;
767 for (i
=0; i
<npart
; i
++) {
768 error
= readBytes( biosdev
, (kAPMSector
+ i
) * factor
, blksize
, buffer
);
770 if (error
|| OSSwapBigToHostInt16(dpme_p
->dpme_signature
) != DPME_SIGNATURE
) {
775 npart
= OSSwapBigToHostInt32(dpme_p
->dpme_map_entries
);
778 printf("name = %s, %s%s %d -> %d [%d -> %d] {%d}\n",
779 dpme.dpme_name, dpme.dpme_type, (dpme.dpme_flags & DPME_FLAGS_BOOTABLE) ? "(bootable)" : "",
780 dpme.dpme_pblock_start, dpme.dpme_pblocks,
781 dpme.dpme_lblock_start, dpme.dpme_lblocks,
782 dpme.dpme_boot_block);
785 if (strcmp(dpme_p
->dpme_type
, "Apple_HFS") == 0) {
786 bvr
= newAPMBVRef(biosdev
,
788 OSSwapBigToHostInt32(dpme_p
->dpme_pblock_start
) * factor
,
797 kBIOSDevTypeHardDrive
);
798 bvr
->next
= map
->bvr
;
808 if (countPtr
) *countPtr
= map
? map
->bvrcnt
: 0;
810 return map
? map
->bvr
: NULL
;
813 //==========================================================================
815 BVRef
diskScanBootVolumes( int biosdev
, int * countPtr
)
817 struct DiskBVMap
* map
;
821 // Find an existing mapping for this device.
823 for ( map
= gDiskBVMap
; map
; map
= map
->next
) {
824 if ( biosdev
== map
->biosdev
) {
831 bvr
= diskScanFDiskBootVolumes(biosdev
, &count
);
833 bvr
= diskScanAPMBootVolumes(biosdev
, &count
);
838 if (countPtr
) *countPtr
= count
;
843 //==========================================================================
845 static const struct NamedValue fdiskTypes
[] =
847 { FDISK_NTFS
, "Windows NTFS" },
848 { FDISK_FAT32
, "Windows FAT32" },
850 { FDISK_UFS
, "Apple UFS" },
851 { FDISK_HFS
, "Apple HFS" },
852 { FDISK_BOOTER
, "Apple Boot/UFS" },
854 { 0x00, 0 } /* must be last */
857 //==========================================================================
859 void getBootVolumeDescription( BVRef bvr
, char * str
, long strMaxLen
, BOOL verbose
)
861 unsigned char type
= (unsigned char) bvr
->part_type
;
862 const char * name
= getNameForValue( fdiskTypes
, type
);
866 name
= bvr
->type_name
;
869 if ( name
&& verbose
) {
870 sprintf( str
, "hd(%d,%d) ",
871 BIOS_DEV_UNIT(bvr
), bvr
->part_no
);
872 for (; strMaxLen
> 0 && *p
!= '\0'; p
++, strMaxLen
--);
876 bvr
->description(bvr
, p
, strMaxLen
);
878 const char * name
= getNameForValue( fdiskTypes
, type
);
880 name
= bvr
->type_name
;
883 sprintf(p
, "TYPE %02x", type
);
885 strncpy(p
, name
, strMaxLen
);
891 //==========================================================================
894 getFAT32VolumeDescription( BVRef bvr
, char *str
, long strMaxLen
)
896 struct fat32_header
{
897 unsigned char code
[3];
898 unsigned char oem_id
[8];
899 unsigned char data
[56];
900 unsigned long serial
;
901 unsigned char label
[11];
902 unsigned char fsid
[8];
903 unsigned char reserved
[420];
904 unsigned short signature
;
905 } __attribute__((packed
));
908 struct fat32_header
*fat32_p
;
909 int label_len
= sizeof(fat32_p
->label
);
912 buf
= (char *)malloc(BPS
);
913 name
= (char *)malloc(label_len
+ 1);
914 fat32_p
= (struct fat32_header
*)buf
;
917 error
= diskRead(bvr
, (long)buf
, BPS
);
918 if ( error
) return 0;
920 if (fat32_p
->signature
!= 0xaa55) return 0;
922 if (strMaxLen
< label_len
) label_len
= strMaxLen
;
923 strncpy(str
, fat32_p
->label
, label_len
);
924 str
[label_len
] = '\0';
929 //==========================================================================
931 static void getVolumeDescription( BVRef bvr
, char * str
, long strMaxLen
)
933 unsigned char type
= (unsigned char) bvr
->part_type
;
934 const char * name
= NULL
;
936 /* First try a few types that we can figure out the
937 * volume description.
942 MSDOSGetDescription(bvr
, str
, strMaxLen
);
947 default: // Not one of our known types
952 name
= getNameForValue( fdiskTypes
, type
);
955 name
= bvr
->type_name
;
958 strncpy( str
, name
, strMaxLen
);
960 sprintf( str
, "TYPE %02x", type
);
964 //==========================================================================
966 int readBootSector( int biosdev
, unsigned int secno
, void * buffer
)
968 struct disk_blk0
* bootSector
= (struct disk_blk0
*) buffer
;
971 if ( bootSector
== NULL
)
973 if ( gBootSector
== NULL
)
975 gBootSector
= (struct disk_blk0
*) malloc(sizeof(*gBootSector
));
976 if ( gBootSector
== NULL
) return -1;
978 bootSector
= gBootSector
;
981 error
= readBytes( biosdev
, secno
, BPS
, bootSector
);
982 if ( error
|| bootSector
->signature
!= DISK_SIGNATURE
)
988 //==========================================================================
989 // Handle seek request from filesystem modules.
991 void diskSeek( BVRef bvr
, long long position
)
993 bvr
->fs_boff
= position
/ BPS
;
996 //==========================================================================
997 // Handle read request from filesystem modules.
999 int diskRead( BVRef bvr
, long addr
, long length
)
1001 return readBytes( bvr
->biosdev
,
1002 bvr
->fs_boff
+ bvr
->part_boff
,
1007 int rawDiskRead( BVRef bvr
, unsigned int secno
, void *buffer
, unsigned int len
)
1010 unsigned char *cbuf
= (unsigned char *)buffer
;
1011 unsigned int copy_len
;
1014 if ((len
& (BPS
-1)) != 0) {
1015 error("raw disk read not sector aligned");
1018 secno
+= bvr
->part_boff
;
1020 cache_valid
= FALSE
;
1024 if (secs
> N_CACHE_SECS
) secs
= N_CACHE_SECS
;
1025 copy_len
= secs
* BPS
;
1027 //printf("rdr: ebiosread(%d, %d, %d)\n", bvr->biosdev, secno, secs);
1028 if ((rc
= ebiosread(bvr
->biosdev
, secno
, secs
)) != 0) {
1029 /* Ignore corrected ECC errors */
1030 if (rc
!= ECC_CORRECTED_ERR
) {
1031 error(" EBIOS read error: %s\n", bios_error(rc
), rc
);
1032 error(" Block %d Sectors %d\n", secno
, secs
);
1036 bcopy( trackbuf
, cbuf
, copy_len
);
1040 spinActivityIndicator();
1046 int rawDiskWrite( BVRef bvr
, unsigned int secno
, void *buffer
, unsigned int len
)
1049 unsigned char *cbuf
= (unsigned char *)buffer
;
1050 unsigned int copy_len
;
1053 if ((len
& (BPS
-1)) != 0) {
1054 error("raw disk write not sector aligned");
1057 secno
+= bvr
->part_boff
;
1059 cache_valid
= FALSE
;
1063 if (secs
> N_CACHE_SECS
) secs
= N_CACHE_SECS
;
1064 copy_len
= secs
* BPS
;
1066 bcopy( cbuf
, trackbuf
, copy_len
);
1067 //printf("rdr: ebioswrite(%d, %d, %d)\n", bvr->biosdev, secno, secs);
1068 if ((rc
= ebioswrite(bvr
->biosdev
, secno
, secs
)) != 0) {
1069 error(" EBIOS write error: %s\n", bios_error(rc
), rc
);
1070 error(" Block %d Sectors %d\n", secno
, secs
);
1076 spinActivityIndicator();
1082 void turnOffFloppy(void)
1086 * Hold controller in reset,
1087 * disable DMA and IRQ,
1088 * turn off floppy motors.