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