/* * Copyright (C) 2008,2019,2020,2021 Klaus Michael Indlekofer. All rights reserved. * * m.indlekofer@gmx.de * * This program 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 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "commoninclude.h" #include "akaiutil_io.h" #ifdef _VISUALCPP /* for low-level floppy I/O, using fdrawcmd.sys */ #include "fdrawcmd.h" /* from http://simonowen.com/fdrawcmd/fdrawcmd.h */ HANDLE fldr_h[FLDRNUM]; /* handle for floppy drive */ u_int fldr_type[FLDRNUM]; /* floppy drive type */ PBYTE fldr_secbuf; /* DMA buffer for sector */ int fldr_init(void) { u_int i; for (i=0;i=FLDRNUM)){ PRINTF_ERR("fldr_open: invalid floppy drive number\n"); return -1; } if (fldr_h[fldrn]!=INVALID_HANDLE_VALUE){ PRINTF_ERR("fldr_open: floppy drive already opened\n"); return -1; } /* check driver */ version=0; /* invalid */ /* Note: must use GENERIC_READ|GENERIC_WRITE for DeviceIoControl() */ h=CreateFileA("\\\\.\\fdrawcmd",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); if (h!=INVALID_HANDLE_VALUE){ if (!DeviceIoControl(h,IOCTL_FDRAWCMD_GET_VERSION,NULL,0,&version,sizeof(version),&ret,NULL)){ version=0; /* invalid */ } CloseHandle(h); } if (version==0){ PRINTF_ERR("fldr_open: fdrawcmd.sys is not installed, see: http://simonowen.com/fdrawcmd/\n"); return -1; } if (HIWORD(version)!=HIWORD(FDRAWCMD_VERSION)){ PRINTF_ERR("fldr_open: the installed fdrawcmd.sys is not compatible with this program\n"); return -1; } /* open and init device */ sprintf(devname,"\\\\.\\fdraw%u",(u_int)fldrn); /* Note: must use GENERIC_READ|GENERIC_WRITE for DeviceIoControl() */ h=CreateFileA(devname,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); if (h==INVALID_HANDLE_VALUE){ PRINTF_ERR("fldr_open: cannot open floppy drive\n"); return -1; } if (fldrtype==FLDR_TYPE_FLH){ datarate=AKAI_FLH_DISK_DATARATE; }else{ datarate=AKAI_FLL_DISK_DATARATE; } if (!DeviceIoControl(h,IOCTL_FD_SET_DATA_RATE,&datarate,sizeof(datarate),NULL,0,&ret,NULL)){ PRINTF_ERR("fldr_open: cannot set floppy data rate\n"); CloseHandle(h); return -1; } if (!DeviceIoControl(h,IOCTL_FD_RESET,NULL,0,NULL,0,&ret,NULL)){ PRINTF_ERR("fldr_open: cannot reset floppy controller\n"); CloseHandle(h); return -1; } /* save handle and type */ fldr_h[fldrn]=h; fldr_type[fldrn]=fldrtype; return 0; } int fldr_checkfloppyinserted(int fldrn) { DWORD ret; if ((fldrn<0)||(fldrn>=FLDRNUM)||(fldr_h[fldrn]==INVALID_HANDLE_VALUE)){ return -1; } /* check if no floppy inserted */ if (!DeviceIoControl(fldr_h[fldrn],IOCTL_FD_CHECK_DISK,NULL,0,NULL,0,&ret,NULL)){ return -1; } return 0; /* floppy is inserted */ } int fldr_io_direct(int fldrn,u_int blk,u_char *buf,int mode) { BYTE cyl; BYTE head; BYTE sec; DWORD ret; FD_READ_WRITE_PARAMS rwp; u_int retry; int retval; if ((fldrn<0)||(fldrn>=FLDRNUM)||(fldr_h[fldrn]==INVALID_HANDLE_VALUE)||(buf==NULL)){ return -1; } if ((mode!=IO_BLKS_READ)&&(mode!=IO_BLKS_WRITE)){ return -1; } if (fldr_type[fldrn]==FLDR_TYPE_FLH){ cyl=blk/(AKAI_FL_DISK_SIDES*AKAI_FLH_DISK_SECTORS); head=1&(blk/AKAI_FLH_DISK_SECTORS); sec=AKAI_FL_SECTOR_BASE+(blk%AKAI_FLH_DISK_SECTORS); }else{ cyl=blk/(AKAI_FL_DISK_SIDES*AKAI_FLL_DISK_SECTORS); head=1&(blk/AKAI_FLL_DISK_SECTORS); sec=AKAI_FL_SECTOR_BASE+(blk%AKAI_FLL_DISK_SECTORS); } #ifdef DEBUG PRINTF_OUT("fldr_io_direct: cyl=%u head=%u sec=%u\n",(u_int)cyl,(u_int)head,(u_int)sec); #endif /* check if no floppy inserted */ if (!DeviceIoControl(fldr_h[fldrn],IOCTL_FD_CHECK_DISK,NULL,0,NULL,0,&ret,NULL)){ return -1; } /* goto track */ if (!DeviceIoControl(fldr_h[fldrn],IOCTL_FDCMD_SEEK,&cyl,sizeof(cyl),NULL,0,&ret,NULL)){ return -1; } /* set parameters */ rwp.flags=FD_OPTION_MFM; rwp.phead=head; rwp.cyl=cyl; rwp.head=head; rwp.sector=sec; rwp.size=AKAI_FL_SECTOR_SIZE_CODE; rwp.eot=sec+1; /* for IOCTL_FDCMD_READ_DATA,IOCTL_FDCMD_WRITE_DATA: last sector number+1 !!! */ rwp.gap=0x0a; rwp.datalen=0xff; retval=0; /* no error so far */ if (mode==IO_BLKS_WRITE){ /* copy sector from buf to DMA buffer */ bcopy(buf,(u_char *)fldr_secbuf,AKAI_FL_SECSIZE); for (retry=0;retry=FLDR_RETRYMAX){ retval=-1; /* error */ } }else{ for (retry=0;retry=FLDR_RETRYMAX){ retval=-1; /* error */ } /* copy sector from DMA buffer to buf */ /* Note: even if DeviceIoControl() above returned an error */ /* in this case, DMA buffer could contain leftover from last call */ /* or could contain correct complete or partial sector */ /* -> let caller decide what to do with buf in case of an error */ bcopy((u_char *)fldr_secbuf,buf,AKAI_FL_SECSIZE); } return retval; } int fldr_format(int fldrn) { BYTE cyl; BYTE head; BYTE sec; BYTE secnum; BYTE secgap3; BYTE secoffset; BYTE trackskew; DWORD ret; static BYTE fpbuf[sizeof(FD_FORMAT_PARAMS)+sizeof(FD_ID_HEADER)*AKAI_FLH_DISK_SECTORS]; /* space for max. track size */ PFD_FORMAT_PARAMS fpp; DWORD fpsize; #ifndef FLDR_FORMAT_NOVERIFY FD_READ_WRITE_PARAMS rwp; #endif u_int retry; int retval; if ((fldrn<0)||(fldrn>=FLDRNUM)||(fldr_h[fldrn]==INVALID_HANDLE_VALUE)){ return -1; } if (fldr_type[fldrn]==FLDR_TYPE_FLH){ secnum=AKAI_FLH_DISK_SECTORS; secgap3=AKAI_FLH_SECTOR_GAP3; secoffset=AKAI_FLH_SECTOR_OFFSET; trackskew=AKAI_FLH_TRACK_SKEW; }else{ secnum=AKAI_FLL_DISK_SECTORS; secgap3=AKAI_FLL_SECTOR_GAP3; secoffset=AKAI_FLL_SECTOR_OFFSET; trackskew=AKAI_FLL_TRACK_SKEW; } retval=0; /* no error so far */ fpp=(PFD_FORMAT_PARAMS)fpbuf; fpsize=sizeof(FD_FORMAT_PARAMS)+secnum*sizeof(FD_ID_HEADER); for (cyl=0;cylflags=FD_OPTION_MFM; fpp->phead=head; fpp->size=AKAI_FL_SECTOR_SIZE_CODE; fpp->sectors=secnum; fpp->gap=secgap3; fpp->fill=AKAI_FL_SECTOR_FILL; for (sec=0;secHeader[sec].cyl=cyl; fpp->Header[sec].head=head; fpp->Header[sec].sector=AKAI_FL_SECTOR_BASE+((secoffset+sec+cyl*(secnum-trackskew))%secnum); fpp->Header[sec].size=AKAI_FL_SECTOR_SIZE_CODE; } #ifndef FLDR_FORMAT_NOVERIFY rwp.flags=FD_OPTION_MFM; rwp.phead=head; rwp.cyl=cyl; rwp.head=head; rwp.sector=AKAI_FL_SECTOR_BASE; rwp.size=AKAI_FL_SECTOR_SIZE_CODE; rwp.eot=AKAI_FL_SECTOR_BASE+secnum-1; /* for IOCTL_FDCMD_VERIFY: last sector number !!! */ rwp.gap=0x0a; rwp.datalen=0xff; #endif for (retry=0;retry=FLDR_RETRYMAX){ PRINTF_OUT(" - cannot format track\n"); FLUSH_ALL; #if 1 /* XXX ignore error, continue */ retval=-1; #else return -1; #endif } } } PRINTF_OUT("\rdone \n"); FLUSH_ALL; return retval; } #endif /* _VISUALCPP */ /* cache */ struct blk_cache_s blk_cache[BLK_CACHE_NUM]; int blk_cache_enable; void init_blk_cache(void) { int i; for (i=0;i>32), (u_int)(0xffffffff&blk_cache[i].startoff), blk_cache[i].blksize, blk_cache[i].blk, blk_cache[i].age, blk_cache[i].modified); #else /* !_VISUALCPP */ PRINTF_OUT("%4i %3i 0x%02x%08x 0x%04x 0x%04x 0x%08x %i\n", i, blk_cache[i].fd, (u_int)(blk_cache[i].startoff>>32), (u_int)(0xffffffff&blk_cache[i].startoff), blk_cache[i].blksize, blk_cache[i].blk, blk_cache[i].age, blk_cache[i].modified); #endif /* !_VISUALCPP */ } #ifdef _VISUALCPP PRINTF_OUT("--------------------------------------------------------------\n"); #else /* !_VISUALCPP */ PRINTF_OUT("--------------------------------------------------------\n"); #endif /* !_VISUALCPP */ } int find_blk_cache(int fd, #ifdef _VISUALCPP int fldrn, #endif /* _VISUALCPP */ OFF64_T startoff,u_int blk,u_int blksize) { int i; /* scan cache */ for (i=0;i=BLK_CACHE_NUM)||(!blk_cache[i].valid)){ return; } blk_cache[i].age=0; /* reset age */ /* find the youngest of the others */ agemin=BLK_CACHE_AGE_MAX; for (j=0;j(OFF64_T)0xffffffff){ /* XXX */ return -1; } if (bsize==0){ return 0; /* done */ } if (!blk_cache_enable){ /* cache disabled? */ cachealloc=0; /* don't allocate cache */ } if (cachealloc /* must allocate cache? */ #ifdef _VISUALCPP ||(fldrn>=0) /* or is floppy drive? */ #endif /* _VISUALCPP */ ){ /* single blocks */ blkmin=bstart; blkmax=bstart+bsize; blkchunk=1; }else{ /* all blocks at once */ blkmin=bstart; blkmax=bstart+1; /* fake value: only once */ blkchunk=bsize; } for (blk=blkmin;blk=0){ /* is floppy drive? */ /* Note: ignore startoff if floppy drive */ if (fldr_io_direct(fldrn,blk,buf+(blk-blkmin)*blksize,mode)<0){ return -1; } }else /* no floppy drive */ #endif /* _VISUALCPP */ { /* goto blk */ if (LSEEK64(fd,startoff+(OFF64_T)(blk*blksize),SEEK_SET)<0){ #ifdef DEBUG PERROR("lseek"); #endif return -1; } if (mode==IO_BLKS_WRITE){ /* write block(s) */ err=WRITE(fd,(void *)(buf+(blk-blkmin)*blksize),blkchunk*blksize); if (err<0){ #ifdef DEBUG PERROR("write"); #endif return -1; } if (err!=(int)(blkchunk*blksize)){ #ifdef DEBUG PRINTF_ERR("write: incomplete\n"); #endif return -1; } }else{ /* read block(s) */ err=READ(fd,(void *)(buf+(blk-blkmin)*blksize),blkchunk*blksize); if (err<0){ #ifdef DEBUG PERROR("read"); #endif return -1; } if (err!=(int)(blkchunk*blksize)){ #ifdef DEBUG PRINTF_ERR("read: incomplete\n"); #endif return -1; } } } if (cachealloc){ /* must allocate cache? */ /* find free entry or the oldest one */ agemax=0; jmax=0; /* first guess */ for (j=0;j=agemax){ /* older or equal? */ agemax=blk_cache[j].age; jmax=j; } } /* old cache entry not free and modified? */ err=0; if ((blk_cache[jmax].valid)&&(blk_cache[jmax].modified)&&(blk_cache[jmax].buf!=NULL)){ /* must flush this block */ if (io_blks_direct(blk_cache[jmax].fd, #ifdef _VISUALCPP blk_cache[jmax].fldrn, #endif /* _VISUALCPP */ blk_cache[jmax].startoff, (u_char *)blk_cache[jmax].buf, blk_cache[jmax].blk, 1, blk_cache[jmax].blksize, 0, /* don't alloc cache */ IO_BLKS_WRITE)<0){ PRINTF_ERR("cannot flush cache block 0x%08x of fd %i\n",blk_cache[jmax].blk,blk_cache[jmax].fd); err=1; /* cannot allocate below */ } } if (!err){ if ((blk_cache[jmax].buf!=NULL)&&(blk_cache[jmax].blksize!=blksize)){ /* not compatible? */ /* free buffer */ free(blk_cache[jmax].buf); blk_cache[jmax].buf=NULL; } if (blk_cache[jmax].buf==NULL){ /* buffer not allocated yet/anymore? */ /* need to allocate buffer */ if ((blk_cache[jmax].buf=(u_char *)malloc(blksize))==NULL){ PERROR("malloc"); err=1; /* cannot allocate below */ } blk_cache[jmax].valid=0; /* free */ blk_cache[jmax].blksize=blksize; } } if (!err){ /* allocate cache entry */ blk_cache[jmax].valid=1; blk_cache[jmax].modified=0; blk_cache[jmax].fd=fd; #ifdef _VISUALCPP blk_cache[jmax].fldrn=fldrn; #endif /* _VISUALCPP */ blk_cache[jmax].startoff=startoff; blk_cache[jmax].blk=blk; blk_cache_aging(jmax); /* adjust ages */ /* copy data */ bcopy(buf+(blk-blkmin)*blksize,blk_cache[jmax].buf,blksize); } } } return 0; } /* Note: prior to changing blksize of any device/file, must flush cache first !!! */ int flush_blk_cache(void) { int ret; int i; /* scan cache */ ret=0; /* no error so far */ for (i=0;i(OFF64_T)0xffffffff){ /* XXX */ return -1; } #ifdef _VISUALCPP if ((fd<0)&&(fldrn<0)){ return -1; } #else /* !_VISUALCPP */ if (fd<0){ return -1; } #endif /* !_VISUALCPP */ if (!blk_cache_enable){ /* cache disabled? */ cachealloc=0; /* don't allocate cache */ } /* check every block */ chunkstart=bstart; chunksize=0; /* no chunk of missing blocks so far */ for (blk=bstart;blk<(bstart+bsize);blk++){ if (blk_cache_enable){ /* cache enabled? */ /* look in cache */ i=find_blk_cache(fd, #ifdef _VISUALCPP fldrn, #endif /* _VISUALCPP */ startoff,blk,blksize); /* Note: no match for blk if blksize has changed!!! */ /* however, these cache entries can be reused if needed */ }else{ i=-1; /* not found in cache */ } if (i<0){ /* not found in cache? */ if (chunksize==0){ /* new chunk of missing blocks? */ chunkstart=blk; /* start of chunk */ } chunksize++; /* one more missing block */ }else{ /* found in cache */ /* Note: now, blk_cache[i].buf!=NULL */ /* Note: must do cache-I/O first, since io_blks_direct could steal i from cache */ if (mode==IO_BLKS_WRITE){ /* copy to cache */ bcopy(buf+(blk-bstart)*blksize,blk_cache[i].buf,blksize); blk_cache[i].modified=1; /* modified */ }else{ /* copy from cache */ bcopy(blk_cache[i].buf,buf+(blk-bstart)*blksize,blksize); } blk_cache_aging(i); /* adjust ages */ /* chunk of missing blocks pending? */ if (chunksize>0){ /* must read or write */ if (io_blks_direct(fd, #ifdef _VISUALCPP fldrn, #endif /* _VISUALCPP */ startoff, (u_char *)(buf+(chunkstart-bstart)*blksize), chunkstart, chunksize, blksize, cachealloc, mode)<0){ return -1; } chunksize=0; /* no chunk of missing blocks anymore */ } } } /* one last time: chunk of missing blocks pending? */ if (chunksize>0){ /* must read or write */ if (io_blks_direct(fd, #ifdef _VISUALCPP fldrn, #endif /* _VISUALCPP */ startoff, (u_char *)(buf+(chunkstart-bstart)*blksize), chunkstart, chunksize, blksize, cachealloc, mode)<0){ return -1; } chunksize=0; /* no chunk of missing blocks anymore */ } return 0; } /* EOF */