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: releng/11.0/usr.sbin/fdread/fdutil.c 297962 2016-04-14 12:46:46Z araujo $
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#ifndef PC98
96#if 0
97	{ FDF_3_2880 },
98#endif
99	{ FDF_3_1722 },
100	{ FDF_3_1476 },
101	{ FDF_3_1440 },
102	{ FDF_3_1200 },
103	{ FDF_3_820 },
104	{ FDF_3_800 },
105	{ FDF_3_720 },
106#endif	/* !PC98 */
107	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
108};
109
110static struct fd_type fd_types_144m[] = {
111#ifdef PC98
112	{ FDF_3_1440 },
113	{ FDF_3_1200 },
114	{ FDF_3_720 },
115	{ FDF_3_360 },
116	{ FDF_3_640 },
117	{ FDF_3_1230 },
118	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
119#else
120	{ FDF_3_1722 },
121	{ FDF_3_1476 },
122	{ FDF_3_1440 },
123	{ FDF_3_1200 },
124	{ FDF_3_820 },
125	{ FDF_3_800 },
126	{ FDF_3_720 },
127	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
128#endif
129};
130
131static struct fd_type fd_types_12m[] = {
132#ifdef PC98
133	{ FDF_5_1200 },
134	{ FDF_5_720 },
135	{ FDF_5_360 },
136	{ FDF_5_640 },
137	{ FDF_5_1230 },
138	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
139#else
140	{ FDF_5_1200 },
141	{ FDF_5_1230 },
142	{ FDF_5_1480 },
143	{ FDF_5_1440 },
144	{ FDF_5_820 },
145	{ FDF_5_800 },
146	{ FDF_5_720 },
147	{ FDF_5_360 | FL_2STEP },
148	{ FDF_5_640 },
149	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
150#endif
151};
152
153static struct fd_type fd_types_720k[] =
154{
155#ifndef PC98
156	{ FDF_3_720 },
157#endif
158	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
159};
160
161static struct fd_type fd_types_360k[] =
162{
163#ifndef PC98
164	{ FDF_5_360 },
165#endif
166	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
167};
168
169
170/*
171 * Parse a format string, and fill in the parameter pointed to by `out'.
172 *
173 * sectrac,secsize,datalen,gap,ncyls,speed,heads,f_gap,f_inter,offs2,flags[...]
174 *
175 * sectrac = sectors per track
176 * secsize = sector size in bytes
177 * datalen = length of sector if secsize == 128
178 * gap     = gap length when reading
179 * ncyls   = number of cylinders
180 * speed   = transfer speed 250/300/500/1000 KB/s
181 * heads   = number of heads
182 * f_gap   = gap length when formatting
183 * f_inter = sector interleave when formatting
184 * offs2   = offset of sectors on side 2
185 * flags   = +/-mfm | +/-2step | +/-perpend
186 *             mfm - use MFM recording
187 *             2step - use 2 steps between cylinders
188 *             perpend - user perpendicular (vertical) recording
189 *
190 * Any omitted value will be passed on from parameter `in'.
191 */
192void
193parse_fmt(const char *s, enum fd_drivetype type,
194	  struct fd_type in, struct fd_type *out)
195{
196	int i, j;
197	const char *cp;
198	char *s1;
199
200	*out = in;
201
202	for (i = 0;; i++) {
203		if (s == NULL)
204			break;
205
206		if ((cp = strchr(s, ',')) == NULL) {
207			s1 = strdup(s);
208			if (s1 == NULL)
209				abort();
210			s = 0;
211		} else {
212			s1 = malloc(cp - s + 1);
213			if (s1 == NULL)
214				abort();
215			memcpy(s1, s, cp - s);
216			s1[cp - s] = 0;
217
218			s = cp + 1;
219		}
220		if (strlen(s1) == 0) {
221			free(s1);
222			continue;
223		}
224
225		switch (i) {
226		case 0:		/* sectrac */
227			if (getnum(s1, &out->sectrac))
228				errx(EX_USAGE,
229				     "bad numeric value for sectrac: %s", s1);
230			break;
231
232		case 1:		/* secsize */
233			if (getnum(s1, &j))
234				errx(EX_USAGE,
235				     "bad numeric value for secsize: %s", s1);
236			if (j == 128) out->secsize = 0;
237			else if (j == 256) out->secsize = 1;
238			else if (j == 512) out->secsize = 2;
239			else if (j == 1024) out->secsize = 3;
240			else
241				errx(EX_USAGE, "bad sector size %d", j);
242			break;
243
244		case 2:		/* datalen */
245			if (getnum(s1, &j))
246				errx(EX_USAGE,
247				     "bad numeric value for datalen: %s", s1);
248			if (j >= 256)
249				errx(EX_USAGE, "bad datalen %d", j);
250			out->datalen = j;
251			break;
252
253		case 3:		/* gap */
254			if (getnum(s1, &out->gap))
255				errx(EX_USAGE,
256				     "bad numeric value for gap: %s", s1);
257			break;
258
259		case 4:		/* ncyls */
260			if (getnum(s1, &j))
261				errx(EX_USAGE,
262				     "bad numeric value for ncyls: %s", s1);
263			if (j > 85)
264				errx(EX_USAGE, "bad # of cylinders %d", j);
265			out->tracks = j;
266			break;
267
268		case 5:		/* speed */
269			if (getnum(s1, &j))
270				errx(EX_USAGE,
271				     "bad numeric value for speed: %s", s1);
272			switch (type) {
273			default:
274				abort(); /* paranoia */
275
276			case FDT_360K:
277			case FDT_720K:
278				if (j == 250)
279					out->trans = FDC_250KBPS;
280				else
281					errx(EX_USAGE, "bad speed %d", j);
282				break;
283
284			case FDT_12M:
285				if (j == 300)
286					out->trans = FDC_300KBPS;
287				else if (j == 250)
288					out->trans = FDC_250KBPS;
289				else if (j == 500)
290					out->trans = FDC_500KBPS;
291				else
292					errx(EX_USAGE, "bad speed %d", j);
293				break;
294
295			case FDT_288M:
296				if (j == 1000)
297					out->trans = FDC_1MBPS;
298				/* FALLTHROUGH */
299			case FDT_144M:
300				if (j == 250)
301					out->trans = FDC_250KBPS;
302				else if (j == 500)
303					out->trans = FDC_500KBPS;
304				else
305					errx(EX_USAGE, "bad speed %d", j);
306				break;
307			}
308			break;
309
310		case 6:		/* heads */
311			if (getnum(s1, &j))
312				errx(EX_USAGE,
313				     "bad numeric value for heads: %s", s1);
314			if (j == 1 || j == 2)
315				out->heads = j;
316			else
317				errx(EX_USAGE, "bad # of heads %d", j);
318			break;
319
320		case 7:		/* f_gap */
321			if (getnum(s1, &out->f_gap))
322				errx(EX_USAGE,
323				     "bad numeric value for f_gap: %s", s1);
324			break;
325
326		case 8:		/* f_inter */
327			if (getnum(s1, &out->f_inter))
328				errx(EX_USAGE,
329				     "bad numeric value for f_inter: %s", s1);
330			break;
331
332		case 9:		/* offs2 */
333			if (getnum(s1, &out->offset_side2))
334				errx(EX_USAGE,
335				     "bad numeric value for offs2: %s", s1);
336			break;
337
338		default:
339			if (strcmp(s1, "+mfm") == 0)
340				out->flags |= FL_MFM;
341			else if (strcmp(s1, "-mfm") == 0)
342				out->flags &= ~FL_MFM;
343			else if (strcmp(s1, "+auto") == 0)
344				out->flags |= FL_AUTO;
345			else if (strcmp(s1, "-auto") == 0)
346				out->flags &= ~FL_AUTO;
347			else if (strcmp(s1, "+2step") == 0)
348				out->flags |= FL_2STEP;
349			else if (strcmp(s1, "-2step") == 0)
350				out->flags &= ~FL_2STEP;
351			else if (strcmp(s1, "+perpnd") == 0)
352				out->flags |= FL_PERPND;
353			else if (strcmp(s1, "-perpnd") == 0)
354				out->flags &= ~FL_PERPND;
355			else
356				errx(EX_USAGE, "bad flag: %s", s1);
357			break;
358		}
359		free(s1);
360	}
361
362	out->size = out->tracks * out->heads * out->sectrac;
363}
364
365/*
366 * Print a textual translation of the drive (density) type described
367 * by `in' to stdout.  The string uses the same form that is parseable
368 * by parse_fmt().
369 */
370void
371print_fmt(struct fd_type in)
372{
373	int secsize, speed;
374
375	secsize = 128 << in.secsize;
376	switch (in.trans) {
377	case FDC_250KBPS:	speed = 250; break;
378	case FDC_300KBPS:	speed = 300; break;
379	case FDC_500KBPS:	speed = 500; break;
380	case FDC_1MBPS:		speed = 1000; break;
381	default:		speed = 1; break;
382	}
383
384	printf("%d,%d,%#x,%#x,%d,%d,%d,%#x,%d,%d",
385	       in.sectrac, secsize, in.datalen, in.gap, in.tracks,
386	       speed, in.heads, in.f_gap, in.f_inter, in.offset_side2);
387	if (in.flags & FL_MFM)
388		printf(",+mfm");
389	if (in.flags & FL_2STEP)
390		printf(",+2step");
391	if (in.flags & FL_PERPND)
392		printf(",+perpnd");
393	if (in.flags & FL_AUTO)
394		printf(",+auto");
395	putc('\n', stdout);
396}
397
398/*
399 * Based on `size' (in kilobytes), walk through the table of known
400 * densities for drive type `type' and see if we can find one.  If
401 * found, return it (as a pointer to static storage), otherwise return
402 * NULL.
403 */
404struct fd_type *
405get_fmt(int size, enum fd_drivetype type)
406{
407	int i, n;
408	struct fd_type *fdtp;
409
410	switch (type) {
411	default:
412		return (0);
413
414	case FDT_360K:
415		fdtp = fd_types_360k;
416		n = sizeof fd_types_360k / sizeof(struct fd_type);
417		break;
418
419	case FDT_720K:
420		fdtp = fd_types_720k;
421		n = sizeof fd_types_720k / sizeof(struct fd_type);
422		break;
423
424	case FDT_12M:
425		fdtp = fd_types_12m;
426		n = sizeof fd_types_12m / sizeof(struct fd_type);
427		break;
428
429	case FDT_144M:
430		fdtp = fd_types_144m;
431		n = sizeof fd_types_144m / sizeof(struct fd_type);
432		break;
433
434	case FDT_288M:
435		fdtp = fd_types_288m;
436		n = sizeof fd_types_288m / sizeof(struct fd_type);
437		break;
438	}
439
440	if (size == -1)
441		return fd_types_auto;
442
443	for (i = 0; i < n; i++, fdtp++) {
444		fdtp->size = fdtp->sectrac * fdtp->heads * fdtp->tracks;
445		if (((128 << fdtp->secsize) * fdtp->size / 1024) == size)
446			return (fdtp);
447	}
448	return (0);
449}
450
451/*
452 * Parse a number from `s'.  If the string cannot be converted into a
453 * number completely, return -1, otherwise 0.  The result is returned
454 * in `*res'.
455 */
456int
457getnum(const char *s, int *res)
458{
459	unsigned long ul;
460	char *cp;
461
462	ul = strtoul(s, &cp, 0);
463	if (*cp != '\0')
464	  return (-1);
465
466	*res = (int)ul;
467	return (0);
468}
469
470/*
471 * Return a short name and a verbose description for the drive
472 * described by `t'.
473 */
474void
475getname(enum fd_drivetype t, const char **name, const char **descr)
476{
477
478	switch (t) {
479	default:
480		*name = "unknown";
481		*descr = "unknown drive type";
482		break;
483
484	case FDT_360K:
485		*name = "360K";
486		*descr = "5.25\" double-density";
487		break;
488
489	case FDT_12M:
490		*name = "1.2M";
491		*descr = "5.25\" high-density";
492		break;
493
494	case FDT_720K:
495		*name = "720K";
496		*descr = "3.5\" double-density";
497		break;
498
499	case FDT_144M:
500		*name = "1.44M";
501		*descr = "3.5\" high-density";
502		break;
503
504	case FDT_288M:
505		*name = "2.88M";
506		*descr = "3.5\" extra-density";
507		break;
508	}
509}
510