179110Sjoerg/*
279110Sjoerg * Copyright (c) 2001 Joerg Wunsch
379110Sjoerg *
479110Sjoerg * All rights reserved.
579110Sjoerg *
679110Sjoerg * Redistribution and use in source and binary forms, with or without
779110Sjoerg * modification, are permitted provided that the following conditions
879110Sjoerg * are met:
979110Sjoerg * 1. Redistributions of source code must retain the above copyright
1079110Sjoerg *    notice, this list of conditions and the following disclaimer.
1179110Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1279110Sjoerg *    notice, this list of conditions and the following disclaimer in the
1379110Sjoerg *    documentation and/or other materials provided with the distribution.
1479110Sjoerg *
1579110Sjoerg * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
1679110Sjoerg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1779110Sjoerg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1879110Sjoerg * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
1979110Sjoerg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2079110Sjoerg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2179110Sjoerg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2279110Sjoerg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2379110Sjoerg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2479110Sjoerg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2579110Sjoerg *
2679110Sjoerg * $FreeBSD$
2779110Sjoerg */
2879110Sjoerg
2987992Sjoerg#include <dev/ic/nec765.h>
3087992Sjoerg
3179110Sjoerg#include <sys/fdcio.h>
3279110Sjoerg
3387992Sjoerg#include <err.h>
3479110Sjoerg#include <stdio.h>
3587992Sjoerg#include <stdlib.h>
3679110Sjoerg#include <string.h>
3787992Sjoerg#include <sysexits.h>
3879110Sjoerg
3979110Sjoerg#include "fdutil.h"
4079110Sjoerg
4187992Sjoerg/*
4287992Sjoerg * Decode the FDC status pointed to by `fdcsp', and print a textual
4387992Sjoerg * translation to stderr.  If `terse' is false, the numerical FDC
4487992Sjoerg * register status is printed, too.
4587992Sjoerg */
4679110Sjoergvoid
4779110Sjoergprintstatus(struct fdc_status *fdcsp, int terse)
4879110Sjoerg{
4979110Sjoerg	char msgbuf[100];
5079110Sjoerg
5179110Sjoerg	if (!terse)
5279110Sjoerg		fprintf(stderr,
5379110Sjoerg		"\nFDC status ST0=%#x ST1=%#x ST2=%#x C=%u H=%u R=%u N=%u:\n",
5479110Sjoerg			fdcsp->status[0] & 0xff,
5579110Sjoerg			fdcsp->status[1] & 0xff,
5679110Sjoerg			fdcsp->status[2] & 0xff,
5779110Sjoerg			fdcsp->status[3] & 0xff,
5879110Sjoerg			fdcsp->status[4] & 0xff,
5979110Sjoerg			fdcsp->status[5] & 0xff,
6079110Sjoerg			fdcsp->status[6] & 0xff);
6179110Sjoerg
62134081Sphk	if ((fdcsp->status[0] & NE7_ST0_IC_RC) == 0) {
63134081Sphk		sprintf(msgbuf, "timeout");
64134081Sphk	} else if ((fdcsp->status[0] & NE7_ST0_IC_RC) != NE7_ST0_IC_AT) {
6579110Sjoerg		sprintf(msgbuf, "unexcpted interrupt code %#x",
6679110Sjoerg			fdcsp->status[0] & NE7_ST0_IC_RC);
6779110Sjoerg	} else {
6879110Sjoerg		strcpy(msgbuf, "unexpected error code in ST1/ST2");
6979110Sjoerg
7079110Sjoerg		if (fdcsp->status[1] & NE7_ST1_EN)
7179110Sjoerg			strcpy(msgbuf, "end of cylinder (wrong format)");
7279110Sjoerg		else if (fdcsp->status[1] & NE7_ST1_DE) {
7379110Sjoerg			if (fdcsp->status[2] & NE7_ST2_DD)
7479110Sjoerg				strcpy(msgbuf, "CRC error in data field");
7579110Sjoerg			else
7679110Sjoerg				strcpy(msgbuf, "CRC error in ID field");
7779110Sjoerg		} else if (fdcsp->status[1] & NE7_ST1_MA) {
7879110Sjoerg			if (fdcsp->status[2] & NE7_ST2_MD)
7979110Sjoerg				strcpy(msgbuf, "no address mark in data field");
8079110Sjoerg			else
8179110Sjoerg				strcpy(msgbuf, "no address mark in ID field");
8279110Sjoerg		} else if (fdcsp->status[2] & NE7_ST2_WC)
8379110Sjoerg			strcpy(msgbuf, "wrong cylinder (format mismatch)");
8479110Sjoerg		else if (fdcsp->status[1] & NE7_ST1_ND)
8579110Sjoerg			strcpy(msgbuf, "no data (sector not found)");
8679110Sjoerg	}
8779110Sjoerg	fputs(msgbuf, stderr);
8879110Sjoerg}
8979110Sjoerg
90134081Sphkstatic struct fd_type fd_types_auto[1] =
91134081Sphk    { { 0,0,0,0,0,0,0,0,0,0,0,FL_AUTO } };
92126230Sphk
93127522Snyan
94134081Sphkstatic struct fd_type fd_types_288m[] = {
95127522Snyan#if 0
96134081Sphk	{ FDF_3_2880 },
97127522Snyan#endif
98134081Sphk	{ FDF_3_1722 },
99134081Sphk	{ FDF_3_1476 },
100134081Sphk	{ FDF_3_1440 },
101134081Sphk	{ FDF_3_1200 },
102134081Sphk	{ FDF_3_820 },
103134081Sphk	{ FDF_3_800 },
104134081Sphk	{ FDF_3_720 },
105139905Sdelphij	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
106127522Snyan};
107127522Snyan
108127522Snyanstatic struct fd_type fd_types_144m[] = {
109137459Snyan#ifdef PC98
110137459Snyan#if 0
111134081Sphk	{ FDF_3_1722 },
112134081Sphk	{ FDF_3_1476 },
113137459Snyan#endif
114134081Sphk	{ FDF_3_1440 },
115134081Sphk	{ FDF_3_1200 },
116137459Snyan#if 0
117134081Sphk	{ FDF_3_820 },
118134081Sphk	{ FDF_3_800 },
119137459Snyan#endif
120134081Sphk	{ FDF_3_720 },
121137459Snyan	{ FDF_3_360 },
122137459Snyan	{ FDF_3_640 },
123137459Snyan	{ FDF_3_1230 },
124137459Snyan#if 0
125137459Snyan	{ FDF_3_1280 },
126137459Snyan	{ FDF_3_1480 },
127137459Snyan	{ FDF_3_1640 },
128137459Snyan#endif
129139905Sdelphij	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
130137459Snyan#else
131137459Snyan	{ FDF_3_1722 },
132137459Snyan	{ FDF_3_1476 },
133137459Snyan	{ FDF_3_1440 },
134137459Snyan	{ FDF_3_1200 },
135137459Snyan	{ FDF_3_820 },
136137459Snyan	{ FDF_3_800 },
137137459Snyan	{ FDF_3_720 },
138139905Sdelphij	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
139137459Snyan#endif
140127522Snyan};
141127522Snyan
142134081Sphkstatic struct fd_type fd_types_12m[] = {
143137459Snyan#ifdef PC98
144134081Sphk	{ FDF_5_1200 },
145137459Snyan#if 0
146137459Snyan	{ FDF_5_820 },
147137459Snyan	{ FDF_5_800 },
148137459Snyan#endif
149137459Snyan	{ FDF_5_720 },
150137459Snyan	{ FDF_5_360 },
151137459Snyan	{ FDF_5_640 },
152134081Sphk	{ FDF_5_1230 },
153137459Snyan#if 0
154137459Snyan	{ FDF_5_1280 },
155137459Snyan#endif
156139905Sdelphij	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
157137459Snyan#else
158137459Snyan	{ FDF_5_1200 },
159137459Snyan	{ FDF_5_1230 },
160134081Sphk	{ FDF_5_1480 },
161134081Sphk	{ FDF_5_1440 },
162134081Sphk	{ FDF_5_820 },
163134081Sphk	{ FDF_5_800 },
164134081Sphk	{ FDF_5_720 },
165134081Sphk	{ FDF_5_360 | FL_2STEP },
166134081Sphk	{ FDF_5_640 },
167139905Sdelphij	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
168137459Snyan#endif
16987992Sjoerg};
17087992Sjoerg
17187992Sjoergstatic struct fd_type fd_types_720k[] =
17287992Sjoerg{
173134081Sphk	{ FDF_3_720 },
174139905Sdelphij	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
17587992Sjoerg};
17687992Sjoerg
17787992Sjoergstatic struct fd_type fd_types_360k[] =
17887992Sjoerg{
179134081Sphk	{ FDF_5_360 },
180139905Sdelphij	{ 0,0,0,0,0,0,0,0,0,0,0,0 }
18187992Sjoerg};
18287992Sjoerg
183127522Snyan
18487992Sjoerg/*
18587992Sjoerg * Parse a format string, and fill in the parameter pointed to by `out'.
18687992Sjoerg *
18787992Sjoerg * sectrac,secsize,datalen,gap,ncyls,speed,heads,f_gap,f_inter,offs2,flags[...]
18887992Sjoerg *
18987992Sjoerg * sectrac = sectors per track
19087992Sjoerg * secsize = sector size in bytes
19187992Sjoerg * datalen = length of sector if secsize == 128
19287992Sjoerg * gap     = gap length when reading
19387992Sjoerg * ncyls   = number of cylinders
19487992Sjoerg * speed   = transfer speed 250/300/500/1000 KB/s
19587992Sjoerg * heads   = number of heads
19687992Sjoerg * f_gap   = gap length when formatting
19787992Sjoerg * f_inter = sector interleave when formatting
19887992Sjoerg * offs2   = offset of sectors on side 2
19987992Sjoerg * flags   = +/-mfm | +/-2step | +/-perpend
20087992Sjoerg *             mfm - use MFM recording
20187992Sjoerg *             2step - use 2 steps between cylinders
20287992Sjoerg *             perpend - user perpendicular (vertical) recording
20387992Sjoerg *
20487992Sjoerg * Any omitted value will be passed on from parameter `in'.
20587992Sjoerg */
20687992Sjoergvoid
20787992Sjoergparse_fmt(const char *s, enum fd_drivetype type,
20887992Sjoerg	  struct fd_type in, struct fd_type *out)
20987992Sjoerg{
21087992Sjoerg	int i, j;
21187992Sjoerg	const char *cp;
21287992Sjoerg	char *s1;
21387992Sjoerg
21487992Sjoerg	*out = in;
21587992Sjoerg
21687992Sjoerg	for (i = 0;; i++) {
21787992Sjoerg		if (s == 0)
21887992Sjoerg			break;
21987992Sjoerg
22087992Sjoerg		if ((cp = strchr(s, ',')) == 0) {
22187992Sjoerg			s1 = strdup(s);
22287992Sjoerg			if (s1 == NULL)
22387992Sjoerg				abort();
22487992Sjoerg			s = 0;
22587992Sjoerg		} else {
22687992Sjoerg			s1 = malloc(cp - s + 1);
22787992Sjoerg			if (s1 == NULL)
22887992Sjoerg				abort();
22987992Sjoerg			memcpy(s1, s, cp - s);
23087992Sjoerg			s1[cp - s] = 0;
23187992Sjoerg
23287992Sjoerg			s = cp + 1;
23387992Sjoerg		}
23487992Sjoerg		if (strlen(s1) == 0) {
23587992Sjoerg			free(s1);
23687992Sjoerg			continue;
23787992Sjoerg		}
23887992Sjoerg
23987992Sjoerg		switch (i) {
24087992Sjoerg		case 0:		/* sectrac */
24187992Sjoerg			if (getnum(s1, &out->sectrac))
24287992Sjoerg				errx(EX_USAGE,
24387992Sjoerg				     "bad numeric value for sectrac: %s", s1);
24487992Sjoerg			break;
24587992Sjoerg
24687992Sjoerg		case 1:		/* secsize */
24787992Sjoerg			if (getnum(s1, &j))
24887992Sjoerg				errx(EX_USAGE,
24987992Sjoerg				     "bad numeric value for secsize: %s", s1);
25087992Sjoerg			if (j == 128) out->secsize = 0;
25187992Sjoerg			else if (j == 256) out->secsize = 1;
25287992Sjoerg			else if (j == 512) out->secsize = 2;
25387992Sjoerg			else if (j == 1024) out->secsize = 3;
25487992Sjoerg			else
25587992Sjoerg				errx(EX_USAGE, "bad sector size %d", j);
25687992Sjoerg			break;
25787992Sjoerg
25887992Sjoerg		case 2:		/* datalen */
25987992Sjoerg			if (getnum(s1, &j))
26087992Sjoerg				errx(EX_USAGE,
26187992Sjoerg				     "bad numeric value for datalen: %s", s1);
26287992Sjoerg			if (j >= 256)
26387992Sjoerg				errx(EX_USAGE, "bad datalen %d", j);
26487992Sjoerg			out->datalen = j;
26587992Sjoerg			break;
26687992Sjoerg
26787992Sjoerg		case 3:		/* gap */
26887992Sjoerg			if (getnum(s1, &out->gap))
26987992Sjoerg				errx(EX_USAGE,
27087992Sjoerg				     "bad numeric value for gap: %s", s1);
27187992Sjoerg			break;
27287992Sjoerg
27387992Sjoerg		case 4:		/* ncyls */
27487992Sjoerg			if (getnum(s1, &j))
27587992Sjoerg				errx(EX_USAGE,
27687992Sjoerg				     "bad numeric value for ncyls: %s", s1);
27787992Sjoerg			if (j > 85)
27887992Sjoerg				errx(EX_USAGE, "bad # of cylinders %d", j);
27987992Sjoerg			out->tracks = j;
28087992Sjoerg			break;
28187992Sjoerg
28287992Sjoerg		case 5:		/* speed */
28387992Sjoerg			if (getnum(s1, &j))
28487992Sjoerg				errx(EX_USAGE,
28587992Sjoerg				     "bad numeric value for speed: %s", s1);
28687992Sjoerg			switch (type) {
28787992Sjoerg			default:
28887992Sjoerg				abort(); /* paranoia */
28987992Sjoerg
29087992Sjoerg			case FDT_360K:
29187992Sjoerg			case FDT_720K:
29287992Sjoerg				if (j == 250)
29387992Sjoerg					out->trans = FDC_250KBPS;
294127522Snyan				else
29587992Sjoerg					errx(EX_USAGE, "bad speed %d", j);
29687992Sjoerg				break;
29787992Sjoerg
29887992Sjoerg			case FDT_12M:
29987992Sjoerg				if (j == 300)
30087992Sjoerg					out->trans = FDC_300KBPS;
301134081Sphk				else if (j == 250)
302134081Sphk					out->trans = FDC_250KBPS;
30387992Sjoerg				else if (j == 500)
30487992Sjoerg					out->trans = FDC_500KBPS;
30587992Sjoerg				else
306127522Snyan					errx(EX_USAGE, "bad speed %d", j);
30787992Sjoerg				break;
30887992Sjoerg
30987992Sjoerg			case FDT_288M:
31087992Sjoerg				if (j == 1000)
31187992Sjoerg					out->trans = FDC_1MBPS;
31287992Sjoerg				/* FALLTHROUGH */
31387992Sjoerg			case FDT_144M:
31487992Sjoerg				if (j == 250)
31587992Sjoerg					out->trans = FDC_250KBPS;
31687992Sjoerg				else if (j == 500)
31787992Sjoerg					out->trans = FDC_500KBPS;
31887992Sjoerg				else
319127522Snyan					errx(EX_USAGE, "bad speed %d", j);
32087992Sjoerg				break;
32187992Sjoerg			}
32287992Sjoerg			break;
32387992Sjoerg
32487992Sjoerg		case 6:		/* heads */
32587992Sjoerg			if (getnum(s1, &j))
32687992Sjoerg				errx(EX_USAGE,
32787992Sjoerg				     "bad numeric value for heads: %s", s1);
32887992Sjoerg			if (j == 1 || j == 2)
32987992Sjoerg				out->heads = j;
33087992Sjoerg			else
33187992Sjoerg				errx(EX_USAGE, "bad # of heads %d", j);
33287992Sjoerg			break;
33387992Sjoerg
33487992Sjoerg		case 7:		/* f_gap */
33587992Sjoerg			if (getnum(s1, &out->f_gap))
33687992Sjoerg				errx(EX_USAGE,
33787992Sjoerg				     "bad numeric value for f_gap: %s", s1);
33887992Sjoerg			break;
33987992Sjoerg
34087992Sjoerg		case 8:		/* f_inter */
34187992Sjoerg			if (getnum(s1, &out->f_inter))
34287992Sjoerg				errx(EX_USAGE,
34387992Sjoerg				     "bad numeric value for f_inter: %s", s1);
34487992Sjoerg			break;
34587992Sjoerg
34687992Sjoerg		case 9:		/* offs2 */
34787992Sjoerg			if (getnum(s1, &out->offset_side2))
34887992Sjoerg				errx(EX_USAGE,
34987992Sjoerg				     "bad numeric value for offs2: %s", s1);
35087992Sjoerg			break;
35187992Sjoerg
35287992Sjoerg		default:
35387992Sjoerg			if (strcmp(s1, "+mfm") == 0)
35487992Sjoerg				out->flags |= FL_MFM;
35587992Sjoerg			else if (strcmp(s1, "-mfm") == 0)
35687992Sjoerg				out->flags &= ~FL_MFM;
357134081Sphk			else if (strcmp(s1, "+auto") == 0)
358134081Sphk				out->flags |= FL_AUTO;
359134081Sphk			else if (strcmp(s1, "-auto") == 0)
360134081Sphk				out->flags &= ~FL_AUTO;
36187992Sjoerg			else if (strcmp(s1, "+2step") == 0)
36287992Sjoerg				out->flags |= FL_2STEP;
36387992Sjoerg			else if (strcmp(s1, "-2step") == 0)
36487992Sjoerg				out->flags &= ~FL_2STEP;
36587992Sjoerg			else if (strcmp(s1, "+perpnd") == 0)
36687992Sjoerg				out->flags |= FL_PERPND;
36787992Sjoerg			else if (strcmp(s1, "-perpnd") == 0)
36887992Sjoerg				out->flags &= ~FL_PERPND;
36987992Sjoerg			else
37087992Sjoerg				errx(EX_USAGE, "bad flag: %s", s1);
37187992Sjoerg			break;
37287992Sjoerg		}
37387992Sjoerg		free(s1);
37487992Sjoerg	}
37587992Sjoerg
376126230Sphk	out->size = out->tracks * out->heads * out->sectrac;
37787992Sjoerg}
37887992Sjoerg
37987992Sjoerg/*
38087992Sjoerg * Print a textual translation of the drive (density) type described
38187992Sjoerg * by `in' to stdout.  The string uses the same form that is parseable
38287992Sjoerg * by parse_fmt().
38387992Sjoerg */
38487992Sjoergvoid
38587992Sjoergprint_fmt(struct fd_type in)
38687992Sjoerg{
38787992Sjoerg	int secsize, speed;
38887992Sjoerg
38987992Sjoerg	secsize = 128 << in.secsize;
39087992Sjoerg	switch (in.trans) {
39187992Sjoerg	case FDC_250KBPS:	speed = 250; break;
39287992Sjoerg	case FDC_300KBPS:	speed = 300; break;
39387992Sjoerg	case FDC_500KBPS:	speed = 500; break;
39487992Sjoerg	case FDC_1MBPS:		speed = 1000; break;
39587992Sjoerg	default:		speed = 1; break;
39687992Sjoerg	}
39787992Sjoerg
39887992Sjoerg	printf("%d,%d,%#x,%#x,%d,%d,%d,%#x,%d,%d",
39987992Sjoerg	       in.sectrac, secsize, in.datalen, in.gap, in.tracks,
40087992Sjoerg	       speed, in.heads, in.f_gap, in.f_inter, in.offset_side2);
40187992Sjoerg	if (in.flags & FL_MFM)
40287992Sjoerg		printf(",+mfm");
40387992Sjoerg	if (in.flags & FL_2STEP)
40487992Sjoerg		printf(",+2step");
40587992Sjoerg	if (in.flags & FL_PERPND)
40687992Sjoerg		printf(",+perpnd");
407134081Sphk	if (in.flags & FL_AUTO)
408134081Sphk		printf(",+auto");
40987992Sjoerg	putc('\n', stdout);
41087992Sjoerg}
41187992Sjoerg
41287992Sjoerg/*
41387992Sjoerg * Based on `size' (in kilobytes), walk through the table of known
41487992Sjoerg * densities for drive type `type' and see if we can find one.  If
41587992Sjoerg * found, return it (as a pointer to static storage), otherwise return
41687992Sjoerg * NULL.
41787992Sjoerg */
41887992Sjoergstruct fd_type *
41987992Sjoergget_fmt(int size, enum fd_drivetype type)
42087992Sjoerg{
42187992Sjoerg	int i, n;
42287992Sjoerg	struct fd_type *fdtp;
42387992Sjoerg
42487992Sjoerg	switch (type) {
42587992Sjoerg	default:
42687992Sjoerg		return (0);
42787992Sjoerg
42887992Sjoerg	case FDT_360K:
42987992Sjoerg		fdtp = fd_types_360k;
43087992Sjoerg		n = sizeof fd_types_360k / sizeof(struct fd_type);
43187992Sjoerg		break;
43287992Sjoerg
43387992Sjoerg	case FDT_720K:
43487992Sjoerg		fdtp = fd_types_720k;
43587992Sjoerg		n = sizeof fd_types_720k / sizeof(struct fd_type);
43687992Sjoerg		break;
43787992Sjoerg
43887992Sjoerg	case FDT_12M:
43987992Sjoerg		fdtp = fd_types_12m;
44087992Sjoerg		n = sizeof fd_types_12m / sizeof(struct fd_type);
44187992Sjoerg		break;
44287992Sjoerg
44387992Sjoerg	case FDT_144M:
44487992Sjoerg		fdtp = fd_types_144m;
44587992Sjoerg		n = sizeof fd_types_144m / sizeof(struct fd_type);
44687992Sjoerg		break;
44787992Sjoerg
44887992Sjoerg	case FDT_288M:
44987992Sjoerg		fdtp = fd_types_288m;
45087992Sjoerg		n = sizeof fd_types_288m / sizeof(struct fd_type);
45187992Sjoerg		break;
45287992Sjoerg	}
45387992Sjoerg
454126230Sphk	if (size == -1)
455126230Sphk		return fd_types_auto;
456126230Sphk
457134081Sphk	for (i = 0; i < n; i++, fdtp++) {
458134081Sphk		fdtp->size = fdtp->sectrac * fdtp->heads * fdtp->tracks;
459127522Snyan		if (((128 << fdtp->secsize) * fdtp->size / 1024) == size)
460127522Snyan			return (fdtp);
461134081Sphk	}
46287992Sjoerg	return (0);
46387992Sjoerg}
46487992Sjoerg
46587992Sjoerg/*
46687992Sjoerg * Parse a number from `s'.  If the string cannot be converted into a
46787992Sjoerg * number completely, return -1, otherwise 0.  The result is returned
46887992Sjoerg * in `*res'.
46987992Sjoerg */
47087992Sjoergint
47187992Sjoerggetnum(const char *s, int *res)
47287992Sjoerg{
47387992Sjoerg	unsigned long ul;
47487992Sjoerg	char *cp;
47587992Sjoerg
47687992Sjoerg	ul = strtoul(s, &cp, 0);
47787992Sjoerg	if (*cp != '\0')
47887992Sjoerg	  return (-1);
47987992Sjoerg
48087992Sjoerg	*res = (int)ul;
48187992Sjoerg	return (0);
48287992Sjoerg}
48387992Sjoerg
48487992Sjoerg/*
48587992Sjoerg * Return a short name and a verbose description for the drive
48687992Sjoerg * described by `t'.
48787992Sjoerg */
48887992Sjoergvoid
48987992Sjoerggetname(enum fd_drivetype t, const char **name, const char **descr)
49087992Sjoerg{
49187992Sjoerg
49287992Sjoerg	switch (t) {
49387992Sjoerg	default:
49487992Sjoerg		*name = "unknown";
49587992Sjoerg		*descr = "unknown drive type";
49687992Sjoerg		break;
49787992Sjoerg
49887992Sjoerg	case FDT_360K:
49987992Sjoerg		*name = "360K";
50087992Sjoerg		*descr = "5.25\" double-density";
50187992Sjoerg		break;
50287992Sjoerg
50387992Sjoerg	case FDT_12M:
50487992Sjoerg		*name = "1.2M";
50587992Sjoerg		*descr = "5.25\" high-density";
50687992Sjoerg		break;
50787992Sjoerg
50887992Sjoerg	case FDT_720K:
50987992Sjoerg		*name = "720K";
51087992Sjoerg		*descr = "3.5\" double-density";
51187992Sjoerg		break;
51287992Sjoerg
51387992Sjoerg	case FDT_144M:
51487992Sjoerg		*name = "1.44M";
51587992Sjoerg		*descr = "3.5\" high-density";
51687992Sjoerg		break;
51787992Sjoerg
51887992Sjoerg	case FDT_288M:
51987992Sjoerg		*name = "2.88M";
52087992Sjoerg		*descr = "3.5\" extra-density";
52187992Sjoerg		break;
52287992Sjoerg	}
52387992Sjoerg}
524