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