xref: /freebsd/usr.sbin/fdread/fdutil.c (revision 535af610)
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  * $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  */
48 void
49 printstatus(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 
92 static struct fd_type fd_types_auto[1] =
93     { { 0,0,0,0,0,0,0,0,0,0,0,FL_AUTO } };
94 
95 
96 static 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 
110 static 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 
121 static 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 
134 static 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 
140 static 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  */
169 void
170 parse_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  */
347 void
348 print_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  */
381 struct fd_type *
382 get_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  */
433 int
434 getnum(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  */
451 void
452 getname(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