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