1/* Copyright 1994,1996-2003,2005,2007,2009 Alain Knaff. 2 * This file is part of mtools. 3 * 4 * Mtools is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * Mtools is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with Mtools. If not, see <http://www.gnu.org/licenses/>. 16 * 17 * Io to an xdf disk 18 * 19 * written by: 20 * 21 * Alain L. Knaff 22 * alain@knaff.lu 23 * 24 */ 25 26 27#include "sysincludes.h" 28#ifdef OS_linux 29#include "msdos.h" 30#include "mtools.h" 31#include "devices.h" 32#include "xdf_io.h" 33 34/* Algorithms can't be patented */ 35 36typedef struct sector_map { 37 unsigned int head:1; 38 unsigned int size:7; 39} sector_map_t; 40 41 42struct { 43 unsigned char track_size; 44 unsigned int track0_size:7; 45 unsigned int rootskip:1; 46 unsigned char rate; 47 sector_map_t map[9]; 48} xdf_table[]= { 49 { 50 19, 16, 0, 0, 51 { {0,3}, {0,6}, {1,2}, {0,2}, {1,6}, {1,3}, {0,0} } 52 }, 53 { 54 23, 19, 0, 0, 55 { {0,3}, {0,4}, {1,6}, {0,2}, {1,2}, {0,6}, {1,4}, {1,3}, {0,0} } 56 }, 57 { 58 46, 37, 1, 0x43, 59 { {0,3}, {0,4}, {0,5}, {0,7}, {1,3}, {1,4}, {1,5}, {1,7}, {0,0} } 60 }, 61 { 62 24, 20, 1, 0, 63 { {0,5}, {1,6}, {0,6}, {1, 5} } 64 }, 65 { 66 48, 41, 1, 0, 67 { {0,6}, {1,7}, {0,7}, {1, 6} } 68 } 69}; 70 71#define NUMBER(x) (sizeof(x)/sizeof(x[0])) 72 73typedef struct { 74 unsigned char begin; /* where it begins */ 75 unsigned char end; 76 unsigned char sector; 77 unsigned char sizecode; 78 79 unsigned int dirty:1; 80 unsigned int phantom:2; 81 unsigned int valid:1; 82 unsigned int head:1; 83} TrackMap_t; 84 85 86 87typedef struct Xdf_t { 88 Class_t *Class; 89 int refs; 90 Stream_t *Next; 91 Stream_t *Buffer; 92 93 int fd; 94 char *buffer; 95 96 int current_track; 97 98 sector_map_t *map; 99 100 int track_size; 101 int track0_size; 102 int sector_size; 103 int FatSize; 104 int RootDirSize; 105 TrackMap_t *track_map; 106 107 unsigned char last_sector; 108 unsigned char rate; 109 110 unsigned int stretch:1; 111 unsigned int rootskip:1; 112 signed int drive:4; 113} Xdf_t; 114 115typedef struct { 116 unsigned char head; 117 unsigned char sector; 118 unsigned char ptr; 119} Compactify_t; 120 121 122static int analyze_reply(RawRequest_t *raw_cmd, int do_print) 123{ 124 int ret, bytes, newbytes; 125 126 bytes = 0; 127 while(1) { 128 ret = analyze_one_reply(raw_cmd, &newbytes, do_print); 129 bytes += newbytes; 130 switch(ret) { 131 case 0: 132 return bytes; 133 case 1: 134 raw_cmd++; 135 break; 136 case -1: 137 if(bytes) 138 return bytes; 139 else 140 return 0; 141 } 142 } 143} 144 145 146 147static int send_cmd(int fd, RawRequest_t *raw_cmd, int nr, 148 const char *message, int retries) 149{ 150 int j; 151 int ret=-1; 152 153 if(!nr) 154 return 0; 155 for (j=0; j< retries; j++){ 156 switch(send_one_cmd(fd, raw_cmd, message)) { 157 case -1: 158 return -1; 159 case 1: 160 j++; 161 continue; 162 case 0: 163 break; 164 } 165 if((ret=analyze_reply(raw_cmd, j)) > 0) 166 return ret; /* ok */ 167 } 168 if(j > 1 && j == retries) { 169 fprintf(stderr,"Too many errors, giving up\n"); 170 return 0; 171 } 172 return -1; 173} 174 175 176 177#define REC (This->track_map[ptr]) 178#define END(x) (This->track_map[(x)].end) 179#define BEGIN(x) (This->track_map[(x)].begin) 180 181static int add_to_request(Xdf_t *This, int ptr, 182 RawRequest_t *request, int *nr, 183 int direction, Compactify_t *compactify) 184{ 185#if 0 186 if(direction == MT_WRITE) { 187 printf("writing %d: %d %d %d %d [%02x]\n", 188 ptr, This->current_track, 189 REC.head, REC.sector, REC.sizecode, 190 *(This->buffer + ptr * This->sector_size)); 191 } else 192 printf(" load %d.%d\n", This->current_track, ptr); 193#endif 194 if(REC.phantom) { 195 if(direction== MT_READ) 196 memset(This->buffer + ptr * This->sector_size, 0, 197 128 << REC.sizecode); 198 return 0; 199 } 200 201 if(*nr && 202 RR_SIZECODE(request+(*nr)-1) == REC.sizecode && 203 compactify->head == REC.head && 204 compactify->ptr + 1 == ptr && 205 compactify->sector +1 == REC.sector) { 206 RR_SETSIZECODE(request+(*nr)-1, REC.sizecode); 207 } else { 208 if(*nr) 209 RR_SETCONT(request+(*nr)-1); 210 RR_INIT(request+(*nr)); 211 RR_SETDRIVE(request+(*nr), This->drive); 212 RR_SETRATE(request+(*nr), This->rate); 213 RR_SETTRACK(request+(*nr), This->current_track); 214 RR_SETPTRACK(request+(*nr), 215 This->current_track << This->stretch); 216 RR_SETHEAD(request+(*nr), REC.head); 217 RR_SETSECTOR(request+(*nr), REC.sector); 218 RR_SETSIZECODE(request+(*nr), REC.sizecode); 219 RR_SETDIRECTION(request+(*nr), direction); 220 RR_SETDATA(request+(*nr), 221 (caddr_t) This->buffer + ptr * This->sector_size); 222 (*nr)++; 223 } 224 compactify->ptr = ptr; 225 compactify->head = REC.head; 226 compactify->sector = REC.sector; 227 return 0; 228} 229 230 231static void add_to_request_if_invalid(Xdf_t *This, int ptr, 232 RawRequest_t *request, int *nr, 233 Compactify_t *compactify) 234{ 235 if(!REC.valid) 236 add_to_request(This, ptr, request, nr, MT_READ, compactify); 237 238} 239 240 241static void adjust_bounds(Xdf_t *This, off_t *begin, off_t *end) 242{ 243 /* translates begin and end from byte to sectors */ 244 *begin = *begin / This->sector_size; 245 *end = (*end + This->sector_size - 1) / This->sector_size; 246} 247 248 249static __inline__ int try_flush_dirty(Xdf_t *This) 250{ 251 int ptr, nr, bytes; 252 RawRequest_t requests[100]; 253 Compactify_t compactify; 254 255 if(This->current_track < 0) 256 return 0; 257 258 nr = 0; 259 for(ptr=0; ptr < This->last_sector; ptr=REC.end) 260 if(REC.dirty) 261 add_to_request(This, ptr, 262 requests, &nr, 263 MT_WRITE, &compactify); 264#if 1 265 bytes = send_cmd(This->fd,requests, nr, "writing", 4); 266 if(bytes < 0) 267 return bytes; 268#else 269 bytes = 0xffffff; 270#endif 271 for(ptr=0; ptr < This->last_sector; ptr=REC.end) 272 if(REC.dirty) { 273 if(bytes >= REC.end - REC.begin) { 274 bytes -= REC.end - REC.begin; 275 REC.dirty = 0; 276 } else 277 return 1; 278 } 279 return 0; 280} 281 282 283 284static int flush_dirty(Xdf_t *This) 285{ 286 int ret; 287 288 while((ret = try_flush_dirty(This))) { 289 if(ret < 0) 290 return ret; 291 } 292 return 0; 293} 294 295 296static int load_data(Xdf_t *This, off_t begin, off_t end, int retries) 297{ 298 int ptr, nr, bytes; 299 RawRequest_t requests[100]; 300 Compactify_t compactify; 301 302 adjust_bounds(This, &begin, &end); 303 304 ptr = begin; 305 nr = 0; 306 for(ptr=REC.begin; ptr < end ; ptr = REC.end) 307 add_to_request_if_invalid(This, ptr, requests, &nr, 308 &compactify); 309 bytes = send_cmd(This->fd,requests, nr, "reading", retries); 310 if(bytes < 0) 311 return bytes; 312 ptr = begin; 313 for(ptr=REC.begin; ptr < end ; ptr = REC.end) { 314 if(!REC.valid) { 315 if(bytes >= REC.end - REC.begin) { 316 bytes -= REC.end - REC.begin; 317 REC.valid = 1; 318 } else if(ptr > begin) 319 return ptr * This->sector_size; 320 else 321 return -1; 322 } 323 } 324 return end * This->sector_size; 325} 326 327static void mark_dirty(Xdf_t *This, off_t begin, off_t end) 328{ 329 int ptr; 330 331 adjust_bounds(This, &begin, &end); 332 333 ptr = begin; 334 for(ptr=REC.begin; ptr < end ; ptr = REC.end) { 335 REC.valid = 1; 336 if(!REC.phantom) 337 REC.dirty = 1; 338 } 339} 340 341 342static int load_bounds(Xdf_t *This, off_t begin, off_t end) 343{ 344 off_t lbegin, lend; 345 int endp1, endp2; 346 347 lbegin = begin; 348 lend = end; 349 350 adjust_bounds(This, &lbegin, &lend); 351 352 if(begin != BEGIN(lbegin) * This->sector_size && 353 end != BEGIN(lend) * This->sector_size && 354 lend < END(END(lbegin))) 355 /* contiguous end & begin, load them in one go */ 356 return load_data(This, begin, end, 4); 357 358 if(begin != BEGIN(lbegin) * This->sector_size) { 359 endp1 = load_data(This, begin, begin, 4); 360 if(endp1 < 0) 361 return endp1; 362 } 363 364 if(end != BEGIN(lend) * This->sector_size) { 365 endp2 = load_data(This, end, end, 4); 366 if(endp2 < 0) 367 return BEGIN(lend) * This->sector_size; 368 } 369 return lend * This->sector_size; 370} 371 372 373static int fill_t0(Xdf_t *This, int ptr, int size, int *sector, int *head) 374{ 375 int n; 376 377 for(n = 0; n < size; ptr++,n++) { 378 REC.head = *head; 379 REC.sector = *sector + 129; 380 REC.phantom = 0; 381 (*sector)++; 382 if(!*head && *sector >= This->track0_size - 8) { 383 *sector = 0; 384 *head = 1; 385 } 386 } 387 return ptr; 388} 389 390 391static int fill_phantoms(Xdf_t *This, int ptr, int size) 392{ 393 int n; 394 395 for(n = 0; n < size; ptr++,n++) 396 REC.phantom = 1; 397 return ptr; 398} 399 400static void decompose(Xdf_t *This, int where, int len, off_t *begin, 401 off_t *end, int boot) 402{ 403 int ptr, track; 404 sector_map_t *map; 405 int lbegin, lend; 406 407 track = where / This->track_size / 1024; 408 409 *begin = where - track * This->track_size * 1024; 410 *end = where + len - track * This->track_size * 1024; 411 maximize(*end, This->track_size * 1024); 412 413 if(This->current_track == track && !boot) 414 /* already OK, return immediately */ 415 return; 416 if(!boot) 417 flush_dirty(This); 418 This->current_track = track; 419 420 if(track) { 421 for(ptr=0, map=This->map; map->size; map++) { 422 /* iterate through all sectors */ 423 lbegin = ptr; 424 lend = ptr + (128 << map->size) / This->sector_size; 425 for( ; ptr < lend ; ptr++) { 426 REC.begin = lbegin; 427 REC.end = lend; 428 429 REC.head = map->head; 430 REC.sector = map->size + 128; 431 REC.sizecode = map->size; 432 433 REC.valid = 0; 434 REC.dirty = 0; 435 REC.phantom = 0; 436 } 437 } 438 REC.begin = REC.end = ptr; 439 } else { 440 int sector, head; 441 442 head = 0; 443 sector = 0; 444 445 for(ptr=boot; ptr < 2 * This->track_size; ptr++) { 446 REC.begin = ptr; 447 REC.end = ptr+1; 448 449 REC.sizecode = 2; 450 451 REC.valid = 0; 452 REC.dirty = 0; 453 } 454 455 /* boot & 1st fat */ 456 ptr=fill_t0(This, 0, 1 + This->FatSize, §or, &head); 457 458 /* second fat */ 459 ptr=fill_phantoms(This, ptr, This->FatSize); 460 461 /* root dir */ 462 ptr=fill_t0(This, ptr, This->RootDirSize, §or, &head); 463 464 /* "bad sectors" at the beginning of the fs */ 465 ptr=fill_phantoms(This, ptr, 5); 466 467 if(This->rootskip) 468 sector++; 469 470 /* beginning of the file system */ 471 ptr = fill_t0(This, ptr, 472 (This->track_size - This->FatSize) * 2 - 473 This->RootDirSize - 6, 474 §or, &head); 475 } 476 This->last_sector = ptr; 477} 478 479 480static int xdf_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len) 481{ 482 off_t begin, end; 483 size_t len2; 484 DeclareThis(Xdf_t); 485 486 decompose(This, truncBytes32(where), len, &begin, &end, 0); 487 len2 = load_data(This, begin, end, 4); 488 if(len2 < 0) 489 return len2; 490 len2 -= begin; 491 maximize(len, len2); 492 memcpy(buf, This->buffer + begin, len); 493 return end - begin; 494} 495 496static int xdf_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len) 497{ 498 off_t begin, end; 499 size_t len2; 500 DeclareThis(Xdf_t); 501 502 decompose(This, truncBytes32(where), len, &begin, &end, 0); 503 len2 = load_bounds(This, begin, end); 504 if(len2 < 0) 505 return len2; 506 maximize(end, (off_t)len2); 507 len2 -= begin; 508 maximize(len, (off_t)len2); 509 memcpy(This->buffer + begin, buf, len); 510 mark_dirty(This, begin, end); 511 return end - begin; 512} 513 514static int xdf_flush(Stream_t *Stream) 515{ 516 DeclareThis(Xdf_t); 517 518 return flush_dirty(This); 519} 520 521static int xdf_free(Stream_t *Stream) 522{ 523 DeclareThis(Xdf_t); 524 Free(This->track_map); 525 Free(This->buffer); 526 return close(This->fd); 527} 528 529 530static int check_geom(struct device *dev, int media, struct bootsector *boot) 531{ 532 int sect; 533 534 if(media >= 0xfc && media <= 0xff) 535 return 1; /* old DOS */ 536 537 if (!IS_MFORMAT_ONLY(dev)) { 538 if(compare(dev->sectors, 19) && 539 compare(dev->sectors, 23) && 540 compare(dev->sectors, 24) && 541 compare(dev->sectors, 46) && 542 compare(dev->sectors, 48)) 543 return 1; 544 545 /* check against contradictory info from configuration file */ 546 if(compare(dev->heads, 2)) 547 return 1; 548 } 549 550 /* check against info from boot */ 551 if(boot) { 552 sect = WORD(nsect); 553 if((sect != 19 && sect != 23 && sect != 24 && 554 sect != 46 && sect != 48) || 555 (!IS_MFORMAT_ONLY(dev) && compare(dev->sectors, sect)) || 556 WORD(nheads) !=2) 557 return 1; 558 } 559 return 0; 560} 561 562static void set_geom(struct bootsector *boot, struct device *dev) 563{ 564 /* fill in config info to be returned to user */ 565 dev->heads = 2; 566 dev->use_2m = 0xff; 567 if(boot) { 568 dev->sectors = WORD(nsect); 569 if(WORD(psect)) 570 dev->tracks = WORD(psect) / dev->sectors / 2; 571 } 572} 573 574static int config_geom(Stream_t *Stream, struct device *dev, 575 struct device *orig_dev, int media, 576 struct bootsector *boot) 577{ 578 if(check_geom(dev, media, boot)) 579 return 1; 580 set_geom(boot,dev); 581 return 0; 582} 583 584static Class_t XdfClass = { 585 xdf_read, 586 xdf_write, 587 xdf_flush, 588 xdf_free, 589 config_geom, 590 0, /* get_data */ 591 0 /* pre-allocate */ 592}; 593 594Stream_t *XdfOpen(struct device *dev, char *name, 595 int mode, char *errmsg, struct xdf_info *info) 596{ 597 Xdf_t *This; 598 off_t begin, end; 599 struct bootsector *boot; 600 unsigned int type; 601 602 if(dev && (!SHOULD_USE_XDF(dev) || check_geom(dev, 0, 0))) 603 return NULL; 604 605 This = New(Xdf_t); 606 if (!This) 607 return NULL; 608 609 This->Class = &XdfClass; 610 This->sector_size = 512; 611 This->stretch = 0; 612 613 precmd(dev); 614 This->fd = open(name, mode | dev->mode | O_EXCL | O_NDELAY); 615 if(This->fd < 0) { 616#ifdef HAVE_SNPRINTF 617 snprintf(errmsg,199,"xdf floppy: open: \"%s\"", strerror(errno)); 618#else 619 sprintf(errmsg,"xdf floppy: open: \"%s\"", strerror(errno)); 620#endif 621 goto exit_0; 622 } 623 closeExec(This->fd); 624 625 This->drive = GET_DRIVE(This->fd); 626 if(This->drive < 0) 627 goto exit_1; 628 629 /* allocate buffer */ 630 This->buffer = (char *) malloc(96 * 512); 631 if (!This->buffer) 632 goto exit_1; 633 634 This->current_track = -1; 635 This->track_map = (TrackMap_t *) 636 calloc(96, sizeof(TrackMap_t)); 637 if(!This->track_map) 638 goto exit_2; 639 640 /* lock the device on writes */ 641 if (lock_dev(This->fd, mode == O_RDWR, dev)) { 642#ifdef HAVE_SNPRINTF 643 snprintf(errmsg,199,"xdf floppy: device \"%s\" busy:", 644 dev->name); 645#else 646 sprintf(errmsg,"xdf floppy: device \"%s\" busy:", 647 dev->name); 648#endif 649 goto exit_3; 650 } 651 652 /* Before reading the boot sector, assume dummy values suitable 653 * for reading at least the boot sector */ 654 This->track_size = 11; 655 This->track0_size = 6; 656 This->rate = 0; 657 This->FatSize = 9; 658 This->RootDirSize = 1; 659 decompose(This, 0, 512, &begin, &end, 0); 660 if (load_data(This, 0, 1, 1) < 0 ) { 661 This->rate = 0x43; 662 if(load_data(This, 0, 1, 1) < 0) 663 goto exit_3; 664 } 665 666 boot = (struct bootsector *) This->buffer; 667 This->FatSize = WORD(fatlen); 668 This->RootDirSize = WORD(dirents)/16; 669 This->track_size = WORD(nsect); 670 for(type=0; type < NUMBER(xdf_table); type++) { 671 if(xdf_table[type].track_size == This->track_size) { 672 This->map = xdf_table[type].map; 673 This->track0_size = xdf_table[type].track0_size; 674 This->rootskip = xdf_table[type].rootskip; 675 This->rate = xdf_table[type].rate; 676 break; 677 } 678 } 679 if(type == NUMBER(xdf_table)) 680 goto exit_3; 681 682 if(info) { 683 info->RootDirSize = This->RootDirSize; 684 info->FatSize = This->FatSize; 685 info->BadSectors = 5; 686 } 687 decompose(This, 0, 512, &begin, &end, 1); 688 689 This->refs = 1; 690 This->Next = 0; 691 This->Buffer = 0; 692 if(dev) 693 set_geom(boot, dev); 694 return (Stream_t *) This; 695 696exit_3: 697 Free(This->track_map); 698exit_2: 699 Free(This->buffer); 700exit_1: 701 close(This->fd); 702exit_0: 703 Free(This); 704 return NULL; 705} 706 707#endif 708 709/* Algorithms can't be patented */ 710 711