diff options
Diffstat (limited to 'xdf_io.c')
-rw-r--r-- | xdf_io.c | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/xdf_io.c b/xdf_io.c new file mode 100644 index 0000000..f0db3b3 --- /dev/null +++ b/xdf_io.c @@ -0,0 +1,710 @@ +/* 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; + + +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; + int FatSize; + 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, 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, 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, §or, &head); + + /* second fat */ + ptr=fill_phantoms(This, ptr, This->FatSize); + + /* root dir */ + ptr=fill_t0(This, ptr, This->RootDirSize, §or, &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, + §or, &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); + if(len2 < 0) + return len2; + 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); + if(len2 < 0) + return len2; + maximize(end, (off_t)len2); + len2 -= begin; + maximize(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, struct device *dev, + struct device *orig_dev, 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 */ +}; + +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 */ + |