/* * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 int camverbose = 0; static int scsi_format_track(struct cam_device *cam_dev, union ccb *ccb, int timeout, int cyl, int head, struct format_capacity_descriptor *cap) { struct format_ufi_data ufid; scsi_fillout_format_ufi_data(&ufid, cyl, head, cap); scsi_format_unit_12(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG, 0, /* XXX: lun */ cyl, 0, /* XXX: use default interleave */ (u_int8_t *)&ufid, sizeof(ufid), SSD_FULL_SIZE, timeout); return (cam_send_ccb(cam_dev, ccb)); } /* XXX: Move this to scsi_util.[ch], and thence to the system tree. */ static void scsi_verify(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t byte2, u_int32_t lba_addr, u_int16_t nblocks, u_int8_t sense_len, u_int32_t timeout) { struct scsi_verify *scsi_cmd; scsi_cmd = (struct scsi_verify *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = VERIFY; scsi_cmd->byte2 = byte2; scsi_ulto4b(lba_addr, scsi_cmd->addr); scsi_ulto2b(nblocks, scsi_cmd->len); cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL, 0, sense_len, sizeof(*scsi_cmd), timeout); } /* * XXX: Makes some assumptions about drive geometry right now. * Assumes 2 heads, 1:1 interleave between heads. */ static int scsi_verify_track(struct cam_device *cam_dev, union ccb *ccb, int timeout, int cyl, int head, int spt) { u_int32_t lba_addr; u_int16_t nblocks; #define NHEADS 2 lba_addr = cyl * spt * NHEADS; nblocks = spt * NHEADS; scsi_verify(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG, 0, /* XXX: byte2, lun */ lba_addr, nblocks, SSD_FULL_SIZE, timeout); return (cam_send_ccb(cam_dev, ccb)); #undef NHEADS } 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; #if 0 struct fdc_status fdcs[MAXPRINTERRS]; /* XXX */ int unit; int format, quiet, verify, verify_only, confirm; int fd, c, i, track, error, tracks_per_dot, bytes_per_track, errs; int flags; char *fmtstring; char *devname; const char *name, *descr; #else int unit; int format, quiet, verify, verify_only, confirm; int /*fd,*/ c, i, track, error, tracks_per_dot, bytes_per_track, errs; char *devname; #endif min_mode_cmd_size = UFI_MODE_SENSE_LEN; min_cmd_timeout = UFI_MODE_SENSE_TIMEOUT; format = quiet = verify_only = confirm = 0; verify = 1; #if 0 fmtstring = NULL; #endif while ((c = getopt(argc, argv, "f:nqs:vy")) != -1) switch (c) { #ifdef FIXME case 'f': /* format in kilobytes */ if (getnum(optarg, &format)) { fprintf(stderr, "Bad argument %s to -f option; must be numeric\n", optarg); usage(); } break; #endif 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 0 fprintf(stderr, "Errors encountered:\nCyl Head Sect Error\n"); for (i = 0; i < errs && i < MAXPRINTERRS; i++) { fprintf(stderr, " %2d %2d %2d ", fdcs[i].status[3], fdcs[i].status[4], fdcs[i].status[5]); printstatus(fdcs + i, 1); putc('\n', stderr); } #endif if (errs >= MAXPRINTERRS) fprintf(stderr, "(Further errors not printed.)\n"); } return (errs != 0); }