FreeDOS/kernel/dsk.c
2001-07-09 22:19:33 +00:00

914 lines
27 KiB
C

/****************************************************************/
/* */
/* dsk.c */
/* */
/* Copyright (c) 1995 */
/* Pasquale J. Villani */
/* All Rights Reserved */
/* */
/* This file is part of DOS-C. */
/* */
/* DOS-C is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU General Public License */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version. */
/* */
/* DOS-C is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
/* the GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public */
/* License along with DOS-C; see the file COPYING. If not, */
/* write to the Free Software Foundation, 675 Mass Ave, */
/* Cambridge, MA 02139, USA. */
/****************************************************************/
#include "portab.h"
#include "globals.h"
#include "disk.h"
#ifdef VERSION_STRINGS
static BYTE *dskRcsId = "$Id$";
#endif
/*
* $Log$
* Revision 1.17 2001/07/09 22:19:33 bartoldeman
* LBA/FCB/FAT/SYS/Ctrl-C/ioctl fixes + memory savings
*
* Revision 1.17 2001/05/13 tomehlert
* Added full support for LBA hard drives
* initcode moved (mostly) to initdisk.c
* lower interface partly redesigned
*
* Revision 1.16 2001/04/29 brianreifsnyder
* Added phase 1 support for LBA hard drives
*
* Revision 1.15 2001/04/16 01:45:26 bartoldeman
* Fixed handles, config.sys drivers, warnings. Enabled INT21/AH=6C, printf %S/%Fs
*
* Revision 1.14 2001/04/15 03:21:50 bartoldeman
* See history.txt for the list of fixes.
*
* Revision 1.13 2001/03/27 20:27:43 bartoldeman
* dsk.c (reported by Nagy Daniel), inthndlr and int25/26 fixes by Tom Ehlert.
*
* Revision 1.12 2001/03/24 22:13:05 bartoldeman
* See history.txt: dsk.c changes, warning removal and int21 entry handling.
*
* Revision 1.11 2001/03/21 02:56:25 bartoldeman
* See history.txt for changes. Bug fixes and HMA support are the main ones.
*
* Revision 1.9 2001/03/08 21:15:00 bartoldeman
* Space saving fixes from Tom Ehlert
*
* Revision 1.8 2000/06/21 18:16:46 jimtabor
* Add UMB code, patch, and code fixes
*
* Revision 1.7 2000/06/01 06:37:38 jimtabor
* Read History for Changes
*
* Revision 1.6 2000/05/26 19:25:19 jimtabor
* Read History file for Change info
*
* Revision 1.5 2000/05/25 20:56:21 jimtabor
* Fixed project history
*
* Revision 1.4 2000/05/17 19:15:12 jimtabor
* Cleanup, add and fix source.
*
* Revision 1.3 2000/05/11 04:26:26 jimtabor
* Added code for DOS FN 69 & 6C
*
* Revision 1.2 2000/05/08 04:29:59 jimtabor
* Update CVS to 2020
*
* Revision 1.1.1.1 2000/05/06 19:34:53 jhall1
* The FreeDOS Kernel. A DOS kernel that aims to be 100% compatible with
* MS-DOS. Distributed under the GNU GPL.
*
* Revision 1.6 2000/04/29 05:13:16 jtabor
* Added new functions and clean up code
*
* Revision 1.5 2000/03/09 06:07:11 kernel
* 2017f updates by James Tabor
*
* Revision 1.4 1999/08/10 18:07:57 jprice
* ror4 2011-04 patch
*
* Revision 1.3 1999/04/16 21:43:40 jprice
* ror4 multi-sector IO
*
* Revision 1.2 1999/04/16 00:53:32 jprice
* Optimized FAT handling
*
* Revision 1.1.1.1 1999/03/29 15:40:51 jprice
* New version without IPL.SYS
*
* Revision 1.5 1999/02/14 04:26:46 jprice
* Changed check media so that it checks if a floppy disk has been changed.
*
* Revision 1.4 1999/02/08 05:55:57 jprice
* Added Pat's 1937 kernel patches
*
* Revision 1.3 1999/02/01 01:48:41 jprice
* Clean up; Now you can use hex numbers in config.sys. added config.sys screen function to change screen mode (28 or 43/50 lines)
*
* Revision 1.2 1999/01/22 04:13:25 jprice
* Formating
*
* Revision 1.1.1.1 1999/01/20 05:51:01 jprice
* Imported sources
*
*
* Rev 1.7 06 Dec 1998 8:45:18 patv
* Changed due to new I/O subsystem.
*
* Rev 1.6 04 Jan 1998 23:15:16 patv
* Changed Log for strip utility
*
* Rev 1.5 10 Jan 1997 5:41:48 patv
* Modified for extended partition support
*
* Rev 1.4 29 May 1996 21:03:32 patv
* bug fixes for v0.91a
*
* Rev 1.3 19 Feb 1996 3:21:36 patv
* Added NLS, int2f and config.sys processing
*
* Rev 1.2 01 Sep 1995 17:54:18 patv
* First GPL release.
*
* Rev 1.1 30 Jul 1995 20:52:00 patv
* Eliminated version strings in ipl
*
* Rev 1.0 02 Jul 1995 8:32:42 patv
* Initial revision.
*/
#if defined(DEBUG)
#define DebugPrintf(x) printf x
#else
#define DebugPrintf(x)
#endif
#define STATIC
#ifdef PROTO
BOOL fl_reset(WORD);
COUNT fl_readdasd(WORD);
COUNT fl_diskchanged(WORD);
COUNT fl_rd_status(WORD);
COUNT fl_read(WORD, WORD, WORD, WORD, WORD, BYTE FAR *);
COUNT fl_write(WORD, WORD, WORD, WORD, WORD, BYTE FAR *);
COUNT fl_verify(WORD, WORD, WORD, WORD, WORD, BYTE FAR *);
extern COUNT fl_lba_ReadWrite (BYTE drive, WORD mode,
struct _bios_LBA_address_packet FAR *dap_p);
int LBA_Transfer(struct DriveParamS *driveParam ,UWORD mode, VOID FAR *buffer,
ULONG LBA_address,unsigned total, UWORD *transferred);
#else
BOOL fl_reset();
COUNT fl_readdasd();
COUNT fl_diskchanged();
COUNT fl_rd_status();
COUNT fl_read();
COUNT fl_write();
COUNT fl_verify();
#endif
#define NENTRY 26 /* total size of dispatch table */
extern BYTE FAR nblk_rel;
#define LBA_READ 0x4200
#define LBA_WRITE 0x4300
UWORD LBA_WRITE_VERIFY = 0x4302;
#define LBA_VERIFY 0x4400
extern void __int__(int);
/* this buffer must not overlap a 64K boundary
due to DMA transfers
this is certainly true, if located somewhere
at 0xf+1000 and must hold already during BOOT time
*/
UBYTE DiskTransferBuffer[1 * SEC_SIZE];
static struct Access_info
{
BYTE AI_spec;
BYTE AI_Flag;
};
struct media_info *miarrayptr; /* Internal media info structs */
bpb bpbarray[NDEV]; /* BIOS parameter blocks */
/* STATIC bpb *bpbptrs[NDEV]; pointers to bpbs */
/*TE - array access functions */
struct media_info *getPMiarray(int dev) { return &miarrayptr[dev];}
bpb *getPBpbarray(unsigned dev){ return &bpbarray[dev];}
#define N_PART 4 /* number of partitions per
table partition */
COUNT nUnits; /* number of returned units */
#define PARTOFF 0x1be
#ifdef PROTO
WORD
_dsk_init (rqptr rq, struct media_info *pmiarray),
mediachk (rqptr rq, struct media_info *pmiarray),
bldbpb (rqptr rq, struct media_info *pmiarray),
blockio (rqptr rq, struct media_info *pmiarray),
IoctlQueblk(rqptr rq, struct media_info *pmiarray),
Genblkdev (rqptr rq, struct media_info *pmiarray),
Getlogdev (rqptr rq, struct media_info *pmiarray),
Setlogdev (rqptr rq, struct media_info *pmiarray),
blk_Open (rqptr rq, struct media_info *pmiarray),
blk_Close (rqptr rq, struct media_info *pmiarray),
blk_Media (rqptr rq, struct media_info *pmiarray),
blk_noerr (rqptr rq, struct media_info *pmiarray),
blk_nondr (rqptr rq, struct media_info *pmiarray),
blk_error (rqptr rq, struct media_info *pmiarray);
WORD dskerr(COUNT);
#else
WORD _dsk_init(),
mediachk(),
bldbpb(),
blockio(),
IoctlQueblk(),
Genblkdev(),
Getlogdev(),
Setlogdev(),
blk_Open(),
blk_Close(),
blk_Media(),
blk_noerr(),
blk_nondr(),
blk_error();
WORD dskerr();
#endif
/* */
/* the function dispatch table */
/* */
#ifdef PROTO
static WORD(*dispatch[NENTRY]) (rqptr rq, struct media_info *pmiarray) =
#else
static WORD(*dispatch[NENTRY]) () =
#endif
{
_dsk_init, /* Initialize */
mediachk, /* Media Check */
bldbpb, /* Build BPB */
blk_error, /* Ioctl In */
blockio, /* Input (Read) */
blk_nondr, /* Non-destructive Read */
blk_noerr, /* Input Status */
blk_noerr, /* Input Flush */
blockio, /* Output (Write) */
blockio, /* Output with verify */
blk_noerr, /* Output Status */
blk_noerr, /* Output Flush */
blk_error, /* Ioctl Out */
blk_Open, /* Device Open */
blk_Close, /* Device Close */
blk_Media, /* Removable Media */
blk_noerr, /* Output till busy */
blk_error, /* undefined */
blk_error, /* undefined */
Genblkdev, /* Generic Ioctl Call */
blk_error, /* undefined */
blk_error, /* undefined */
blk_error, /* undefined */
Getlogdev, /* Get Logical Device */
Setlogdev, /* Set Logical Device */
IoctlQueblk /* Ioctl Query */
};
#define hd(x) ((x) & 0x80)
/* ----------------------------------------------------------------------- */
/* F U N C T I O N S --------------------------------------------------- */
/* ----------------------------------------------------------------------- */
COUNT FAR blk_driver(rqptr rp)
{
if (rp->r_unit >= nUnits && rp->r_command != C_INIT)
return failure(E_UNIT);
if (rp->r_command > NENTRY)
{
return failure(E_FAILURE); /* general failure */
}
else
return ((*dispatch[rp->r_command]) (rp, getPMiarray(rp->r_unit)));
}
/* disk init is done in diskinit.c, so this should never be called */
WORD _dsk_init(rqptr rp, struct media_info *pmiarray)
{
UNREFERENCED_PARAMETER(rp);
UNREFERENCED_PARAMETER(pmiarray);
fatal("No disk init!");
return S_DONE; /* to keep the compiler happy */
}
WORD mediachk(rqptr rp, struct media_info *pmiarray)
{
COUNT drive = pmiarray->drive.driveno;
COUNT result;
/* if it's a hard drive, media never changes */
if (hd(drive))
rp->r_mcretcode = M_NOT_CHANGED;
else
/* else, check floppy status */
{
if ((result = fl_readdasd(drive)) == 2) /* if we can detect a change ... */
{
if ((result = fl_diskchanged(drive)) == 1) /* check if it has changed... */
rp->r_mcretcode = M_CHANGED;
else if (result == 0)
rp->r_mcretcode = M_NOT_CHANGED;
else
rp->r_mcretcode = tdelay((LONG) 37) ? M_DONT_KNOW : M_NOT_CHANGED;
}
else if (result == 3) /* if it's a fixed disk, then no change */
rp->r_mcretcode = M_NOT_CHANGED;
else /* can not detect or error... */
rp->r_mcretcode = tdelay((LONG) 37) ? M_DONT_KNOW : M_NOT_CHANGED;
}
return S_DONE;
}
/*
* Read Write Sector Zero or Hard Drive Dos Bpb
*/
STATIC WORD RWzero(rqptr rp, WORD t)
{
struct media_info *pmiarray = getPMiarray(rp->r_unit);
UWORD done;
return LBA_Transfer(&pmiarray->drive,
t == 0 ? LBA_READ : LBA_WRITE,
(UBYTE FAR *)&DiskTransferBuffer,
pmiarray->mi_offset,1,&done);
}
/*
0 if not set, 1 = a, 2 = b, etc, assume set.
page 424 MS Programmer's Ref.
*/
static WORD Getlogdev(rqptr rp, struct media_info *pmiarray)
{
BYTE x = rp->r_unit;
UNREFERENCED_PARAMETER(pmiarray);
x++;
if( x > nblk_rel )
return failure(E_UNIT);
rp->r_unit = x;
return S_DONE;
}
static WORD Setlogdev(rqptr rp, struct media_info *pmiarray)
{
UNREFERENCED_PARAMETER(rp);
UNREFERENCED_PARAMETER(pmiarray);
return S_DONE;
}
static WORD blk_Open(rqptr rp, struct media_info *pmiarray)
{
UNREFERENCED_PARAMETER(rp);
pmiarray->mi_FileOC++;
return S_DONE;
}
static WORD blk_Close(rqptr rp, struct media_info *pmiarray)
{
UNREFERENCED_PARAMETER(rp);
pmiarray->mi_FileOC--;
return S_DONE;
}
static WORD blk_nondr(rqptr rp, struct media_info *pmiarray)
{
UNREFERENCED_PARAMETER(rp);
UNREFERENCED_PARAMETER(pmiarray);
return S_BUSY|S_DONE;
}
static WORD blk_Media(rqptr rp, struct media_info *pmiarray)
{
UNREFERENCED_PARAMETER(rp);
if (hd( pmiarray->drive.driveno))
return S_BUSY|S_DONE; /* Hard Drive */
else
return S_DONE; /* Floppy */
}
STATIC WORD bldbpb(rqptr rp, struct media_info *pmiarray)
{
ULONG count;
bpb *pbpbarray;
WORD head,/*track,*/sector,ret;
ret = RWzero( rp, 0);
if (ret != 0)
return (dskerr(ret));
/*TE ~ 200 bytes*/
pbpbarray = getPBpbarray(rp->r_unit);
getword(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_NBYTE]), &pbpbarray->bpb_nbyte);
getbyte(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_NSECTOR]), &pbpbarray->bpb_nsector);
getword(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_NRESERVED]), &pbpbarray->bpb_nreserved);
getbyte(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_NFAT]), &pbpbarray->bpb_nfat);
getword(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_NDIRENT]), &pbpbarray->bpb_ndirent);
getword(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_NSIZE]), &pbpbarray->bpb_nsize);
getword(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_NSIZE]), &pbpbarray->bpb_nsize);
getbyte(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_MDESC]), &pbpbarray->bpb_mdesc);
getword(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_NFSECT]), &pbpbarray->bpb_nfsect);
getword(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_NSECS]), &pbpbarray->bpb_nsecs);
getword(&((((BYTE *) & DiskTransferBuffer[BT_BPB]))[BPB_NHEADS]), &pbpbarray->bpb_nheads);
getlong(&((((BYTE *) & DiskTransferBuffer[BT_BPB])[BPB_HIDDEN])), &pbpbarray->bpb_hidden);
getlong(&((((BYTE *) & DiskTransferBuffer[BT_BPB])[BPB_HUGE])), &pbpbarray->bpb_huge);
/* Needs fat32 offset code */
getlong(&((((BYTE *) & DiskTransferBuffer[0x27])[0])), &pmiarray->fs.serialno);
memcpy(pmiarray->fs.volume,&DiskTransferBuffer[0x2B], 11);
memcpy(pmiarray->fs.fstype,&DiskTransferBuffer[0x36], 8);
#ifdef DSK_DEBUG
printf("BPB_NBYTE = %04x\n", pbpbarray->bpb_nbyte);
printf("BPB_NSECTOR = %02x\n", pbpbarray->bpb_nsector);
printf("BPB_NRESERVED = %04x\n", pbpbarray->bpb_nreserved);
printf("BPB_NFAT = %02x\n", pbpbarray->bpb_nfat);
printf("BPB_NDIRENT = %04x\n", pbpbarray->bpb_ndirent);
printf("BPB_NSIZE = %04x\n", pbpbarray->bpb_nsize);
printf("BPB_MDESC = %02x\n", pbpbarray->bpb_mdesc);
printf("BPB_NFSECT = %04x\n", pbpbarray->bpb_nfsect);
#endif
rp->r_bpptr = pbpbarray;
count = pmiarray->mi_size =
pbpbarray->bpb_nsize == 0 ?
pbpbarray->bpb_huge :
pbpbarray->bpb_nsize;
getword((&(((BYTE *) & DiskTransferBuffer[BT_BPB])[BPB_NHEADS])), &pmiarray->drive.chs.Head);
head = pmiarray->drive.chs.Head;
getword((&(((BYTE *) & DiskTransferBuffer[BT_BPB])[BPB_NSECS])), &pmiarray->drive.chs.Sector);
if (pmiarray->mi_size == 0)
getlong(&((((BYTE *) & DiskTransferBuffer[BT_BPB])[BPB_HUGE])), &pmiarray->mi_size);
sector = pmiarray->drive.chs.Sector;
if (head == 0 || sector == 0)
{
tmark();
return failure(E_FAILURE);
}
pmiarray->drive.chs.Cylinder = count / (head * sector);
tmark();
#ifdef DSK_DEBUG
printf("BPB_NSECS = %04x\n", sector);
printf("BPB_NHEADS = %04x\n", head);
printf("BPB_HIDDEN = %08lx\n", pbpbarray->bpb_hidden);
printf("BPB_HUGE = %08lx\n", pbpbarray->bpb_huge);
#endif
return S_DONE;
}
static WORD IoctlQueblk(rqptr rp, struct media_info *pmiarray)
{
UNREFERENCED_PARAMETER(pmiarray);
switch(rp->r_count){
case 0x0846:
case 0x0847:
case 0x0860:
case 0x0866:
case 0x0867:
break;
default:
return failure(E_CMD);
}
return S_DONE;
}
STATIC WORD Genblkdev(rqptr rp,struct media_info *pmiarray)
{
int ret;
switch(rp->r_count){
case 0x0860: /* get device parameters */
{
struct gblkio FAR * gblp = (struct gblkio FAR *) rp->r_trans;
REG COUNT x = 5,y = 1,z = 0;
if (!hd(pmiarray->drive.driveno)){
y = 2;
x = 8; /* any odd ball drives return this */
if (pmiarray->mi_size <= 0xffff)
switch((UWORD)pmiarray->mi_size)
{
case 640:
case 720: /* 320-360 */
x = 0;
z = 1;
break;
case 1440: /* 720 */
x = 2;
break;
case 2400: /* 1.2 */
x = 1;
break;
case 2880: /* 1.44 */
x = 7;
break;
case 5760: /* 2.88 almost forgot this one*/
x = 9;
break;
}
}
gblp->gbio_devtype = (UBYTE) x;
gblp->gbio_devattrib = (UWORD) y;
gblp->gbio_media = (UBYTE) z;
gblp->gbio_ncyl = pmiarray->drive.chs.Cylinder;
fmemcpy(&gblp->gbio_bpb, &bpbarray[rp->r_unit], sizeof(gblp->gbio_bpb));
gblp->gbio_nsecs = bpbarray[rp->r_unit].bpb_nsector;
break;
}
case 0x0866: /* get volume serial number */
{
struct Gioc_media FAR * gioc = (struct Gioc_media FAR *) rp->r_trans;
struct FS_info FAR * fs = &pmiarray->fs;
gioc->ioc_serialno = fs->serialno;
fmemcpy(gioc->ioc_volume,fs->volume,11);
fmemcpy(gioc->ioc_fstype, fs->fstype,8);
}
break;
case 0x0846: /* set volume serial number */
{
struct Gioc_media FAR * gioc = (struct Gioc_media FAR *) rp->r_trans;
struct FS_info FAR * fs = (struct FS_info FAR *) &DiskTransferBuffer[0x27];
ret = RWzero( rp, 0);
if (ret != 0)
return (dskerr(ret));
fs->serialno = gioc->ioc_serialno;
pmiarray->fs.serialno = fs->serialno;
ret = RWzero( rp, 1);
if (ret != 0)
return (dskerr(ret));
}
break;
case 0x0867: /* get access flag, always on*/
{
struct Access_info FAR * ai = (struct Access_info FAR *) rp->r_trans;
ai->AI_Flag = 1;
}
break;
case 0x0847: /* set access flag, no real use*/
break;
default:
return failure(E_CMD);
}
return S_DONE;
}
WORD blockio(rqptr rp, struct media_info *pmiarray)
{
ULONG start;
WORD ret;
int action;
switch (rp->r_command){
case C_INPUT: action = LBA_READ; break;
case C_OUTPUT:action = LBA_WRITE; break;
case C_OUTVFY:action = LBA_WRITE_VERIFY; break;
default:
return failure(E_FAILURE);
}
tmark();
start = (rp->r_start != HUGECOUNT ? rp->r_start : rp->r_huge);
if (start >= pmiarray->mi_size ||
start + rp->r_count > pmiarray->mi_size)
{
return 0x0408;
}
start += pmiarray->mi_offset;
ret = LBA_Transfer(&pmiarray->drive ,action,
rp->r_trans,
start, rp->r_count,(UWORD*)&rp->r_count);
if (ret != 0)
{
return dskerr(ret);
}
return S_DONE;
}
STATIC WORD blk_error(rqptr rp, struct media_info *pmiarray)
{
UNREFERENCED_PARAMETER(pmiarray);
rp->r_count = 0;
return failure(E_FAILURE); /* general failure */
}
STATIC WORD blk_noerr(rqptr rp, struct media_info *pmiarray)
{
UNREFERENCED_PARAMETER(rp);
UNREFERENCED_PARAMETER(pmiarray);
return S_DONE;
}
STATIC WORD dskerr(COUNT code)
{
/* printf("diskette error:\nhead = %d\ntrack = %d\nsector = %d\ncount = %d\n",
head, track, sector, count); */
switch (code & 0x03)
{
case 1: /* invalid command - general failure */
if (code & 0x08)
return S_ERROR | E_NOTRDY; /* failure(E_NOTRDY); at least on yhe INT25 route,
0x8002 is returned */
else
return failure(E_CMD);
case 2: /* address mark not found - general failure */
return failure(E_FAILURE);
case 3: /* write protect */
return failure(E_WRPRT);
default:
if (code & 0x80) /* time-out */
return failure(E_NOTRDY);
else if (code & 0x40) /* seek error */
return failure(E_SEEK);
else if (code & 0x10) /* CRC error */
return failure(E_CRC);
else if (code & 0x04)
return failure(E_NOTFND);
else
return failure(E_FAILURE);
}
}
/*
translate LBA sectors into CHS addressing
*/
void LBA_to_CHS(struct CHS *chs, ULONG LBA_address, struct DriveParamS *driveparam)
{
chs->Sector = LBA_address% driveparam->chs.Sector + 1;
LBA_address /= driveparam->chs.Sector;
chs->Head = LBA_address % driveparam->chs.Head;
chs->Cylinder = LBA_address / driveparam->chs.Head;
}
/* Test for 64K boundary crossing and return count small */
/* enough not to exceed the threshold. */
STATIC unsigned DMA_max_transfer(void FAR *buffer, unsigned count)
{
UWORD utemp = (((UWORD) FP_SEG(buffer) << 4) + FP_OFF(buffer));
#define SEC_SHIFT 9 /* = 0x200 = 512 */
utemp >>= SEC_SHIFT;
if (count > (0xffff >> SEC_SHIFT) - utemp)
{
count = (0xffff >> SEC_SHIFT) - utemp;
}
return count;
}
/*
int LBA_Transfer(
struct DriveParamS *driveParam, physical characteristics of drive
UWORD mode, LBA_READ/WRITE/WRITE_VERIFY
VOID FAR *buffer, user buffer
ULONG LBA_address, absolute sector address
unsigned totaltodo, number of sectors to transfer
UWORD *transferred sectors actually transferred
Read/Write/Write+verify some sectors, using LBA addressing.
This function handles all the minor details, including:
retry in case of errors
crossing the 64K DMA boundary
translation to CHS addressing if necessary
crossing track boundaries (necessary for some BIOS's
High memory doesn't work very well, use internal buffer
write with verify details for LBA
*/
int LBA_Transfer(struct DriveParamS *driveParam ,UWORD mode, VOID FAR *buffer,
ULONG LBA_address,unsigned totaltodo, UWORD *transferred)
{
static struct _bios_LBA_address_packet dap = {
16,0,0,0,0,0,0
};
unsigned count;
unsigned error_code;
struct CHS chs;
void FAR *transfer_address;
int num_retries;
*transferred = 0;
if (LBA_address+totaltodo > driveParam->total_sectors)
{
printf("LBA-Transfer error : address overflow = %lu > %lu max\n",LBA_address+totaltodo,driveParam->total_sectors);
return 1;
}
for ( ;totaltodo != 0; )
{
count = totaltodo;
count = min(count, 0x7f);
/* avoid overflowing 64K DMA boundary */
count = DMA_max_transfer(buffer,count);
if (FP_SEG(buffer) == 0xffff || count == 0)
{
transfer_address = DiskTransferBuffer;
count = 1;
if ((mode & 0xff00) == (LBA_WRITE & 0xff00))
{
fmemcpy(DiskTransferBuffer,buffer,512);
}
}
else {
transfer_address = buffer;
}
for ( num_retries = 0; num_retries < N_RETRY; num_retries++)
{
if (driveParam->LBA_supported)
{
dap.number_of_blocks = count;
dap.buffer_address = transfer_address;
dap.block_address_high = 0; /* clear high part */
dap.block_address = LBA_address; /* clear high part */
/* Load the registers and call the interrupt. */
if (driveParam->WriteVerifySupported || mode != LBA_WRITE_VERIFY)
{
error_code = fl_lba_ReadWrite(driveParam->driveno,mode, &dap);
}
else {
/* verify requested, but not supported */
error_code = fl_lba_ReadWrite(driveParam->driveno,LBA_WRITE, &dap);
if (error_code == 0)
{
error_code = fl_lba_ReadWrite(driveParam->driveno,LBA_VERIFY, &dap);
}
}
}
else
{ /* transfer data, using old bios functions */
LBA_to_CHS(&chs, LBA_address, driveParam);
/* avoid overflow at end of track */
if (chs.Sector + count > driveParam->chs.Sector + 1)
{
count = driveParam->chs.Sector + 1 - chs.Sector;
}
if (chs.Cylinder > 1023)
{
printf("LBA-Transfer error : cylinder %u > 1023\n", chs.Cylinder);
return 1;
}
error_code = (mode == LBA_READ ? fl_read : fl_write)(
driveParam->driveno,
chs.Head, chs.Cylinder, chs.Sector,
count, transfer_address);
if (error_code == 0 &&
mode == LBA_WRITE_VERIFY)
{
error_code = fl_verify(
driveParam->driveno,
chs.Head, chs.Cylinder, chs.Sector,
count, transfer_address);
}
}
if (error_code == 0)
break;
fl_reset(driveParam->driveno);
} /* end of retries */
if (error_code)
{
return error_code;
}
/* copy to user buffer if nesessary */
if (transfer_address == DiskTransferBuffer &&
(mode & 0xff00) == (LBA_READ & 0xff00))
{
fmemcpy(buffer,DiskTransferBuffer,512);
}
*transferred += count;
LBA_address += count;
totaltodo -= count;
buffer = add_far(buffer,count*512);
}
return(error_code);
}