1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2001 Joerg Wunsch
5 *
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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	{ FDF_3_1722 },
110	{ FDF_3_1476 },
111	{ FDF_3_1440 },
112	{ FDF_3_1200 },
113	{ FDF_3_820 },
114	{ FDF_3_800 },
115	{ FDF_3_720 },
116	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
117};
118
119static struct fd_type fd_types_12m[] = {
120	{ FDF_5_1200 },
121	{ FDF_5_1230 },
122	{ FDF_5_1480 },
123	{ FDF_5_1440 },
124	{ FDF_5_820 },
125	{ FDF_5_800 },
126	{ FDF_5_720 },
127	{ FDF_5_360 | FL_2STEP },
128	{ FDF_5_640 },
129	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
130};
131
132static struct fd_type fd_types_720k[] =
133{
134	{ FDF_3_720 },
135	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
136};
137
138static struct fd_type fd_types_360k[] =
139{
140	{ FDF_5_360 },
141	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
142};
143
144
145/*
146 * Parse a format string, and fill in the parameter pointed to by `out'.
147 *
148 * sectrac,secsize,datalen,gap,ncyls,speed,heads,f_gap,f_inter,offs2,flags[...]
149 *
150 * sectrac = sectors per track
151 * secsize = sector size in bytes
152 * datalen = length of sector if secsize == 128
153 * gap     = gap length when reading
154 * ncyls   = number of cylinders
155 * speed   = transfer speed 250/300/500/1000 KB/s
156 * heads   = number of heads
157 * f_gap   = gap length when formatting
158 * f_inter = sector interleave when formatting
159 * offs2   = offset of sectors on side 2
160 * flags   = +/-mfm | +/-2step | +/-perpend
161 *             mfm - use MFM recording
162 *             2step - use 2 steps between cylinders
163 *             perpend - user perpendicular (vertical) recording
164 *
165 * Any omitted value will be passed on from parameter `in'.
166 */
167void
168parse_fmt(const char *s, enum fd_drivetype type,
169	  struct fd_type in, struct fd_type *out)
170{
171	int i, j;
172	const char *cp;
173	char *s1;
174
175	*out = in;
176
177	for (i = 0;; i++) {
178		if (s == NULL)
179			break;
180
181		if ((cp = strchr(s, ',')) == NULL) {
182			s1 = strdup(s);
183			if (s1 == NULL)
184				abort();
185			s = 0;
186		} else {
187			s1 = malloc(cp - s + 1);
188			if (s1 == NULL)
189				abort();
190			memcpy(s1, s, cp - s);
191			s1[cp - s] = 0;
192
193			s = cp + 1;
194		}
195		if (strlen(s1) == 0) {
196			free(s1);
197			continue;
198		}
199
200		switch (i) {
201		case 0:		/* sectrac */
202			if (getnum(s1, &out->sectrac))
203				errx(EX_USAGE,
204				     "bad numeric value for sectrac: %s", s1);
205			break;
206
207		case 1:		/* secsize */
208			if (getnum(s1, &j))
209				errx(EX_USAGE,
210				     "bad numeric value for secsize: %s", s1);
211			if (j == 128) out->secsize = 0;
212			else if (j == 256) out->secsize = 1;
213			else if (j == 512) out->secsize = 2;
214			else if (j == 1024) out->secsize = 3;
215			else
216				errx(EX_USAGE, "bad sector size %d", j);
217			break;
218
219		case 2:		/* datalen */
220			if (getnum(s1, &j))
221				errx(EX_USAGE,
222				     "bad numeric value for datalen: %s", s1);
223			if (j >= 256)
224				errx(EX_USAGE, "bad datalen %d", j);
225			out->datalen = j;
226			break;
227
228		case 3:		/* gap */
229			if (getnum(s1, &out->gap))
230				errx(EX_USAGE,
231				     "bad numeric value for gap: %s", s1);
232			break;
233
234		case 4:		/* ncyls */
235			if (getnum(s1, &j))
236				errx(EX_USAGE,
237				     "bad numeric value for ncyls: %s", s1);
238			if (j > 85)
239				errx(EX_USAGE, "bad # of cylinders %d", j);
240			out->tracks = j;
241			break;
242
243		case 5:		/* speed */
244			if (getnum(s1, &j))
245				errx(EX_USAGE,
246				     "bad numeric value for speed: %s", s1);
247			switch (type) {
248			default:
249				abort(); /* paranoia */
250
251			case FDT_360K:
252			case FDT_720K:
253				if (j == 250)
254					out->trans = FDC_250KBPS;
255				else
256					errx(EX_USAGE, "bad speed %d", j);
257				break;
258
259			case FDT_12M:
260				if (j == 300)
261					out->trans = FDC_300KBPS;
262				else if (j == 250)
263					out->trans = FDC_250KBPS;
264				else if (j == 500)
265					out->trans = FDC_500KBPS;
266				else
267					errx(EX_USAGE, "bad speed %d", j);
268				break;
269
270			case FDT_288M:
271				if (j == 1000)
272					out->trans = FDC_1MBPS;
273				/* FALLTHROUGH */
274			case FDT_144M:
275				if (j == 250)
276					out->trans = FDC_250KBPS;
277				else if (j == 500)
278					out->trans = FDC_500KBPS;
279				else
280					errx(EX_USAGE, "bad speed %d", j);
281				break;
282			}
283			break;
284
285		case 6:		/* heads */
286			if (getnum(s1, &j))
287				errx(EX_USAGE,
288				     "bad numeric value for heads: %s", s1);
289			if (j == 1 || j == 2)
290				out->heads = j;
291			else
292				errx(EX_USAGE, "bad # of heads %d", j);
293			break;
294
295		case 7:		/* f_gap */
296			if (getnum(s1, &out->f_gap))
297				errx(EX_USAGE,
298				     "bad numeric value for f_gap: %s", s1);
299			break;
300
301		case 8:		/* f_inter */
302			if (getnum(s1, &out->f_inter))
303				errx(EX_USAGE,
304				     "bad numeric value for f_inter: %s", s1);
305			break;
306
307		case 9:		/* offs2 */
308			if (getnum(s1, &out->offset_side2))
309				errx(EX_USAGE,
310				     "bad numeric value for offs2: %s", s1);
311			break;
312
313		default:
314			if (strcmp(s1, "+mfm") == 0)
315				out->flags |= FL_MFM;
316			else if (strcmp(s1, "-mfm") == 0)
317				out->flags &= ~FL_MFM;
318			else if (strcmp(s1, "+auto") == 0)
319				out->flags |= FL_AUTO;
320			else if (strcmp(s1, "-auto") == 0)
321				out->flags &= ~FL_AUTO;
322			else if (strcmp(s1, "+2step") == 0)
323				out->flags |= FL_2STEP;
324			else if (strcmp(s1, "-2step") == 0)
325				out->flags &= ~FL_2STEP;
326			else if (strcmp(s1, "+perpnd") == 0)
327				out->flags |= FL_PERPND;
328			else if (strcmp(s1, "-perpnd") == 0)
329				out->flags &= ~FL_PERPND;
330			else
331				errx(EX_USAGE, "bad flag: %s", s1);
332			break;
333		}
334		free(s1);
335	}
336
337	out->size = out->tracks * out->heads * out->sectrac;
338}
339
340/*
341 * Print a textual translation of the drive (density) type described
342 * by `in' to stdout.  The string uses the same form that is parseable
343 * by parse_fmt().
344 */
345void
346print_fmt(struct fd_type in)
347{
348	int secsize, speed;
349
350	secsize = 128 << in.secsize;
351	switch (in.trans) {
352	case FDC_250KBPS:	speed = 250; break;
353	case FDC_300KBPS:	speed = 300; break;
354	case FDC_500KBPS:	speed = 500; break;
355	case FDC_1MBPS:		speed = 1000; break;
356	default:		speed = 1; break;
357	}
358
359	printf("%d,%d,%#x,%#x,%d,%d,%d,%#x,%d,%d",
360	       in.sectrac, secsize, in.datalen, in.gap, in.tracks,
361	       speed, in.heads, in.f_gap, in.f_inter, in.offset_side2);
362	if (in.flags & FL_MFM)
363		printf(",+mfm");
364	if (in.flags & FL_2STEP)
365		printf(",+2step");
366	if (in.flags & FL_PERPND)
367		printf(",+perpnd");
368	if (in.flags & FL_AUTO)
369		printf(",+auto");
370	putc('\n', stdout);
371}
372
373/*
374 * Based on `size' (in kilobytes), walk through the table of known
375 * densities for drive type `type' and see if we can find one.  If
376 * found, return it (as a pointer to static storage), otherwise return
377 * NULL.
378 */
379struct fd_type *
380get_fmt(int size, enum fd_drivetype type)
381{
382	int i, n;
383	struct fd_type *fdtp;
384
385	switch (type) {
386	default:
387		return (0);
388
389	case FDT_360K:
390		fdtp = fd_types_360k;
391		n = sizeof fd_types_360k / sizeof(struct fd_type);
392		break;
393
394	case FDT_720K:
395		fdtp = fd_types_720k;
396		n = sizeof fd_types_720k / sizeof(struct fd_type);
397		break;
398
399	case FDT_12M:
400		fdtp = fd_types_12m;
401		n = sizeof fd_types_12m / sizeof(struct fd_type);
402		break;
403
404	case FDT_144M:
405		fdtp = fd_types_144m;
406		n = sizeof fd_types_144m / sizeof(struct fd_type);
407		break;
408
409	case FDT_288M:
410		fdtp = fd_types_288m;
411		n = sizeof fd_types_288m / sizeof(struct fd_type);
412		break;
413	}
414
415	if (size == -1)
416		return fd_types_auto;
417
418	for (i = 0; i < n; i++, fdtp++) {
419		fdtp->size = fdtp->sectrac * fdtp->heads * fdtp->tracks;
420		if (((128 << fdtp->secsize) * fdtp->size / 1024) == size)
421			return (fdtp);
422	}
423	return (0);
424}
425
426/*
427 * Parse a number from `s'.  If the string cannot be converted into a
428 * number completely, return -1, otherwise 0.  The result is returned
429 * in `*res'.
430 */
431int
432getnum(const char *s, int *res)
433{
434	unsigned long ul;
435	char *cp;
436
437	ul = strtoul(s, &cp, 0);
438	if (*cp != '\0')
439	  return (-1);
440
441	*res = (int)ul;
442	return (0);
443}
444
445/*
446 * Return a short name and a verbose description for the drive
447 * described by `t'.
448 */
449void
450getname(enum fd_drivetype t, const char **name, const char **descr)
451{
452
453	switch (t) {
454	default:
455		*name = "unknown";
456		*descr = "unknown drive type";
457		break;
458
459	case FDT_360K:
460		*name = "360K";
461		*descr = "5.25\" double-density";
462		break;
463
464	case FDT_12M:
465		*name = "1.2M";
466		*descr = "5.25\" high-density";
467		break;
468
469	case FDT_720K:
470		*name = "720K";
471		*descr = "3.5\" double-density";
472		break;
473
474	case FDT_144M:
475		*name = "1.44M";
476		*descr = "3.5\" high-density";
477		break;
478
479	case FDT_288M:
480		*name = "2.88M";
481		*descr = "3.5\" extra-density";
482		break;
483	}
484}
485