1/*
2 * Copyright (c) 2001 Joerg Wunsch
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#include <dev/ic/nec765.h>
30
31#include <sys/fdcio.h>
32
33#include <err.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sysexits.h>
38
39#include "fdutil.h"
40
41/*
42 * Decode the FDC status pointed to by `fdcsp', and print a textual
43 * translation to stderr.  If `terse' is false, the numerical FDC
44 * register status is printed, too.
45 */
46void
47printstatus(struct fdc_status *fdcsp, int terse)
48{
49	char msgbuf[100];
50
51	if (!terse)
52		fprintf(stderr,
53		"\nFDC status ST0=%#x ST1=%#x ST2=%#x C=%u H=%u R=%u N=%u:\n",
54			fdcsp->status[0] & 0xff,
55			fdcsp->status[1] & 0xff,
56			fdcsp->status[2] & 0xff,
57			fdcsp->status[3] & 0xff,
58			fdcsp->status[4] & 0xff,
59			fdcsp->status[5] & 0xff,
60			fdcsp->status[6] & 0xff);
61
62	if ((fdcsp->status[0] & NE7_ST0_IC_RC) == 0) {
63		sprintf(msgbuf, "timeout");
64	} else if ((fdcsp->status[0] & NE7_ST0_IC_RC) != NE7_ST0_IC_AT) {
65		sprintf(msgbuf, "unexcpted interrupt code %#x",
66			fdcsp->status[0] & NE7_ST0_IC_RC);
67	} else {
68		strcpy(msgbuf, "unexpected error code in ST1/ST2");
69
70		if (fdcsp->status[1] & NE7_ST1_EN)
71			strcpy(msgbuf, "end of cylinder (wrong format)");
72		else if (fdcsp->status[1] & NE7_ST1_DE) {
73			if (fdcsp->status[2] & NE7_ST2_DD)
74				strcpy(msgbuf, "CRC error in data field");
75			else
76				strcpy(msgbuf, "CRC error in ID field");
77		} else if (fdcsp->status[1] & NE7_ST1_MA) {
78			if (fdcsp->status[2] & NE7_ST2_MD)
79				strcpy(msgbuf, "no address mark in data field");
80			else
81				strcpy(msgbuf, "no address mark in ID field");
82		} else if (fdcsp->status[2] & NE7_ST2_WC)
83			strcpy(msgbuf, "wrong cylinder (format mismatch)");
84		else if (fdcsp->status[1] & NE7_ST1_ND)
85			strcpy(msgbuf, "no data (sector not found)");
86	}
87	fputs(msgbuf, stderr);
88}
89
90static struct fd_type fd_types_auto[1] =
91    { { 0,0,0,0,0,0,0,0,0,0,0,FL_AUTO } };
92
93
94static struct fd_type fd_types_288m[] = {
95#if 0
96	{ FDF_3_2880 },
97#endif
98	{ FDF_3_1722 },
99	{ FDF_3_1476 },
100	{ FDF_3_1440 },
101	{ FDF_3_1200 },
102	{ FDF_3_820 },
103	{ FDF_3_800 },
104	{ FDF_3_720 },
105	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
106};
107
108static struct fd_type fd_types_144m[] = {
109#ifdef PC98
110#if 0
111	{ FDF_3_1722 },
112	{ FDF_3_1476 },
113#endif
114	{ FDF_3_1440 },
115	{ FDF_3_1200 },
116#if 0
117	{ FDF_3_820 },
118	{ FDF_3_800 },
119#endif
120	{ FDF_3_720 },
121	{ FDF_3_360 },
122	{ FDF_3_640 },
123	{ FDF_3_1230 },
124#if 0
125	{ FDF_3_1280 },
126	{ FDF_3_1480 },
127	{ FDF_3_1640 },
128#endif
129	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
130#else
131	{ FDF_3_1722 },
132	{ FDF_3_1476 },
133	{ FDF_3_1440 },
134	{ FDF_3_1200 },
135	{ FDF_3_820 },
136	{ FDF_3_800 },
137	{ FDF_3_720 },
138	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
139#endif
140};
141
142static struct fd_type fd_types_12m[] = {
143#ifdef PC98
144	{ FDF_5_1200 },
145#if 0
146	{ FDF_5_820 },
147	{ FDF_5_800 },
148#endif
149	{ FDF_5_720 },
150	{ FDF_5_360 },
151	{ FDF_5_640 },
152	{ FDF_5_1230 },
153#if 0
154	{ FDF_5_1280 },
155#endif
156	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
157#else
158	{ FDF_5_1200 },
159	{ FDF_5_1230 },
160	{ FDF_5_1480 },
161	{ FDF_5_1440 },
162	{ FDF_5_820 },
163	{ FDF_5_800 },
164	{ FDF_5_720 },
165	{ FDF_5_360 | FL_2STEP },
166	{ FDF_5_640 },
167	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
168#endif
169};
170
171static struct fd_type fd_types_720k[] =
172{
173	{ FDF_3_720 },
174	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
175};
176
177static struct fd_type fd_types_360k[] =
178{
179	{ FDF_5_360 },
180	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
181};
182
183
184/*
185 * Parse a format string, and fill in the parameter pointed to by `out'.
186 *
187 * sectrac,secsize,datalen,gap,ncyls,speed,heads,f_gap,f_inter,offs2,flags[...]
188 *
189 * sectrac = sectors per track
190 * secsize = sector size in bytes
191 * datalen = length of sector if secsize == 128
192 * gap     = gap length when reading
193 * ncyls   = number of cylinders
194 * speed   = transfer speed 250/300/500/1000 KB/s
195 * heads   = number of heads
196 * f_gap   = gap length when formatting
197 * f_inter = sector interleave when formatting
198 * offs2   = offset of sectors on side 2
199 * flags   = +/-mfm | +/-2step | +/-perpend
200 *             mfm - use MFM recording
201 *             2step - use 2 steps between cylinders
202 *             perpend - user perpendicular (vertical) recording
203 *
204 * Any omitted value will be passed on from parameter `in'.
205 */
206void
207parse_fmt(const char *s, enum fd_drivetype type,
208	  struct fd_type in, struct fd_type *out)
209{
210	int i, j;
211	const char *cp;
212	char *s1;
213
214	*out = in;
215
216	for (i = 0;; i++) {
217		if (s == 0)
218			break;
219
220		if ((cp = strchr(s, ',')) == 0) {
221			s1 = strdup(s);
222			if (s1 == NULL)
223				abort();
224			s = 0;
225		} else {
226			s1 = malloc(cp - s + 1);
227			if (s1 == NULL)
228				abort();
229			memcpy(s1, s, cp - s);
230			s1[cp - s] = 0;
231
232			s = cp + 1;
233		}
234		if (strlen(s1) == 0) {
235			free(s1);
236			continue;
237		}
238
239		switch (i) {
240		case 0:		/* sectrac */
241			if (getnum(s1, &out->sectrac))
242				errx(EX_USAGE,
243				     "bad numeric value for sectrac: %s", s1);
244			break;
245
246		case 1:		/* secsize */
247			if (getnum(s1, &j))
248				errx(EX_USAGE,
249				     "bad numeric value for secsize: %s", s1);
250			if (j == 128) out->secsize = 0;
251			else if (j == 256) out->secsize = 1;
252			else if (j == 512) out->secsize = 2;
253			else if (j == 1024) out->secsize = 3;
254			else
255				errx(EX_USAGE, "bad sector size %d", j);
256			break;
257
258		case 2:		/* datalen */
259			if (getnum(s1, &j))
260				errx(EX_USAGE,
261				     "bad numeric value for datalen: %s", s1);
262			if (j >= 256)
263				errx(EX_USAGE, "bad datalen %d", j);
264			out->datalen = j;
265			break;
266
267		case 3:		/* gap */
268			if (getnum(s1, &out->gap))
269				errx(EX_USAGE,
270				     "bad numeric value for gap: %s", s1);
271			break;
272
273		case 4:		/* ncyls */
274			if (getnum(s1, &j))
275				errx(EX_USAGE,
276				     "bad numeric value for ncyls: %s", s1);
277			if (j > 85)
278				errx(EX_USAGE, "bad # of cylinders %d", j);
279			out->tracks = j;
280			break;
281
282		case 5:		/* speed */
283			if (getnum(s1, &j))
284				errx(EX_USAGE,
285				     "bad numeric value for speed: %s", s1);
286			switch (type) {
287			default:
288				abort(); /* paranoia */
289
290			case FDT_360K:
291			case FDT_720K:
292				if (j == 250)
293					out->trans = FDC_250KBPS;
294				else
295					errx(EX_USAGE, "bad speed %d", j);
296				break;
297
298			case FDT_12M:
299				if (j == 300)
300					out->trans = FDC_300KBPS;
301				else if (j == 250)
302					out->trans = FDC_250KBPS;
303				else if (j == 500)
304					out->trans = FDC_500KBPS;
305				else
306					errx(EX_USAGE, "bad speed %d", j);
307				break;
308
309			case FDT_288M:
310				if (j == 1000)
311					out->trans = FDC_1MBPS;
312				/* FALLTHROUGH */
313			case FDT_144M:
314				if (j == 250)
315					out->trans = FDC_250KBPS;
316				else if (j == 500)
317					out->trans = FDC_500KBPS;
318				else
319					errx(EX_USAGE, "bad speed %d", j);
320				break;
321			}
322			break;
323
324		case 6:		/* heads */
325			if (getnum(s1, &j))
326				errx(EX_USAGE,
327				     "bad numeric value for heads: %s", s1);
328			if (j == 1 || j == 2)
329				out->heads = j;
330			else
331				errx(EX_USAGE, "bad # of heads %d", j);
332			break;
333
334		case 7:		/* f_gap */
335			if (getnum(s1, &out->f_gap))
336				errx(EX_USAGE,
337				     "bad numeric value for f_gap: %s", s1);
338			break;
339
340		case 8:		/* f_inter */
341			if (getnum(s1, &out->f_inter))
342				errx(EX_USAGE,
343				     "bad numeric value for f_inter: %s", s1);
344			break;
345
346		case 9:		/* offs2 */
347			if (getnum(s1, &out->offset_side2))
348				errx(EX_USAGE,
349				     "bad numeric value for offs2: %s", s1);
350			break;
351
352		default:
353			if (strcmp(s1, "+mfm") == 0)
354				out->flags |= FL_MFM;
355			else if (strcmp(s1, "-mfm") == 0)
356				out->flags &= ~FL_MFM;
357			else if (strcmp(s1, "+auto") == 0)
358				out->flags |= FL_AUTO;
359			else if (strcmp(s1, "-auto") == 0)
360				out->flags &= ~FL_AUTO;
361			else if (strcmp(s1, "+2step") == 0)
362				out->flags |= FL_2STEP;
363			else if (strcmp(s1, "-2step") == 0)
364				out->flags &= ~FL_2STEP;
365			else if (strcmp(s1, "+perpnd") == 0)
366				out->flags |= FL_PERPND;
367			else if (strcmp(s1, "-perpnd") == 0)
368				out->flags &= ~FL_PERPND;
369			else
370				errx(EX_USAGE, "bad flag: %s", s1);
371			break;
372		}
373		free(s1);
374	}
375
376	out->size = out->tracks * out->heads * out->sectrac;
377}
378
379/*
380 * Print a textual translation of the drive (density) type described
381 * by `in' to stdout.  The string uses the same form that is parseable
382 * by parse_fmt().
383 */
384void
385print_fmt(struct fd_type in)
386{
387	int secsize, speed;
388
389	secsize = 128 << in.secsize;
390	switch (in.trans) {
391	case FDC_250KBPS:	speed = 250; break;
392	case FDC_300KBPS:	speed = 300; break;
393	case FDC_500KBPS:	speed = 500; break;
394	case FDC_1MBPS:		speed = 1000; break;
395	default:		speed = 1; break;
396	}
397
398	printf("%d,%d,%#x,%#x,%d,%d,%d,%#x,%d,%d",
399	       in.sectrac, secsize, in.datalen, in.gap, in.tracks,
400	       speed, in.heads, in.f_gap, in.f_inter, in.offset_side2);
401	if (in.flags & FL_MFM)
402		printf(",+mfm");
403	if (in.flags & FL_2STEP)
404		printf(",+2step");
405	if (in.flags & FL_PERPND)
406		printf(",+perpnd");
407	if (in.flags & FL_AUTO)
408		printf(",+auto");
409	putc('\n', stdout);
410}
411
412/*
413 * Based on `size' (in kilobytes), walk through the table of known
414 * densities for drive type `type' and see if we can find one.  If
415 * found, return it (as a pointer to static storage), otherwise return
416 * NULL.
417 */
418struct fd_type *
419get_fmt(int size, enum fd_drivetype type)
420{
421	int i, n;
422	struct fd_type *fdtp;
423
424	switch (type) {
425	default:
426		return (0);
427
428	case FDT_360K:
429		fdtp = fd_types_360k;
430		n = sizeof fd_types_360k / sizeof(struct fd_type);
431		break;
432
433	case FDT_720K:
434		fdtp = fd_types_720k;
435		n = sizeof fd_types_720k / sizeof(struct fd_type);
436		break;
437
438	case FDT_12M:
439		fdtp = fd_types_12m;
440		n = sizeof fd_types_12m / sizeof(struct fd_type);
441		break;
442
443	case FDT_144M:
444		fdtp = fd_types_144m;
445		n = sizeof fd_types_144m / sizeof(struct fd_type);
446		break;
447
448	case FDT_288M:
449		fdtp = fd_types_288m;
450		n = sizeof fd_types_288m / sizeof(struct fd_type);
451		break;
452	}
453
454	if (size == -1)
455		return fd_types_auto;
456
457	for (i = 0; i < n; i++, fdtp++) {
458		fdtp->size = fdtp->sectrac * fdtp->heads * fdtp->tracks;
459		if (((128 << fdtp->secsize) * fdtp->size / 1024) == size)
460			return (fdtp);
461	}
462	return (0);
463}
464
465/*
466 * Parse a number from `s'.  If the string cannot be converted into a
467 * number completely, return -1, otherwise 0.  The result is returned
468 * in `*res'.
469 */
470int
471getnum(const char *s, int *res)
472{
473	unsigned long ul;
474	char *cp;
475
476	ul = strtoul(s, &cp, 0);
477	if (*cp != '\0')
478	  return (-1);
479
480	*res = (int)ul;
481	return (0);
482}
483
484/*
485 * Return a short name and a verbose description for the drive
486 * described by `t'.
487 */
488void
489getname(enum fd_drivetype t, const char **name, const char **descr)
490{
491
492	switch (t) {
493	default:
494		*name = "unknown";
495		*descr = "unknown drive type";
496		break;
497
498	case FDT_360K:
499		*name = "360K";
500		*descr = "5.25\" double-density";
501		break;
502
503	case FDT_12M:
504		*name = "1.2M";
505		*descr = "5.25\" high-density";
506		break;
507
508	case FDT_720K:
509		*name = "720K";
510		*descr = "3.5\" double-density";
511		break;
512
513	case FDT_144M:
514		*name = "1.44M";
515		*descr = "3.5\" high-density";
516		break;
517
518	case FDT_288M:
519		*name = "2.88M";
520		*descr = "3.5\" extra-density";
521		break;
522	}
523}
524