Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/ufdformat/ufdformat.c
/* | |||||
* Copyright (c) 2004 by Bruce M. Simpson. | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* 3. The name of the author may not be used to endorse or promote products | |||||
* derived from this software without specific prior written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR | |||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||||
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, | |||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||||
* POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <sys/types.h> | |||||
#include <sys/stdint.h> | |||||
#include <sys/ioctl.h> | |||||
#include <sys/fdcio.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <unistd.h> | |||||
#include <fcntl.h> | |||||
#include <ctype.h> | |||||
#include <err.h> | |||||
#include <errno.h> | |||||
#include <paths.h> | |||||
#include <sysexits.h> | |||||
#include <cam/cam.h> | |||||
#include <cam/cam_debug.h> | |||||
#include <cam/cam_ccb.h> | |||||
#include <cam/scsi/scsi_all.h> | |||||
#include <cam/scsi/scsi_da.h> | |||||
#include <cam/scsi/scsi_pass.h> | |||||
#include <cam/scsi/scsi_message.h> | |||||
#include <camlib.h> | |||||
#include "scsi_util.h" | |||||
#define SIMULATED_FORMAT_DELAY (200 * 1000) /* 0.2 seconds per cyl/side */ | |||||
#define MAXPRINTERRS 10 | |||||
#define UFI_MODE_SENSE_LEN 10 | |||||
#define UFI_MODE_SENSE_TIMEOUT 5 | |||||
static int | |||||
cvtnum(const char *strng) | |||||
{ | |||||
/* cses */ | |||||
char *endp; | |||||
errno = 0; | |||||
long longnum = strtoul(strng, &endp, 0); | |||||
if (strng == endp || *endp != '\0' || errno == ERANGE) | |||||
return (-1); | |||||
return ((int)longnum); | |||||
} | |||||
static void | |||||
usage(void) | |||||
{ | |||||
errx(EX_USAGE, "usage: ufdformat [-f fmt] [-nqvy] device"); | |||||
} | |||||
static int | |||||
yes(void) | |||||
{ | |||||
char reply[256], *p; | |||||
reply[sizeof(reply) - 1] = 0; | |||||
for (;;) { | |||||
fflush(stdout); | |||||
if (!fgets(reply, sizeof(reply) - 1, stdin)) | |||||
return (0); | |||||
for (p = reply; *p == ' ' || *p == '\t'; ++p) | |||||
continue; | |||||
if (*p == 'y' || *p == 'Y') | |||||
return (1); | |||||
if (*p == 'n' || *p == 'N' || *p == '\n' || *p == '\r') | |||||
return (0); | |||||
printf("Answer `yes' or `no': "); | |||||
} | |||||
} | |||||
int | |||||
main(int argc, char **argv) | |||||
{ | |||||
char driver[DEV_IDLEN+1]; | |||||
union ccb *ccb; | |||||
struct supported_format_descriptors sfds; | |||||
struct fd_type fdt; | |||||
struct cam_device *cam_dev; | |||||
struct scsi_inquiry_data *inq_buf; | |||||
struct flexible_disk_page *flexpage; | |||||
int min_mode_cmd_size, min_cmd_timeout; | |||||
int unit; | |||||
int format, quiet, verify, verify_only, confirm; | |||||
int /*fd,*/ c, i, track, error, tracks_per_dot, bytes_per_track, errs; | |||||
char *devname; | |||||
min_mode_cmd_size = UFI_MODE_SENSE_LEN; | |||||
min_cmd_timeout = UFI_MODE_SENSE_TIMEOUT; | |||||
format = quiet = verify_only = confirm = 0; | |||||
verify = 1; | |||||
while ((c = getopt(argc, argv, "f:nqs:vy")) != -1) | |||||
switch (c) { | |||||
case 'f': /* format in kilobytes */ | |||||
if ((format = cvtnum(optarg)) < 0) { | |||||
fprintf(stderr, | |||||
"Bad argument %s to -f option; must be numeric\n", | |||||
optarg); | |||||
usage(); | |||||
} | |||||
break; | |||||
case 'n': /* don't verify */ | |||||
verify = 0; | |||||
break; | |||||
case 'q': /* quiet */ | |||||
quiet = 1; | |||||
break; | |||||
case 'v': /* verify only */ | |||||
verify = 1; | |||||
verify_only = 1; | |||||
break; | |||||
case 'y': /* confirm */ | |||||
confirm = 1; | |||||
break; | |||||
default: | |||||
usage(); | |||||
} | |||||
if (optind != argc - 1) | |||||
usage(); | |||||
devname = argv[optind]; | |||||
if (cam_get_device(devname, driver, sizeof(driver), &unit) == -1) | |||||
errx(EX_OSERR, "%s", cam_errbuf); | |||||
if ((cam_dev = | |||||
cam_open_spec_device(driver, unit, O_RDWR, NULL)) == NULL) | |||||
errx(EX_OSERR, "cannot open scsi device %s%d", driver, unit); | |||||
/* | |||||
* Check to see if the device is a removable direct-access | |||||
* device. If it is not, reject it, and exit. | |||||
*/ | |||||
inq_buf = (struct scsi_inquiry_data *)malloc( | |||||
sizeof(struct scsi_inquiry_data)); | |||||
error = scsi_get_inquiry(cam_dev, 0, min_cmd_timeout, inq_buf); | |||||
if (error != 0) | |||||
errx(EX_OSERR, "error during scsi inquiry"); | |||||
if (SID_TYPE(inq_buf) != T_DIRECT || !SID_IS_REMOVABLE(inq_buf)) { | |||||
free(inq_buf); | |||||
errx(EX_OSERR, | |||||
"device %s%d is not a removable direct-access device", | |||||
driver, unit); | |||||
} | |||||
free(inq_buf); | |||||
/* | |||||
* Check to see if the device responds to MODE SENSE requests | |||||
* for the Flexible Disk Page, in order to determine its geometry. | |||||
*/ | |||||
flexpage = malloc(SMS_FLEXIBLE_GEOMETRY_PLEN); | |||||
error = scsi_get_flexible_disk_page(cam_dev, flexpage, | |||||
min_mode_cmd_size, min_cmd_timeout); | |||||
if (error != 0) | |||||
errx(EX_OSERR, "error during scsi mode sense"); | |||||
if (flexpage->pg_code != SMS_FLEXIBLE_GEOMETRY_PAGE || | |||||
flexpage->pg_length != SMS_FLEXIBLE_GEOMETRY_PLEN) { | |||||
free(flexpage); | |||||
errx(EX_OSERR, | |||||
"device %s%d returned an invalid flexible geometry page", | |||||
driver, unit); | |||||
} | |||||
/* Obtain supported disk formats. */ | |||||
error = scsi_get_format_capacities(cam_dev, 0, min_cmd_timeout, | |||||
(u_int8_t *)&sfds, sizeof(sfds)); | |||||
if (error != 0) | |||||
errx(EX_OSERR, "error reading format capacity list"); | |||||
/* | |||||
* TODO: Determine how long it's going to take to format this disk. | |||||
* TODO: Stash the track geometry somewhere. | |||||
* scottl says converting to fdc style is ok. | |||||
* XXX: Should we just allocate flexpage on the stack or in bss? | |||||
*/ | |||||
dump_flexible_geometry(flexpage); | |||||
sfds_dump_supported_formats(&sfds); | |||||
/* | |||||
* Convert UFI floppy geometry to fdc(4) style geometry. | |||||
* This sort-of replaces the 'format choice' stuff from fdformat. | |||||
* | |||||
* TODO: Allow the user to select the format; right now we | |||||
* just use the current/maximum entry i.e. the first one. | |||||
*/ | |||||
floppy_geom_scsi_to_fdc(&fdt, flexpage, &sfds.fdcurrmax); | |||||
free(flexpage); /* XXX: We're not likely to be done with this... */ | |||||
bytes_per_track = fdt.sectrac * (128 << fdt.secsize); | |||||
/* XXX 20/40 = 0.5 */ | |||||
tracks_per_dot = (fdt.tracks * fdt.heads + 20) / 40; | |||||
if (verify_only) { | |||||
if (!quiet) | |||||
printf("Verify %dK floppy `%s%d'.\n", | |||||
fdt.tracks * fdt.heads * bytes_per_track / 1024, | |||||
driver, unit); | |||||
} | |||||
else if (!quiet && !confirm) { | |||||
printf ("Format %dK floppy `%s%d'? (y/n): ", | |||||
fdt.tracks * fdt.heads * bytes_per_track / 1024, | |||||
driver, unit); | |||||
if (!yes()) { | |||||
printf("Not confirmed.\n"); | |||||
return (EX_UNAVAILABLE); | |||||
} | |||||
} | |||||
/* Format/verify loop. */ | |||||
if (!quiet) { | |||||
printf("Processing "); | |||||
for (i = 0; i < (fdt.tracks * fdt.heads) / tracks_per_dot; i++) | |||||
putchar('-'); | |||||
printf("\rProcessing "); | |||||
fflush(stdout); | |||||
} | |||||
error = errs = 0; | |||||
/* Grab things we need for a SCSI floppy format. */ | |||||
ccb = cam_getccb(cam_dev); | |||||
if (ccb == NULL) | |||||
errx(EX_OSERR, "couldn't allocate CCB"); | |||||
for (track = 0; track < fdt.tracks * fdt.heads; track++) { | |||||
if (!verify_only) { | |||||
(void)scsi_format_track(cam_dev, ccb, | |||||
min_cmd_timeout, | |||||
track / fdt.heads, track % fdt.heads, | |||||
&sfds.fdcurrmax); | |||||
if (!quiet && !((track + 1) % tracks_per_dot)) { | |||||
putchar('F'); | |||||
fflush(stdout); | |||||
} | |||||
} | |||||
if (verify) { | |||||
if (scsi_verify_track(cam_dev, ccb, min_cmd_timeout, | |||||
track / fdt.heads, track % fdt.heads, | |||||
fdt.sectrac) < 0) { | |||||
/* | |||||
* XXX: Need a way of obtaining sense | |||||
* errors and printing them. | |||||
*/ | |||||
error = 1; | |||||
errs++; | |||||
} | |||||
if (!quiet && !((track + 1) % tracks_per_dot)) { | |||||
if (!verify_only) | |||||
putchar('\b'); | |||||
if (error) { | |||||
putchar('E'); | |||||
error = 0; | |||||
} | |||||
else | |||||
putchar('V'); | |||||
fflush(stdout); | |||||
} | |||||
} | |||||
} | |||||
if (!quiet) | |||||
printf(" done.\n"); | |||||
if (ccb) | |||||
cam_freeccb(ccb); | |||||
if (!quiet && errs) { | |||||
fflush(stdout); | |||||
if (errs >= MAXPRINTERRS) | |||||
fprintf(stderr, "(Further errors not printed.)\n"); | |||||
} | |||||
return (errs != 0); | |||||
} |