xref: /freebsd/usr.sbin/fdread/fdutil.c (revision 0f8f86b71f022b803e99151c19db81b280f245dc)
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 static 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 
104 static 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 
115 static 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 
128 static 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 
133 static 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  */
160 void
161 parse_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  */
334 void
335 print_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  */
366 struct fd_type *
367 get_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  */
417 int
418 getnum(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  */
435 void
436 getname(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