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