Index: usr.sbin/Makefile =================================================================== --- usr.sbin/Makefile +++ usr.sbin/Makefile @@ -201,6 +201,7 @@ SUBDIR.${MK_TOOLCHAIN}+= crunch SUBDIR.${MK_UNBOUND}+= unbound SUBDIR.${MK_USB}+= uathload +SUBDIR.${MK_USB}+= ufdformat SUBDIR.${MK_USB}+= uhsoctl SUBDIR.${MK_USB}+= usbconfig SUBDIR.${MK_USB}+= usbdump Index: usr.sbin/ufdformat/Makefile =================================================================== --- usr.sbin/ufdformat/Makefile +++ usr.sbin/ufdformat/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +PROG= ufdformat +SRCS= ufdformat.c scsi_util.c + +WARNS?= 2 + +LIBADD= cam sbuf +MAN= ufdformat.1 + +.include Index: usr.sbin/ufdformat/scsi_util.h =================================================================== --- usr.sbin/ufdformat/scsi_util.h +++ usr.sbin/ufdformat/scsi_util.h @@ -0,0 +1,115 @@ +/* + * 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. + * + * $FreeBSD$ + */ + +#ifndef _SCSI_UTIL_H +#define _SCSI_UTIL_H + +/* XXX: Push to ufdformat.c */ +#define UFI_MODE_SENSE_LEN 10 +#define UFI_MODE_SENSE_TIMEOUT 5 + +/* + * 12-byte FORMAT_UNIT command. Only used by UFI and SFF8077i devices. + * XXX: Push to kernel headers? + */ +struct scsi_format_unit_12 +{ + uint8_t opcode; + uint8_t byte2; /* Assignments as for 6-byte FORMAT UNIT */ +#define FUFI_ALL_TRACKS 0xFF /* Format the entire unit */ +#define FUFI_SIDE_LOWER 0x0 /* Lower LBA */ +#define FUFI_SIDE_UPPER 0x1 /* Higher LBA */ +#define FUFI_SIDE_ALL FUFI_SIDE_LOWER + uint8_t track; + uint8_t interleave[2]; + uint8_t unused00[2]; + uint8_t param_len[2]; + uint8_t unused01[3]; +}; + +struct format_ufi_data { + struct format_defect_list_header fdh; + struct format_capacity_descriptor fcd; +}; + +struct supported_format_descriptors { + struct format_capacity_list_header fh; + struct format_capacity_descriptor fdcurrmax; + struct format_capacity_descriptor fdsupp[4]; +}; + +void dump_flexible_geometry(struct flexible_disk_page *); + +void floppy_geom_scsi_to_fdc(struct fd_type *, + const struct flexible_disk_page *, + const struct format_capacity_descriptor *); + +/* XXX: These two move to kernel headers eventually. */ +void scsi_format_unit_12(struct ccb_scsiio *, u_int32_t, + void (*)(struct cam_periph *, union ccb *), + u_int8_t, u_int8_t, u_int8_t, u_int16_t, u_int8_t *, u_int32_t, + u_int8_t, u_int32_t); +void scsi_read_format_capacities(struct ccb_scsiio *, u_int32_t, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, u_int8_t, u_int8_t *, + u_int32_t, u_int8_t, u_int32_t); + +void scsi_fillout_format_ufi_data(struct format_ufi_data *, + u_int8_t, u_int8_t, struct format_capacity_descriptor *); +int scsi_get_format_capacities(struct cam_device *, int, + int, u_int8_t *, u_int32_t); +int scsi_get_mode_sense(struct cam_device *, int, int, int, int, int, + u_int8_t *, int, int); +int scsi_get_inquiry(struct cam_device *, int, int, + struct scsi_inquiry_data *); +int scsi_get_flexible_disk_page(struct cam_device *, + struct flexible_disk_page *, int, int); +int scsi_format_track(struct cam_device *, union ccb *, + int, int, int head, struct format_capacity_descriptor *); +void scsi_verify(struct ccb_scsiio *, u_int32_t, + void (*)(struct cam_periph *, union ccb *), u_int8_t, + u_int8_t, u_int32_t, u_int16_t, u_int8_t, u_int32_t); +int scsi_verify_track(struct cam_device *, union ccb *, + int, int, int head, int); + +void sfds_dump_supported_formats(struct supported_format_descriptors *); +void sfds_dump_desc(char *hdr, int, struct format_capacity_descriptor *); + +/* Macros for working with mode pages. */ +#define MODE_PAGE_HEADER_6(mh)\ + (struct scsi_mode_page_header *)find_mode_page_6(mh) + +#define MODE_PAGE_HEADER_10(mh)\ + (struct scsi_mode_page_header *)find_mode_page_10(mh) + +#define MODE_PAGE_DATA(mph)\ + (u_int8_t *)(mph) + sizeof(struct scsi_mode_page_header) + +#endif /* _SCSI_UTIL_H */ Index: usr.sbin/ufdformat/scsi_util.c =================================================================== --- usr.sbin/ufdformat/scsi_util.c +++ usr.sbin/ufdformat/scsi_util.c @@ -0,0 +1,572 @@ +/* + * 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 /* XXX */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi_util.h" + +int camverbose = 0; + +void +dump_flexible_geometry(struct flexible_disk_page *flex) +{ + u_int16_t tracks; + u_int16_t sectorsize; + + tracks = scsi_2btoul(&flex->ncyl_1); + sectorsize = scsi_2btoul(&flex->bytes_s_1); + fprintf(stderr, + "Geometry: %lu cyl %u heads %u secpercyl %lu bytespersec\n", + (unsigned long)tracks, + flex->nheads, + flex->sec_per_track, + (unsigned long)sectorsize); +} + +/* + * Convert UFI geometry to fdc(4) style geometry, using information + * obtained via MODE_SENSE and READ_FORMAT_CAPACITIES. + * fcl points to the chosen format_capacity_list entry (normally the + * current or maximum value) which is to be used to format the disk. + */ +void +floppy_geom_scsi_to_fdc(struct fd_type *fdt, + const struct flexible_disk_page *flexpage, + const struct format_capacity_descriptor *fcd) +{ + + bzero(fdt, sizeof(*fdt)); + fdt->sectrac = flexpage->sec_per_track; + /* + * Convert sector size to a secsize code for fd_type. + * Assume that 'sector' and 'block' mean the same thing here. + * Assume that the sector size is always a perfect power of two. + */ + fdt->secsize = ffs(scsi_3btol((u_int8_t *)&fcd->block_length)) - 8; + /*fdt->secsize = ffs(scsi_2btoul(&flexpage->bytes_s_1)) - 8;*/ + fdt->datalen = 0; /* XXX: Gross assumption */ + fdt->gap = 0; /* XXX: Gross assumption */ + fdt->tracks = scsi_2btoul((u_int8_t *)&flexpage->ncyl_1); + fdt->size = scsi_4btoul((u_int8_t *)&fcd->nblocks); /* # LBAs */ + fdt->trans = FDC_500KBPS; /* XXX: Gross assumption */ + /*scsi_2btoul(&flexpage->xfr_rate_1);*/ + fdt->heads = flexpage->nheads; + fdt->f_gap = 0; /* XXX */ + fdt->f_inter = 0; /* XXX */ + fdt->offset_side2 = 0; /* XXX */ + fdt->flags = FL_MFM; /* not specified within UFI */ +} + +/* + * Fill-out the additional parameter data required by UFI/SFF8070i + * for the 12-byte FORMAT_UNIT command. + * + * Notes: + * ufid points to the structure to be filled out. + * cap points to the disk format to be used, as returned by the + * GET_FORMAT_CAPACITIES command. + * track is the current track to be formatted, or FUFI_ALL_TRACKS + * for a complete format of the unit. + * side is the current side to be formatted, or FUFI_SIDE_NONE for + * a complete format of the unit. + */ +void +scsi_fillout_format_ufi_data(struct format_ufi_data *ufid, + u_int8_t track, u_int8_t side, + struct format_capacity_descriptor *cap) +{ + + bzero(ufid, sizeof(*ufid)); + + /* + * Always set the Format Options Valid (FOV) and Disable + * CeRTification bits. All other bits should be cleared. + * + * Set the single track bit if a track-by-track format + * was specified. + */ + ufid->fdh.byte2 = FU_DLH_FOV | FU_DLH_DCRT; + ufid->fdh.byte2 |= (track == FUFI_ALL_TRACKS) ? 0 : FU_DLH_STPF; + + /* + * Set the 'upper side' bit if both a track-by-track forat + * and a side were specified. + * Specifies which side if it's a track-by-track format; + * 0 is the bottom side (lower LBA), 1 is the upper side. + */ + if (track != FUFI_ALL_TRACKS && side == FUFI_SIDE_UPPER) + ufid->fdh.byte2 |= FU_DLH_VS; + + /* Fillout format_capacity_descriptor with chosen LBA format. */ + bcopy(cap, &ufid->fcd, sizeof(*cap)); + ufid->fcd.byte4 = 0x00; + + /* Fillout defect_list_length. */ + scsi_ulto2b(sizeof(ufid->fcd), ufid->fdh.defect_list_length); +} + +/* + * 12-byte version of FORMAT_UNIT, for use with UFI and SFF8070i devices. + * + * Notes: + * The track number will come from the unit's flexible disk geometry + * according to the current position in the format. + * The interleave should normally be set to 0. + * The LUN doesn't get filled out here. + * The param_buf is a struct format_unit_ufi_data which was previously + * filled out by scsi_format_unit_ufi_data(). + */ +void +scsi_format_unit_12(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_int8_t track, + u_int16_t interleave, u_int8_t *param_buf, u_int32_t param_len, + u_int8_t sense_len, u_int32_t timeout) +{ + struct scsi_format_unit_12 *scsi_cmd; + + scsi_cmd = (struct scsi_format_unit_12 *)&csio->cdb_io.cdb_bytes; + + bzero(scsi_cmd, sizeof(*scsi_cmd)); + scsi_cmd->opcode = FORMAT_UNIT; + scsi_cmd->byte2 = byte2 | FU_FMT_DATA | 0x07; /* LUN etc */ + scsi_cmd->track = track; + scsi_ulto2b(interleave, scsi_cmd->interleave); + scsi_ulto2b(param_len, scsi_cmd->param_len); + + cam_fill_csio(csio, + retries, + cbfcnp, + CAM_DIR_OUT, + tag_action, + param_buf, + param_len, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +/* + * Prepare a CDB which will return the result of the READ_FORMAT_CAPACITIES + * command into the user-provided buffer fmt_buf, with length fmt_len, + * once executed. + * + * XXX: Move this to kernel headers when ready. + */ +void +scsi_read_format_capacities(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_int8_t *fmt_buf, + u_int32_t fmt_len, u_int8_t sense_len, u_int32_t timeout) +{ + struct scsi_read_format_capacities *scsi_cmd; + + cam_fill_csio(csio, + retries, + cbfcnp, + CAM_DIR_IN, + tag_action, + fmt_buf, + fmt_len, + sense_len, + sizeof(*scsi_cmd), + timeout); + + scsi_cmd = (struct scsi_read_format_capacities *) + &csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + scsi_cmd->opcode = READ_FORMAT_CAPACITIES; + scsi_cmd->byte2 = byte2; /* LUN */ + scsi_ulto2b(fmt_len, scsi_cmd->alloc_length); +} + +/* + * Execute and return the result of the READ_FORMAT_CAPACITIES command. + */ +int +scsi_get_format_capacities(struct cam_device *device, int retry_count, + int timeout, u_int8_t *fmt_buf, u_int32_t fmt_len) +{ + union ccb *ccb; + int error = 0; + + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("couldn't allocate CCB"); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + bzero(fmt_buf, fmt_len); + + scsi_read_format_capacities(&ccb->csio, + retry_count, + NULL, + MSG_SIMPLE_Q_TAG, + 0, /* XXX: byte2 should contain the LUN */ + fmt_buf, + fmt_len, + SSD_FULL_SIZE, + timeout ? timeout : 5000); + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; +#if 0 + if (arglist & CAM_ARG_ERR_RECOVER) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; +#endif + if (cam_send_ccb(device, ccb) < 0) { + perror("error sending SCSI get_format_capacities"); + if (camverbose) + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + cam_freeccb(ccb); + return (1); + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + error = 1; + if (camverbose) + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + + cam_freeccb(ccb); + return (0); +} + +/* + * Perform a SCSI INQUIRY command and return the results in the user + * provided buffer inq_buf. + */ +int +scsi_get_inquiry(struct cam_device *device, int retry_count, + int timeout, struct scsi_inquiry_data *inq_buf) +{ + union ccb *ccb; + int error = 0; + + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("couldn't allocate CCB"); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + bzero(inq_buf, sizeof(*inq_buf)); + + scsi_inquiry(&ccb->csio, + /* retries */ retry_count, + /* cbfcnp */ NULL, + /* tag_action */ MSG_SIMPLE_Q_TAG, + /* inq_buf */ (u_int8_t *)inq_buf, + /* inq_len */ SHORT_INQUIRY_LENGTH, + /* evpd */ 0, + /* page_code */ 0, + /* sense_len */ SSD_FULL_SIZE, + /* timeout */ timeout ? timeout : 5000); + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; +#if 0 + if (arglist & CAM_ARG_ERR_RECOVER) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; +#endif + if (cam_send_ccb(device, ccb) < 0) { + perror("error sending SCSI inquiry"); + if (camverbose) + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + cam_freeccb(ccb); + return (1); + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + error = 1; + if (camverbose) + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + + cam_freeccb(ccb); + return (error); +} + +/* + * Return the results of MODE_SENSE for the SMS_FLEXIBLE_GEOMETRY_PAGE + * into the buffer page_data, but don't attempt to parse it. + */ +int +scsi_get_flexible_disk_page(struct cam_device *cam_dev, + struct flexible_disk_page *page_data, + int min_mode_cmd_size, int timeout) +{ + struct scsi_mode_header_6 *mh6; + struct scsi_mode_header_10 *mh10; + union disk_pages *mode_pars; /* embeds the scsi_mode_page_header */ + u_int8_t *data; + size_t data_len; + int retval; + + data_len = sizeof(union disk_pages); + data_len += (min_mode_cmd_size < 10) ? + sizeof(struct scsi_mode_header_6) : + sizeof(struct scsi_mode_header_10); + + data = malloc(data_len); + if (data == NULL) + errx(EX_OSERR, "error allocating mode sense buffer"); + + retval = scsi_get_mode_sense(cam_dev, SMS_FLEXIBLE_GEOMETRY_PAGE, + SMS_PAGE_CTRL_CURRENT, 0, 0, timeout, + (u_int8_t *)data, data_len, min_mode_cmd_size); + if (retval != 0) { + free(data); + errx(EX_OSERR, "error sending mode sense command"); + } + + mh6 = (struct scsi_mode_header_6 *)data; + mh10 = (struct scsi_mode_header_10 *)data; + mode_pars = (union disk_pages *)((min_mode_cmd_size < 10) ? + MODE_PAGE_HEADER_6(mh6) : MODE_PAGE_HEADER_10(mh10)); + bcopy(&mode_pars->flexible_disk, page_data, + sizeof(struct flexible_disk_page)); + free(data); + + return (retval); +} + +/* + * Return the result of the MODE SENSE command into the user-provided + * buffer 'data'. + */ +int +scsi_get_mode_sense(struct cam_device *device, + int mode_page, int page_control, + int dbd, int retry_count, int timeout, + u_int8_t *data, int datalen, int min_mode_cmd_size) +{ + union ccb *ccb; + int retval; + + ccb = cam_getccb(device); + + if (ccb == NULL) + errx(1, "mode_sense: couldn't allocate CCB"); + + bzero(&(&ccb->ccb_h)[1], + sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + bzero(data, datalen); + + scsi_mode_sense_len(&ccb->csio, + /* retries */ retry_count, + /* cbfcnp */ NULL, + /* tag_action */ MSG_SIMPLE_Q_TAG, + /* dbd */ dbd, + /* page_code */ page_control << 6, + /* page */ mode_page, + /* param_buf */ data, + /* param_len */ datalen, + /* minimum_cmd_size */ min_mode_cmd_size, + /* sense_len */ SSD_FULL_SIZE, + /* timeout */ timeout ? timeout : 5000); + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + if (camverbose) + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + retval = 1; + } + + cam_freeccb(ccb); + return (retval); +} + +void +sfds_dump_desc(char *hdr, int dump_code_type, + struct format_capacity_descriptor *pfd) +{ + u_int32_t block_length, nblocks; + char *type = ""; + + nblocks = scsi_4btoul(pfd->nblocks); + block_length = scsi_3btol(pfd->block_length); + + switch (pfd->byte4 & FCD_CODE_MASK) { + case FCD_UNFORMATTED: + type = "unformatted"; + break; + case FCD_FORMATTED: + type = "formatted"; + break; + case FCD_NOMEDIA: + type = "no disk present"; + break; + } + + if (dump_code_type) { + hdr = ((pfd->byte4 & FCD_CODE_MASK) == FCD_NOMEDIA) ? + "maximum" : "current"; + } + + fprintf(stderr, "%s: %lu blocks, %lu bytes-per-block %s\n", hdr, + (unsigned long)nblocks, (unsigned long)block_length, type); +} + +void +sfds_dump_supported_formats(struct supported_format_descriptors *sfds) +{ + char hdr[16]; + struct format_capacity_descriptor *pfd; + int i, cnt, listlen; + + listlen = sfds->fh.capacity_list_length; + cnt = listlen / sizeof(sfds->fdsupp[0]) - 1; + if ((listlen % sizeof(sfds->fdsupp[0])) > 0) { + fprintf(stderr, + "WARNING: format capacity list may have been truncated.\n"); + } + + sfds_dump_desc("currmax", 1, &sfds->fdcurrmax); + + pfd = &sfds->fdsupp[0]; + for (i = 0; i < cnt; i++, pfd++) { + snprintf(hdr, sizeof(hdr), "%d", i); + sfds_dump_desc(hdr, 0, pfd); + } +} + +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)); +} + +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_12 *scsi_cmd; + + scsi_cmd = (struct scsi_verify_12 *)&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->length); + + 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. + */ +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 +} Index: usr.sbin/ufdformat/ufdformat.1 =================================================================== --- usr.sbin/ufdformat/ufdformat.1 +++ usr.sbin/ufdformat/ufdformat.1 @@ -0,0 +1,142 @@ +.\" +.\" 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. +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd October 12, 2004 +.Os +.Dt UFDFORMAT 1 +.Sh NAME +.Nm ufdformat +.Nd format USB floppy disks +.Sh SYNOPSIS +.Nm +.Op Fl f Ar fmt +.Op Fl nqvy +.Ar device +.Sh DESCRIPTION +The +.Nm +utility formats a floppy disk at +.Ar device , +where +.Ar device +may either be given as a full path +name of a device node for a USB floppy disk drive +(e.g.\& +.Pa /dev/da0 ) , +or using an abbreviated name that will be looked up +under +.Pa /dev +(e.g.\& +.Dq Li da0 ) . +.Pp +The options are as follows: +.Bl -tag -width ".Fl s Ar fmtstr" +.It Fl f Ar fmt +Specify the density settings for a +.Ar fmt +kilobyte format, as described in +.Xr fdcontrol 8 ; +1.44MB is assumed as the default. +.It Fl n +Do not verify floppy after formatting. +.It Fl q +Suppress any normal output from the command, and do not ask the +user for a confirmation whether to format the floppy disk at +.Ar device . +.It Fl v +Do not format, verify only. +.It Fl y +Do not ask for confirmation whether to format the floppy disk but +still report formatting status. +.El +.Pp +If the +.Fl q +flag has not been specified, the user is asked for a confirmation +of the intended formatting process. +In order to continue, an answer +of +.Ql y +must be given. +.Pp +Note that +.Nm +only performs low-level formatting. +In order to create +a file system on the medium, see the commands +.Xr newfs 8 +for a +.Tn UFS +file system, or +.Xr newfs_msdos 8 +for an +.Tn MS-DOS +(FAT) +file system. +.Sh DIAGNOSTICS +Unless +.Fl q +has been specified, a single letter is printed to standard output +to inform the user about the progress of work. +First, an +.Ql F +is printed when the track is being formatted, then a +.Ql V +while it is being verified, and if an error has been detected, it +will finally change to +.Ql E . +Detailed status information (cylinder, head and sector number, and the +exact cause of the error) will be printed for up to 10 errors after the +entire formatting process has completed. +.Pp +An exit status of 0 is returned upon successful operation. +Exit status +1 is returned on any errors during floppy formatting, and an exit status +of 2 reflects invalid arguments given to the program (along with an +appropriate information written to diagnostic output). +.Sh SEE ALSO +.Xr fdformat 1 , +.Xr fdcontrol 8 , +.Xr umass 4 , +.Xr newfs 8 , +.Xr newfs_msdos 8 +.Rs +.%T UFI Command Specification +.%Q "USB Implementers Forum, Inc." +.%N "Revision 1.0" +.%D "December 1998" +.Re +.Sh HISTORY +The +.Nm +utility +first appeared in +.Fx 6.0 . +.Sh AUTHORS +.An -nosplit +The program has been contributed by +.An Bruce M. Simpson . Index: usr.sbin/ufdformat/ufdformat.c =================================================================== --- usr.sbin/ufdformat/ufdformat.c +++ usr.sbin/ufdformat/ufdformat.c @@ -0,0 +1,315 @@ +/* + * 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 + +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); +}