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