/* 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 . * * 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 */