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, &sector, &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, &sector, &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			      &sector, &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