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
36 typedef struct sector_map {
37 unsigned int head:1;
38 unsigned int size:7;
39 } sector_map_t;
40
41
42 static struct {
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
73 typedef 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
87 typedef 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 unsigned int FatSize;
104 unsigned 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
115 typedef struct {
116 unsigned char head;
117 unsigned char sector;
118 unsigned char ptr;
119 } Compactify_t;
120
121
analyze_reply(RawRequest_t * raw_cmd,int do_print)122 static 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
send_cmd(int fd,RawRequest_t * raw_cmd,int nr,const char * message,int retries)147 static 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
add_to_request(Xdf_t * This,int ptr,RawRequest_t * request,int * nr,int direction,Compactify_t * compactify)181 static 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
add_to_request_if_invalid(Xdf_t * This,int ptr,RawRequest_t * request,int * nr,Compactify_t * compactify)231 static 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
adjust_bounds(Xdf_t * This,off_t * begin,off_t * end)241 static 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
try_flush_dirty(Xdf_t * This)249 static __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
flush_dirty(Xdf_t * This)284 static 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
load_data(Xdf_t * This,off_t begin,off_t end,int retries)296 static 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
mark_dirty(Xdf_t * This,off_t begin,off_t end)327 static 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
load_bounds(Xdf_t * This,off_t begin,off_t end)342 static 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
fill_t0(Xdf_t * This,int ptr,unsigned int size,int * sector,int * head)373 static int fill_t0(Xdf_t *This, int ptr, unsigned int size,
374 int *sector, int *head)
375 {
376 int n;
377
378 for(n = 0; n < size; ptr++,n++) {
379 REC.head = *head;
380 REC.sector = *sector + 129;
381 REC.phantom = 0;
382 (*sector)++;
383 if(!*head && *sector >= This->track0_size - 8) {
384 *sector = 0;
385 *head = 1;
386 }
387 }
388 return ptr;
389 }
390
391
fill_phantoms(Xdf_t * This,int ptr,unsigned int size)392 static int fill_phantoms(Xdf_t *This, int ptr, unsigned int size)
393 {
394 int n;
395
396 for(n = 0; n < size; ptr++,n++)
397 REC.phantom = 1;
398 return ptr;
399 }
400
decompose(Xdf_t * This,int where,int len,off_t * begin,off_t * end,int boot)401 static void decompose(Xdf_t *This, int where, int len, off_t *begin,
402 off_t *end, int boot)
403 {
404 int ptr, track;
405 sector_map_t *map;
406 int lbegin, lend;
407
408 track = where / This->track_size / 1024;
409
410 *begin = where - track * This->track_size * 1024;
411 *end = where + len - track * This->track_size * 1024;
412 maximize(*end, This->track_size * 1024);
413
414 if(This->current_track == track && !boot)
415 /* already OK, return immediately */
416 return;
417 if(!boot)
418 flush_dirty(This);
419 This->current_track = track;
420
421 if(track) {
422 for(ptr=0, map=This->map; map->size; map++) {
423 /* iterate through all sectors */
424 lbegin = ptr;
425 lend = ptr + (128 << map->size) / This->sector_size;
426 for( ; ptr < lend ; ptr++) {
427 REC.begin = lbegin;
428 REC.end = lend;
429
430 REC.head = map->head;
431 REC.sector = map->size + 128;
432 REC.sizecode = map->size;
433
434 REC.valid = 0;
435 REC.dirty = 0;
436 REC.phantom = 0;
437 }
438 }
439 REC.begin = REC.end = ptr;
440 } else {
441 int sector, head;
442
443 head = 0;
444 sector = 0;
445
446 for(ptr=boot; ptr < 2 * This->track_size; ptr++) {
447 REC.begin = ptr;
448 REC.end = ptr+1;
449
450 REC.sizecode = 2;
451
452 REC.valid = 0;
453 REC.dirty = 0;
454 }
455
456 /* boot & 1st fat */
457 ptr=fill_t0(This, 0, 1 + This->FatSize, §or, &head);
458
459 /* second fat */
460 ptr=fill_phantoms(This, ptr, This->FatSize);
461
462 /* root dir */
463 ptr=fill_t0(This, ptr, This->RootDirSize, §or, &head);
464
465 /* "bad sectors" at the beginning of the fs */
466 ptr=fill_phantoms(This, ptr, 5);
467
468 if(This->rootskip)
469 sector++;
470
471 /* beginning of the file system */
472 ptr = fill_t0(This, ptr,
473 (This->track_size - This->FatSize) * 2 -
474 This->RootDirSize - 6,
475 §or, &head);
476 }
477 This->last_sector = ptr;
478 }
479
480
xdf_read(Stream_t * Stream,char * buf,mt_off_t where,size_t len)481 static int xdf_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len)
482 {
483 off_t begin, end;
484 size_t len2;
485 DeclareThis(Xdf_t);
486
487 decompose(This, truncBytes32(where), len, &begin, &end, 0);
488 len2 = load_data(This, begin, end, 4);
489 len2 -= begin;
490 maximize(len, len2);
491 memcpy(buf, This->buffer + begin, len);
492 return end - begin;
493 }
494
xdf_write(Stream_t * Stream,char * buf,mt_off_t where,size_t len)495 static int xdf_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len)
496 {
497 off_t begin, end;
498 size_t len2;
499 DeclareThis(Xdf_t);
500
501 decompose(This, truncBytes32(where), len, &begin, &end, 0);
502 len2 = load_bounds(This, begin, end);
503 smaximize(end, (off_t)len2);
504 len2 -= begin;
505 sizemaximize(len, (off_t)len2);
506 memcpy(This->buffer + begin, buf, len);
507 mark_dirty(This, begin, end);
508 return end - begin;
509 }
510
xdf_flush(Stream_t * Stream)511 static int xdf_flush(Stream_t *Stream)
512 {
513 DeclareThis(Xdf_t);
514
515 return flush_dirty(This);
516 }
517
xdf_free(Stream_t * Stream)518 static int xdf_free(Stream_t *Stream)
519 {
520 DeclareThis(Xdf_t);
521 Free(This->track_map);
522 Free(This->buffer);
523 return close(This->fd);
524 }
525
526
check_geom(struct device * dev,int media,union bootsector * boot)527 static int check_geom(struct device *dev, int media, union bootsector *boot)
528 {
529 int sect;
530
531 if(media >= 0xfc && media <= 0xff)
532 return 1; /* old DOS */
533
534 if (!IS_MFORMAT_ONLY(dev)) {
535 if(compare(dev->sectors, 19) &&
536 compare(dev->sectors, 23) &&
537 compare(dev->sectors, 24) &&
538 compare(dev->sectors, 46) &&
539 compare(dev->sectors, 48))
540 return 1;
541
542 /* check against contradictory info from configuration file */
543 if(compare(dev->heads, 2))
544 return 1;
545 }
546
547 /* check against info from boot */
548 if(boot) {
549 sect = WORD(nsect);
550 if((sect != 19 && sect != 23 && sect != 24 &&
551 sect != 46 && sect != 48) ||
552 (!IS_MFORMAT_ONLY(dev) && compare(dev->sectors, sect)) ||
553 WORD(nheads) !=2)
554 return 1;
555 }
556 return 0;
557 }
558
set_geom(union bootsector * boot,struct device * dev)559 static void set_geom(union bootsector *boot, struct device *dev)
560 {
561 /* fill in config info to be returned to user */
562 dev->heads = 2;
563 dev->use_2m = 0xff;
564 if(boot) {
565 dev->sectors = WORD(nsect);
566 if(WORD(psect))
567 dev->tracks = WORD(psect) / dev->sectors / 2;
568 }
569 }
570
config_geom(Stream_t * Stream UNUSEDP,struct device * dev,struct device * orig_dev UNUSEDP,int media,union bootsector * boot)571 static int config_geom(Stream_t *Stream UNUSEDP, struct device *dev,
572 struct device *orig_dev UNUSEDP, int media,
573 union bootsector *boot)
574 {
575 if(check_geom(dev, media, boot))
576 return 1;
577 set_geom(boot,dev);
578 return 0;
579 }
580
581 static Class_t XdfClass = {
582 xdf_read,
583 xdf_write,
584 xdf_flush,
585 xdf_free,
586 config_geom,
587 0, /* get_data */
588 0, /* pre-allocate */
589 0, /* get_dosConvert */
590 0 /* discard */
591 };
592
XdfOpen(struct device * dev,char * name,int mode,char * errmsg,struct xdf_info * info)593 Stream_t *XdfOpen(struct device *dev, char *name,
594 int mode, char *errmsg, struct xdf_info *info)
595 {
596 Xdf_t *This;
597 off_t begin, end;
598 union bootsector *boot;
599 unsigned int type;
600
601 if(dev && (!SHOULD_USE_XDF(dev) || check_geom(dev, 0, 0)))
602 return NULL;
603
604 This = New(Xdf_t);
605 if (!This)
606 return NULL;
607
608 This->Class = &XdfClass;
609 This->sector_size = 512;
610 This->stretch = 0;
611
612 precmd(dev);
613 This->fd = open(name, mode | dev->mode | O_EXCL | O_NDELAY);
614 if(This->fd < 0) {
615 #ifdef HAVE_SNPRINTF
616 snprintf(errmsg,199,"xdf floppy: open: \"%s\"", strerror(errno));
617 #else
618 sprintf(errmsg,"xdf floppy: open: \"%s\"", strerror(errno));
619 #endif
620 goto exit_0;
621 }
622 closeExec(This->fd);
623
624 This->drive = GET_DRIVE(This->fd);
625 if(This->drive < 0)
626 goto exit_1;
627
628 /* allocate buffer */
629 This->buffer = (char *) malloc(96 * 512);
630 if (!This->buffer)
631 goto exit_1;
632
633 This->current_track = -1;
634 This->track_map = (TrackMap_t *)
635 calloc(96, sizeof(TrackMap_t));
636 if(!This->track_map)
637 goto exit_2;
638
639 /* lock the device on writes */
640 if (lock_dev(This->fd, mode == O_RDWR, dev)) {
641 #ifdef HAVE_SNPRINTF
642 snprintf(errmsg,199,"xdf floppy: device \"%s\" busy:",
643 dev->name);
644 #else
645 sprintf(errmsg,"xdf floppy: device \"%s\" busy:",
646 dev->name);
647 #endif
648 goto exit_3;
649 }
650
651 /* Before reading the boot sector, assume dummy values suitable
652 * for reading at least the boot sector */
653 This->track_size = 11;
654 This->track0_size = 6;
655 This->rate = 0;
656 This->FatSize = 9;
657 This->RootDirSize = 1;
658 decompose(This, 0, 512, &begin, &end, 0);
659 if (load_data(This, 0, 1, 1) < 0 ) {
660 This->rate = 0x43;
661 if(load_data(This, 0, 1, 1) < 0)
662 goto exit_3;
663 }
664
665 boot = (union bootsector *) This->buffer;
666 This->FatSize = WORD(fatlen);
667 This->RootDirSize = WORD(dirents)/16;
668 This->track_size = WORD(nsect);
669 for(type=0; type < NUMBER(xdf_table); type++) {
670 if(xdf_table[type].track_size == This->track_size) {
671 This->map = xdf_table[type].map;
672 This->track0_size = xdf_table[type].track0_size;
673 This->rootskip = xdf_table[type].rootskip;
674 This->rate = xdf_table[type].rate;
675 break;
676 }
677 }
678 if(type == NUMBER(xdf_table))
679 goto exit_3;
680
681 if(info) {
682 info->RootDirSize = This->RootDirSize;
683 info->FatSize = This->FatSize;
684 info->BadSectors = 5;
685 }
686 decompose(This, 0, 512, &begin, &end, 1);
687
688 This->refs = 1;
689 This->Next = 0;
690 This->Buffer = 0;
691 if(dev)
692 set_geom(boot, dev);
693 return (Stream_t *) This;
694
695 exit_3:
696 Free(This->track_map);
697 exit_2:
698 Free(This->buffer);
699 exit_1:
700 close(This->fd);
701 exit_0:
702 Free(This);
703 return NULL;
704 }
705
706 #endif
707
708 /* Algorithms can't be patented */
709
710