You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

710 lines
15 KiB

/* Copyright 1994,1996-2003,2005,2007,2009 Alain Knaff.
* This file is part of mtools.
*
* Mtools 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 3 of the License, or
* (at your option) any later version.
*
* Mtools 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 Mtools. If not, see <http://www.gnu.org/licenses/>.
*
* Io to an xdf disk
*
* written by:
*
* Alain L. Knaff
* alain@knaff.lu
*
*/
#include "sysincludes.h"
#ifdef OS_linux
#include "msdos.h"
#include "mtools.h"
#include "devices.h"
#include "xdf_io.h"
/* Algorithms can't be patented */
typedef struct sector_map {
unsigned int head:1;
unsigned int size:7;
} sector_map_t;
static struct {
unsigned char track_size;
unsigned int track0_size:7;
unsigned int rootskip:1;
unsigned char rate;
sector_map_t map[9];
} xdf_table[]= {
{
19, 16, 0, 0,
{ {0,3}, {0,6}, {1,2}, {0,2}, {1,6}, {1,3}, {0,0} }
},
{
23, 19, 0, 0,
{ {0,3}, {0,4}, {1,6}, {0,2}, {1,2}, {0,6}, {1,4}, {1,3}, {0,0} }
},
{
46, 37, 1, 0x43,
{ {0,3}, {0,4}, {0,5}, {0,7}, {1,3}, {1,4}, {1,5}, {1,7}, {0,0} }
},
{
24, 20, 1, 0,
{ {0,5}, {1,6}, {0,6}, {1, 5} }
},
{
48, 41, 1, 0,
{ {0,6}, {1,7}, {0,7}, {1, 6} }
}
};
#define NUMBER(x) (sizeof(x)/sizeof(x[0]))
typedef struct {
unsigned char begin; /* where it begins */
unsigned char end;
unsigned char sector;
unsigned char sizecode;
unsigned int dirty:1;
unsigned int phantom:2;
unsigned int valid:1;
unsigned int head:1;
} TrackMap_t;
typedef struct Xdf_t {
Class_t *Class;
int refs;
Stream_t *Next;
Stream_t *Buffer;
int fd;
char *buffer;
int current_track;
sector_map_t *map;
int track_size;
int track0_size;
int sector_size;
unsigned int FatSize;
unsigned int RootDirSize;
TrackMap_t *track_map;
unsigned char last_sector;
unsigned char rate;
unsigned int stretch:1;
unsigned int rootskip:1;
signed int drive:4;
} Xdf_t;
typedef struct {
unsigned char head;
unsigned char sector;
unsigned char ptr;
} Compactify_t;
static int analyze_reply(RawRequest_t *raw_cmd, int do_print)
{
int ret, bytes, newbytes;
bytes = 0;
while(1) {
ret = analyze_one_reply(raw_cmd, &newbytes, do_print);
bytes += newbytes;
switch(ret) {
case 0:
return bytes;
case 1:
raw_cmd++;
break;
case -1:
if(bytes)
return bytes;
else
return 0;
}
}
}
static int send_cmd(int fd, RawRequest_t *raw_cmd, int nr,
const char *message, int retries)
{
int j;
int ret=-1;
if(!nr)
return 0;
for (j=0; j< retries; j++){
switch(send_one_cmd(fd, raw_cmd, message)) {
case -1:
return -1;
case 1:
j++;
continue;
case 0:
break;
}
if((ret=analyze_reply(raw_cmd, j)) > 0)
return ret; /* ok */
}
if(j > 1 && j == retries) {
fprintf(stderr,"Too many errors, giving up\n");
return 0;
}
return -1;
}
#define REC (This->track_map[ptr])
#define END(x) (This->track_map[(x)].end)
#define BEGIN(x) (This->track_map[(x)].begin)
static int add_to_request(Xdf_t *This, int ptr,
RawRequest_t *request, int *nr,
int direction, Compactify_t *compactify)
{
#if 0
if(direction == MT_WRITE) {
printf("writing %d: %d %d %d %d [%02x]\n",
ptr, This->current_track,
REC.head, REC.sector, REC.sizecode,
*(This->buffer + ptr * This->sector_size));
} else
printf(" load %d.%d\n", This->current_track, ptr);
#endif
if(REC.phantom) {
if(direction== MT_READ)
memset(This->buffer + ptr * This->sector_size, 0,
128 << REC.sizecode);
return 0;
}
if(*nr &&
RR_SIZECODE(request+(*nr)-1) == REC.sizecode &&
compactify->head == REC.head &&
compactify->ptr + 1 == ptr &&
compactify->sector +1 == REC.sector) {
RR_SETSIZECODE(request+(*nr)-1, REC.sizecode);
} else {
if(*nr)
RR_SETCONT(request+(*nr)-1);
RR_INIT(request+(*nr));
RR_SETDRIVE(request+(*nr), This->drive);
RR_SETRATE(request+(*nr), This->rate);
RR_SETTRACK(request+(*nr), This->current_track);
RR_SETPTRACK(request+(*nr),
This->current_track << This->stretch);
RR_SETHEAD(request+(*nr), REC.head);
RR_SETSECTOR(request+(*nr), REC.sector);
RR_SETSIZECODE(request+(*nr), REC.sizecode);
RR_SETDIRECTION(request+(*nr), direction);
RR_SETDATA(request+(*nr),
(caddr_t) This->buffer + ptr * This->sector_size);
(*nr)++;
}
compactify->ptr = ptr;
compactify->head = REC.head;
compactify->sector = REC.sector;
return 0;
}
static void add_to_request_if_invalid(Xdf_t *This, int ptr,
RawRequest_t *request, int *nr,
Compactify_t *compactify)
{
if(!REC.valid)
add_to_request(This, ptr, request, nr, MT_READ, compactify);
}
static void adjust_bounds(Xdf_t *This, off_t *begin, off_t *end)
{
/* translates begin and end from byte to sectors */
*begin = *begin / This->sector_size;
*end = (*end + This->sector_size - 1) / This->sector_size;
}
static __inline__ int try_flush_dirty(Xdf_t *This)
{
int ptr, nr, bytes;
RawRequest_t requests[100];
Compactify_t compactify;
if(This->current_track < 0)
return 0;
nr = 0;
for(ptr=0; ptr < This->last_sector; ptr=REC.end)
if(REC.dirty)
add_to_request(This, ptr,
requests, &nr,
MT_WRITE, &compactify);
#if 1
bytes = send_cmd(This->fd,requests, nr, "writing", 4);
if(bytes < 0)
return bytes;
#else
bytes = 0xffffff;
#endif
for(ptr=0; ptr < This->last_sector; ptr=REC.end)
if(REC.dirty) {
if(bytes >= REC.end - REC.begin) {
bytes -= REC.end - REC.begin;
REC.dirty = 0;
} else
return 1;
}
return 0;
}
static int flush_dirty(Xdf_t *This)
{
int ret;
while((ret = try_flush_dirty(This))) {
if(ret < 0)
return ret;
}
return 0;
}
static int load_data(Xdf_t *This, off_t begin, off_t end, int retries)
{
int ptr, nr, bytes;
RawRequest_t requests[100];
Compactify_t compactify;
adjust_bounds(This, &begin, &end);
ptr = begin;
nr = 0;
for(ptr=REC.begin; ptr < end ; ptr = REC.end)
add_to_request_if_invalid(This, ptr, requests, &nr,
&compactify);
bytes = send_cmd(This->fd,requests, nr, "reading", retries);
if(bytes < 0)
return bytes;
ptr = begin;
for(ptr=REC.begin; ptr < end ; ptr = REC.end) {
if(!REC.valid) {
if(bytes >= REC.end - REC.begin) {
bytes -= REC.end - REC.begin;
REC.valid = 1;
} else if(ptr > begin)
return ptr * This->sector_size;
else
return -1;
}
}
return end * This->sector_size;
}
static void mark_dirty(Xdf_t *This, off_t begin, off_t end)
{
int ptr;
adjust_bounds(This, &begin, &end);
ptr = begin;
for(ptr=REC.begin; ptr < end ; ptr = REC.end) {
REC.valid = 1;
if(!REC.phantom)
REC.dirty = 1;
}
}
static int load_bounds(Xdf_t *This, off_t begin, off_t end)
{
off_t lbegin, lend;
int endp1, endp2;
lbegin = begin;
lend = end;
adjust_bounds(This, &lbegin, &lend);
if(begin != BEGIN(lbegin) * This->sector_size &&
end != BEGIN(lend) * This->sector_size &&
lend < END(END(lbegin)))
/* contiguous end & begin, load them in one go */
return load_data(This, begin, end, 4);
if(begin != BEGIN(lbegin) * This->sector_size) {
endp1 = load_data(This, begin, begin, 4);
if(endp1 < 0)
return endp1;
}
if(end != BEGIN(lend) * This->sector_size) {
endp2 = load_data(This, end, end, 4);
if(endp2 < 0)
return BEGIN(lend) * This->sector_size;
}
return lend * This->sector_size;
}
static int fill_t0(Xdf_t *This, int ptr, unsigned int size,
int *sector, int *head)
{
int n;
for(n = 0; n < size; ptr++,n++) {
REC.head = *head;
REC.sector = *sector + 129;
REC.phantom = 0;
(*sector)++;
if(!*head && *sector >= This->track0_size - 8) {
*sector = 0;
*head = 1;
}
}
return ptr;
}
static int fill_phantoms(Xdf_t *This, int ptr, unsigned int size)
{
int n;
for(n = 0; n < size; ptr++,n++)
REC.phantom = 1;
return ptr;
}
static void decompose(Xdf_t *This, int where, int len, off_t *begin,
off_t *end, int boot)
{
int ptr, track;
sector_map_t *map;
int lbegin, lend;
track = where / This->track_size / 1024;
*begin = where - track * This->track_size * 1024;
*end = where + len - track * This->track_size * 1024;
maximize(*end, This->track_size * 1024);
if(This->current_track == track && !boot)
/* already OK, return immediately */
return;
if(!boot)
flush_dirty(This);
This->current_track = track;
if(track) {
for(ptr=0, map=This->map; map->size; map++) {
/* iterate through all sectors */
lbegin = ptr;
lend = ptr + (128 << map->size) / This->sector_size;
for( ; ptr < lend ; ptr++) {
REC.begin = lbegin;
REC.end = lend;
REC.head = map->head;
REC.sector = map->size + 128;
REC.sizecode = map->size;
REC.valid = 0;
REC.dirty = 0;
REC.phantom = 0;
}
}
REC.begin = REC.end = ptr;
} else {
int sector, head;
head = 0;
sector = 0;
for(ptr=boot; ptr < 2 * This->track_size; ptr++) {
REC.begin = ptr;
REC.end = ptr+1;
REC.sizecode = 2;
REC.valid = 0;
REC.dirty = 0;
}
/* boot & 1st fat */
ptr=fill_t0(This, 0, 1 + This->FatSize, &sector, &head);
/* second fat */
ptr=fill_phantoms(This, ptr, This->FatSize);
/* root dir */
ptr=fill_t0(This, ptr, This->RootDirSize, &sector, &head);
/* "bad sectors" at the beginning of the fs */
ptr=fill_phantoms(This, ptr, 5);
if(This->rootskip)
sector++;
/* beginning of the file system */
ptr = fill_t0(This, ptr,
(This->track_size - This->FatSize) * 2 -
This->RootDirSize - 6,
&sector, &head);
}
This->last_sector = ptr;
}
static int xdf_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len)
{
off_t begin, end;
size_t len2;
DeclareThis(Xdf_t);
decompose(This, truncBytes32(where), len, &begin, &end, 0);
len2 = load_data(This, begin, end, 4);
len2 -= begin;
maximize(len, len2);
memcpy(buf, This->buffer + begin, len);
return end - begin;
}
static int xdf_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len)
{
off_t begin, end;
size_t len2;
DeclareThis(Xdf_t);
decompose(This, truncBytes32(where), len, &begin, &end, 0);
len2 = load_bounds(This, begin, end);
smaximize(end, (off_t)len2);
len2 -= begin;
sizemaximize(len, (off_t)len2);
memcpy(This->buffer + begin, buf, len);
mark_dirty(This, begin, end);
return end - begin;
}
static int xdf_flush(Stream_t *Stream)
{
DeclareThis(Xdf_t);
return flush_dirty(This);
}
static int xdf_free(Stream_t *Stream)
{
DeclareThis(Xdf_t);
Free(This->track_map);
Free(This->buffer);
return close(This->fd);
}
static int check_geom(struct device *dev, int media, union bootsector *boot)
{
int sect;
if(media >= 0xfc && media <= 0xff)
return 1; /* old DOS */
if (!IS_MFORMAT_ONLY(dev)) {
if(compare(dev->sectors, 19) &&
compare(dev->sectors, 23) &&
compare(dev->sectors, 24) &&
compare(dev->sectors, 46) &&
compare(dev->sectors, 48))
return 1;
/* check against contradictory info from configuration file */
if(compare(dev->heads, 2))
return 1;
}
/* check against info from boot */
if(boot) {
sect = WORD(nsect);
if((sect != 19 && sect != 23 && sect != 24 &&
sect != 46 && sect != 48) ||
(!IS_MFORMAT_ONLY(dev) && compare(dev->sectors, sect)) ||
WORD(nheads) !=2)
return 1;
}
return 0;
}
static void set_geom(union bootsector *boot, struct device *dev)
{
/* fill in config info to be returned to user */
dev->heads = 2;
dev->use_2m = 0xff;
if(boot) {
dev->sectors = WORD(nsect);
if(WORD(psect))
dev->tracks = WORD(psect) / dev->sectors / 2;
}
}
static int config_geom(Stream_t *Stream UNUSEDP, struct device *dev,
struct device *orig_dev UNUSEDP, int media,
union bootsector *boot)
{
if(check_geom(dev, media, boot))
return 1;
set_geom(boot,dev);
return 0;
}
static Class_t XdfClass = {
xdf_read,
xdf_write,
xdf_flush,
xdf_free,
config_geom,
0, /* get_data */
0, /* pre-allocate */
0, /* get_dosConvert */
0 /* discard */
};
Stream_t *XdfOpen(struct device *dev, char *name,
int mode, char *errmsg, struct xdf_info *info)
{
Xdf_t *This;
off_t begin, end;
union bootsector *boot;
unsigned int type;
if(dev && (!SHOULD_USE_XDF(dev) || check_geom(dev, 0, 0)))
return NULL;
This = New(Xdf_t);
if (!This)
return NULL;
This->Class = &XdfClass;
This->sector_size = 512;
This->stretch = 0;
precmd(dev);
This->fd = open(name, mode | dev->mode | O_EXCL | O_NDELAY);
if(This->fd < 0) {
#ifdef HAVE_SNPRINTF
snprintf(errmsg,199,"xdf floppy: open: \"%s\"", strerror(errno));
#else
sprintf(errmsg,"xdf floppy: open: \"%s\"", strerror(errno));
#endif
goto exit_0;
}
closeExec(This->fd);
This->drive = GET_DRIVE(This->fd);
if(This->drive < 0)
goto exit_1;
/* allocate buffer */
This->buffer = (char *) malloc(96 * 512);
if (!This->buffer)
goto exit_1;
This->current_track = -1;
This->track_map = (TrackMap_t *)
calloc(96, sizeof(TrackMap_t));
if(!This->track_map)
goto exit_2;
/* lock the device on writes */
if (lock_dev(This->fd, mode == O_RDWR, dev)) {
#ifdef HAVE_SNPRINTF
snprintf(errmsg,199,"xdf floppy: device \"%s\" busy:",
dev->name);
#else
sprintf(errmsg,"xdf floppy: device \"%s\" busy:",
dev->name);
#endif
goto exit_3;
}
/* Before reading the boot sector, assume dummy values suitable
* for reading at least the boot sector */
This->track_size = 11;
This->track0_size = 6;
This->rate = 0;
This->FatSize = 9;
This->RootDirSize = 1;
decompose(This, 0, 512, &begin, &end, 0);
if (load_data(This, 0, 1, 1) < 0 ) {
This->rate = 0x43;
if(load_data(This, 0, 1, 1) < 0)
goto exit_3;
}
boot = (union bootsector *) This->buffer;
This->FatSize = WORD(fatlen);
This->RootDirSize = WORD(dirents)/16;
This->track_size = WORD(nsect);
for(type=0; type < NUMBER(xdf_table); type++) {
if(xdf_table[type].track_size == This->track_size) {
This->map = xdf_table[type].map;
This->track0_size = xdf_table[type].track0_size;
This->rootskip = xdf_table[type].rootskip;
This->rate = xdf_table[type].rate;
break;
}
}
if(type == NUMBER(xdf_table))
goto exit_3;
if(info) {
info->RootDirSize = This->RootDirSize;
info->FatSize = This->FatSize;
info->BadSectors = 5;
}
decompose(This, 0, 512, &begin, &end, 1);
This->refs = 1;
This->Next = 0;
This->Buffer = 0;
if(dev)
set_geom(boot, dev);
return (Stream_t *) This;
exit_3:
Free(This->track_map);
exit_2:
Free(This->buffer);
exit_1:
close(This->fd);
exit_0:
Free(This);
return NULL;
}
#endif
/* Algorithms can't be patented */