summaryrefslogtreecommitdiff
path: root/xdf_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'xdf_io.c')
-rw-r--r--xdf_io.c710
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, &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);
+ 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 */
+