void scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int tag_action, int readop, u_int byte2, u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len, u_int32_t timeout) { struct scsi_send_receive *scsi_cmd; scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = readop ? RECEIVE : SEND; scsi_cmd->byte2 = byte2; scsi_ulto3b(xfer_len, scsi_cmd->xfer_len); scsi_cmd->control = 0; cam_fill_csio(csio, retries, cbfcnp, /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT, tag_action, data_ptr, xfer_len, sense_len, sizeof(*scsi_cmd), timeout); }
static int wds_runsense(struct wds *wp, struct wds_req *r) { u_int8_t c; struct ccb_hdr *ccb_h; ccb_h = &r->ccb->ccb_h; r->flags |= WR_SENSE; scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr); bzero(&r->cmd, sizeof r->cmd); r->cmd.cmd = WDSX_SCSICMD; r->cmd.targ = (ccb_h->target_id << 5) | ccb_h->target_lun; scsi_ulto3b(0, r->cmd.next); r->cmd.scb[0] = REQUEST_SENSE; r->cmd.scb[1] = ccb_h->target_lun << 5; r->cmd.scb[4] = sizeof(struct scsi_sense_data); r->cmd.scb[5] = 0; scsi_ulto3b(WDSTOPHYS(wp, r->buf), r->cmd.data); scsi_ulto3b(sizeof(struct scsi_sense_data), r->cmd.len); r->cmd.write = 0x80; outb(wp->addr + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN); wp->dx->ombs[r->ombn].stat = 1; c = WDSC_MSTART(r->ombn); if (wds_cmd(wp->addr, &c, sizeof c) != 0) { device_printf(wp->dev, "unable to start outgoing sense mbox\n"); wp->dx->ombs[r->ombn].stat = 0; wdsr_ccb_done(wp, r, r->ccb, CAM_AUTOSENSE_FAIL); return CAM_AUTOSENSE_FAIL; } else { DBG(DBX "wds%d: enqueued status cmd 0x%x, r=%p\n", wp->unit, r->cmd.scb[0] & 0xFF, r); /* don't free CCB yet */ smallog3('*', ccb_h->target_id + '0', ccb_h->target_lun + '0'); return CAM_REQ_CMP; } }
static int wds_getvers(struct wds *wp) { struct wds_req *r; int base; u_int8_t c; int i; base = wp->addr; r = wdsr_alloc(wp); if (!r) { device_printf(wp->dev, "no request slot available!\n"); return (-1); } r->flags &= ~WR_DONE; r->ccb = NULL; scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr); bzero(&r->cmd, sizeof r->cmd); r->cmd.cmd = WDSX_GETFIRMREV; outb(base + WDS_HCR, WDSH_DRQEN); c = WDSC_MSTART(r->ombn); if (wds_cmd(base, (u_int8_t *) & c, sizeof c)) { device_printf(wp->dev, "version request failed\n"); wp->wdsr_free |= (1 << r->id); wp->dx->ombs[r->ombn].stat = 0; return (-1); } while (1) { i = 0; while ((inb(base + WDS_STAT) & WDS_IRQ) == 0) { DELAY(9000); if (++i == 100) { device_printf(wp->dev, "getvers timeout\n"); return (-1); } } wds_intr(wp); if (r->flags & WR_DONE) { device_printf(wp->dev, "firmware version %d.%02d\n", r->cmd.targ, r->cmd.scb[0]); wp->wdsr_free |= (1 << r->id); return (0); } } }
static int wds_init(struct wds *wp) { struct wds_setup init; int base; int i; struct wds_cmd wc; base = wp->addr; outb(base + WDS_HCR, WDSH_DRQEN); isa_dmacascade(wp->drq); if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) { for (i = 0; i < 10; i++) { if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY) break; DELAY(40000); } if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) /* probe timeout */ return (1); } bzero(&init, sizeof init); init.cmd = WDSC_INIT; init.scsi_id = WDS_HBA_ID; init.buson_t = 24; init.busoff_t = 48; scsi_ulto3b(WDSTOPHYS(wp, &wp->dx->ombs), init.mbaddr); init.xx = 0; init.nomb = WDS_NOMB; init.nimb = WDS_NIMB; wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY); if (wds_cmd(base, (u_int8_t *) & init, sizeof init) != 0) { device_printf(wp->dev, "wds_cmd init failed\n"); return (1); } wds_wait(base + WDS_STAT, WDS_INIT, WDS_INIT); wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY); bzero(&wc, sizeof wc); wc.cmd = WDSC_DISUNSOL; if (wds_cmd(base, (char *) &wc, sizeof wc) != 0) { device_printf(wp->dev, "wds_cmd init2 failed\n"); return (1); } return (0); }
void ctl_scsi_read_write_buffer(union ctl_io *io, uint8_t *data_ptr, uint32_t data_len, int read_buffer, uint8_t mode, uint8_t buffer_id, uint32_t buffer_offset, ctl_tag_type tag_type, uint8_t control) { struct ctl_scsiio *ctsio; struct scsi_write_buffer *cdb; ctl_scsi_zero_io(io); io->io_hdr.io_type = CTL_IO_SCSI; ctsio = &io->scsiio; cdb = (struct scsi_write_buffer *)ctsio->cdb; if (read_buffer != 0) cdb->opcode = READ_BUFFER; else cdb->opcode = WRITE_BUFFER; cdb->byte2 = mode & RWB_MODE; cdb->buffer_id = buffer_id; scsi_ulto3b(buffer_offset, cdb->offset); scsi_ulto3b(data_len, cdb->length); cdb->control = control; io->io_hdr.io_type = CTL_IO_SCSI; if (read_buffer != 0) io->io_hdr.flags = CTL_FLAG_DATA_IN; else io->io_hdr.flags = CTL_FLAG_DATA_OUT; ctsio->tag_type = tag_type; ctsio->cdb_len = sizeof(*cdb); ctsio->ext_data_ptr = data_ptr; ctsio->ext_data_len = data_len; ctsio->ext_sg_entries = 0; ctsio->ext_data_filled = 0; ctsio->sense_len = SSD_FULL_SIZE; }
static void wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio) { int unit = cam_sim_unit(sim); struct wds *wp; struct ccb_hdr *ccb_h; struct wds_req *r; int base; u_int8_t c; int error; int n; wp = (struct wds *)cam_sim_softc(sim); ccb_h = &csio->ccb_h; DBG(DBX "wds%d: cmd TARG=%d LUN=%d\n", unit, ccb_h->target_id, ccb_h->target_lun); if (ccb_h->target_id > 7 || ccb_h->target_id == WDS_HBA_ID) { ccb_h->status = CAM_TID_INVALID; xpt_done((union ccb *) csio); return; } if (ccb_h->target_lun > 7) { ccb_h->status = CAM_LUN_INVALID; xpt_done((union ccb *) csio); return; } if (csio->dxfer_len > BUFSIZ) { ccb_h->status = CAM_REQ_TOO_BIG; xpt_done((union ccb *) csio); return; } if ((ccb_h->flags & CAM_DATA_MASK) != CAM_DATA_VADDR) { /* don't support these */ ccb_h->status = CAM_REQ_INVALID; xpt_done((union ccb *) csio); return; } base = wp->addr; /* * this check is mostly for debugging purposes, * "can't happen" normally. */ if(wp->want_wdsr) { DBG(DBX "wds%d: someone already waits for buffer\n", unit); smallog('b'); n = xpt_freeze_simq(sim, /* count */ 1); smallog('0'+n); ccb_h->status = CAM_REQUEUE_REQ; xpt_done((union ccb *) csio); return; } r = wdsr_alloc(wp); if (r == NULL) { device_printf(wp->dev, "no request slot available!\n"); wp->want_wdsr = 1; n = xpt_freeze_simq(sim, /* count */ 1); smallog2('f', '0'+n); ccb_h->status = CAM_REQUEUE_REQ; xpt_done((union ccb *) csio); return; } ccb_h->ccb_wdsr = (void *) r; r->ccb = (union ccb *) csio; switch (error = frag_alloc(wp, csio->dxfer_len, &r->buf, &r->mask)) { case CAM_REQ_CMP: break; case CAM_REQUEUE_REQ: DBG(DBX "wds%d: no data buffer available\n", unit); wp->want_wdsr = 1; n = xpt_freeze_simq(sim, /* count */ 1); smallog2('f', '0'+n); wdsr_ccb_done(wp, r, r->ccb, CAM_REQUEUE_REQ); return; default: DBG(DBX "wds%d: request is too big\n", unit); wdsr_ccb_done(wp, r, r->ccb, error); break; } ccb_h->status |= CAM_SIM_QUEUED; r->flags &= ~WR_DONE; scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr); bzero(&r->cmd, sizeof r->cmd); r->cmd.cmd = WDSX_SCSICMD; r->cmd.targ = (ccb_h->target_id << 5) | ccb_h->target_lun; if (ccb_h->flags & CAM_CDB_POINTER) bcopy(csio->cdb_io.cdb_ptr, &r->cmd.scb, csio->cdb_len < 12 ? csio->cdb_len : 12); else bcopy(csio->cdb_io.cdb_bytes, &r->cmd.scb, csio->cdb_len < 12 ? csio->cdb_len : 12); scsi_ulto3b(csio->dxfer_len, r->cmd.len); if (csio->dxfer_len > 0 && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_OUT) { /* we already rejected physical or scattered addresses */ bcopy(csio->data_ptr, r->buf, csio->dxfer_len); } scsi_ulto3b(csio->dxfer_len ? WDSTOPHYS(wp, r->buf) : 0, r->cmd.data); if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) r->cmd.write = 0x80; else r->cmd.write = 0x00; scsi_ulto3b(0, r->cmd.next); outb(base + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN); c = WDSC_MSTART(r->ombn); if (wds_cmd(base, &c, sizeof c) != 0) { device_printf(wp->dev, "unable to start outgoing mbox\n"); wp->dx->ombs[r->ombn].stat = 0; wdsr_ccb_done(wp, r, r->ccb, CAM_RESRC_UNAVAIL); return; } DBG(DBX "wds%d: enqueued cmd 0x%x, r=%p\n", unit, r->cmd.scb[0] & 0xFF, r); smallog3('+', ccb_h->target_id + '0', ccb_h->target_lun + '0'); }
void ctl_scsi_read_write(union ctl_io *io, uint8_t *data_ptr, uint32_t data_len, int read_op, uint8_t byte2, int minimum_cdb_size, uint64_t lba, uint32_t num_blocks, ctl_tag_type tag_type, uint8_t control) { struct ctl_scsiio *ctsio; ctl_scsi_zero_io(io); io->io_hdr.io_type = CTL_IO_SCSI; ctsio = &io->scsiio; /* * Pick out the smallest CDB that will hold the user's request. * minimum_cdb_size allows cranking the CDB size up, even for * requests that would not normally need a large CDB. This can be * useful for testing (e.g. to make sure READ_16 support works without * having an array larger than 2TB) and for compatibility -- e.g. * if your device doesn't support READ_6. (ATAPI drives don't.) */ if ((minimum_cdb_size < 10) && ((lba & 0x1fffff) == lba) && ((num_blocks & 0xff) == num_blocks) && (byte2 == 0)) { struct scsi_rw_6 *cdb; /* * Note that according to SBC-2, the target should return 256 * blocks if the transfer length in a READ(6) or WRITE(6) CDB * is set to 0. Since it's possible that some targets * won't do the right thing, we only send a READ(6) or * WRITE(6) for transfer sizes up to and including 255 blocks. */ cdb = (struct scsi_rw_6 *)ctsio->cdb; cdb->opcode = (read_op) ? READ_6 : WRITE_6; scsi_ulto3b(lba, cdb->addr); cdb->length = num_blocks & 0xff; cdb->control = control; ctsio->cdb_len = sizeof(*cdb); } else if ((minimum_cdb_size < 12) && ((num_blocks & 0xffff) == num_blocks) && ((lba & 0xffffffff) == lba)) { struct scsi_rw_10 *cdb; cdb = (struct scsi_rw_10 *)ctsio->cdb; cdb->opcode = (read_op) ? READ_10 : WRITE_10; cdb->byte2 = byte2; scsi_ulto4b(lba, cdb->addr); cdb->reserved = 0; scsi_ulto2b(num_blocks, cdb->length); cdb->control = control; ctsio->cdb_len = sizeof(*cdb); } else if ((minimum_cdb_size < 16) && ((num_blocks & 0xffffffff) == num_blocks) && ((lba & 0xffffffff) == lba)) { struct scsi_rw_12 *cdb; cdb = (struct scsi_rw_12 *)ctsio->cdb; cdb->opcode = (read_op) ? READ_12 : WRITE_12; cdb->byte2 = byte2; scsi_ulto4b(lba, cdb->addr); scsi_ulto4b(num_blocks, cdb->length); cdb->reserved = 0; cdb->control = control; ctsio->cdb_len = sizeof(*cdb); } else { struct scsi_rw_16 *cdb; cdb = (struct scsi_rw_16 *)ctsio->cdb; cdb->opcode = (read_op) ? READ_16 : WRITE_16; cdb->byte2 = byte2; scsi_u64to8b(lba, cdb->addr); scsi_ulto4b(num_blocks, cdb->length); cdb->reserved = 0; cdb->control = control; ctsio->cdb_len = sizeof(*cdb); } io->io_hdr.io_type = CTL_IO_SCSI; if (read_op != 0) io->io_hdr.flags = CTL_FLAG_DATA_IN; else io->io_hdr.flags = CTL_FLAG_DATA_OUT; ctsio->tag_type = tag_type; ctsio->ext_data_ptr = data_ptr; ctsio->ext_data_len = data_len; ctsio->ext_sg_entries = 0; ctsio->ext_data_filled = 0; ctsio->sense_len = SSD_FULL_SIZE; }
/* * Download firmware stored in buf to cam_dev. If simulation mode * is enabled, only show what packet sizes would be sent to the * device but do not sent any actual packets */ static int fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp, char *buf, int img_size, int sim_mode, int verbose, int retry_count, int timeout) { struct scsi_write_buffer cdb; union ccb *ccb; int pkt_count = 0; u_int32_t pkt_size = 0; char *pkt_ptr = buf; u_int32_t offset; int last_pkt = 0; if ((ccb = cam_getccb(cam_dev)) == NULL) { warnx("Could not allocate CCB"); return (1); } scsi_test_unit_ready(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, 5000); /* Disable freezing the device queue. */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (cam_send_ccb(cam_dev, ccb) < 0) { warnx("Error sending test unit ready"); if (verbose) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); cam_freeccb(ccb); return(1); } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { warnx("Device is not ready"); if (verbose) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); cam_freeccb(ccb); return (1); } pkt_size = vp->max_pkt_size; if (verbose || sim_mode) { fprintf(stdout, "--------------------------------------------------\n"); fprintf(stdout, "PktNo. PktSize BytesRemaining LastPkt\n"); fprintf(stdout, "--------------------------------------------------\n"); } /* Download single fw packets. */ do { if (img_size <= vp->max_pkt_size) { last_pkt = 1; pkt_size = img_size; } if (verbose || sim_mode) fprintf(stdout, "%3u %5u (0x%05X) %7u (0x%06X) " "%d\n", pkt_count, pkt_size, pkt_size, img_size - pkt_size, img_size - pkt_size, last_pkt); bzero(&cdb, sizeof(cdb)); cdb.opcode = WRITE_BUFFER; cdb.control = 0; /* Parameter list length. */ scsi_ulto3b(pkt_size, &cdb.length[0]); offset = vp->inc_cdb_offset ? (pkt_ptr - buf) : 0; scsi_ulto3b(offset, &cdb.offset[0]); cdb.byte2 = last_pkt ? vp->cdb_byte2_last : vp->cdb_byte2; cdb.buffer_id = vp->inc_cdb_buffer_id ? pkt_count : 0; /* Zero out payload of ccb union after ccb header. */ bzero((u_char *)ccb + sizeof(struct ccb_hdr), sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); /* Copy previously constructed cdb into ccb_scsiio struct. */ bcopy(&cdb, &ccb->csio.cdb_io.cdb_bytes[0], sizeof(struct scsi_write_buffer)); /* Fill rest of ccb_scsiio struct. */ if (!sim_mode) { cam_fill_csio(&ccb->csio, /* ccb_scsiio */ retry_count, /* retries */ NULL, /* cbfcnp */ CAM_DIR_OUT | CAM_DEV_QFRZDIS, /* flags */ CAM_TAG_ACTION_NONE, /* tag_action */ (u_char *)pkt_ptr, /* data_ptr */ pkt_size, /* dxfer_len */ SSD_FULL_SIZE, /* sense_len */ sizeof(struct scsi_write_buffer), /* cdb_len */ timeout ? timeout : CMD_TIMEOUT); /* timeout */ /* Execute the command. */ if (cam_send_ccb(cam_dev, ccb) < 0) { warnx("Error writing image to device"); if (verbose) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); goto bailout; } } /* Prepare next round. */ pkt_count++; pkt_ptr += pkt_size; img_size -= pkt_size; } while(!last_pkt); if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (verbose) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); goto bailout; } cam_freeccb(ccb); return (0); bailout: cam_freeccb(ccb); return (1); }
/* * Download firmware stored in buf to cam_dev. If simulation mode * is enabled, only show what packet sizes would be sent to the * device but do not sent any actual packets */ static int fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp, char *buf, int img_size, int sim_mode, int printerrors, int retry_count, int timeout, const char *imgname, const char *type) { struct scsi_write_buffer cdb; progress_t progress; int size; union ccb *ccb; int pkt_count = 0; int max_pkt_size; u_int32_t pkt_size = 0; char *pkt_ptr = buf; u_int32_t offset; int last_pkt = 0; int16_t *ptr; if ((ccb = cam_getccb(cam_dev)) == NULL) { warnx("Could not allocate CCB"); return (1); } if (strcmp(type, "scsi") == 0) { scsi_test_unit_ready(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, 5000); } else if (strcmp(type, "ata") == 0) { /* cam_getccb cleans up the header, caller has to zero the payload */ bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr)); ptr = (uint16_t *)malloc(sizeof(struct ata_params)); if (ptr == NULL) { cam_freeccb(ccb); warnx("can't malloc memory for identify\n"); return(1); } bzero(ptr, sizeof(struct ata_params)); cam_fill_ataio(&ccb->ataio, 1, NULL, /*flags*/CAM_DIR_IN, MSG_SIMPLE_Q_TAG, /*data_ptr*/(uint8_t *)ptr, /*dxfer_len*/sizeof(struct ata_params), timeout ? timeout : 30 * 1000); ata_28bit_cmd(&ccb->ataio, ATA_ATA_IDENTIFY, 0, 0, 0); } else { warnx("weird disk type '%s'", type); return 1; } /* Disable freezing the device queue. */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (cam_send_ccb(cam_dev, ccb) < 0) { warnx("Error sending identify/test unit ready"); if (printerrors) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); cam_freeccb(ccb); return(1); } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { warnx("Device is not ready"); if (printerrors) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); cam_freeccb(ccb); return (1); } max_pkt_size = vp->max_pkt_size; if (vp->max_pkt_size == 0 && strcmp(type, "ata") == 0) { max_pkt_size = UNKNOWN_MAX_PKT_SIZE; } pkt_size = vp->max_pkt_size; progress_init(&progress, imgname, size = img_size); /* Download single fw packets. */ do { if (img_size <= max_pkt_size) { last_pkt = 1; pkt_size = img_size; } progress_update(&progress, size - img_size); progress_draw(&progress); bzero(&cdb, sizeof(cdb)); if (strcmp(type, "scsi") == 0) { cdb.opcode = WRITE_BUFFER; cdb.control = 0; /* Parameter list length. */ scsi_ulto3b(pkt_size, &cdb.length[0]); offset = vp->inc_cdb_offset ? (pkt_ptr - buf) : 0; scsi_ulto3b(offset, &cdb.offset[0]); cdb.byte2 = last_pkt ? vp->cdb_byte2_last : vp->cdb_byte2; cdb.buffer_id = vp->inc_cdb_buffer_id ? pkt_count : 0; /* Zero out payload of ccb union after ccb header. */ bzero((u_char *)ccb + sizeof(struct ccb_hdr), sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); /* Copy previously constructed cdb into ccb_scsiio struct. */ bcopy(&cdb, &ccb->csio.cdb_io.cdb_bytes[0], sizeof(struct scsi_write_buffer)); /* Fill rest of ccb_scsiio struct. */ if (!sim_mode) { cam_fill_csio(&ccb->csio, /* ccb_scsiio */ retry_count, /* retries */ NULL, /* cbfcnp */ CAM_DIR_OUT | CAM_DEV_QFRZDIS, /* flags */ CAM_TAG_ACTION_NONE, /* tag_action */ (u_char *)pkt_ptr, /* data_ptr */ pkt_size, /* dxfer_len */ SSD_FULL_SIZE, /* sense_len */ sizeof(struct scsi_write_buffer), /* cdb_len */ timeout ? timeout : CMD_TIMEOUT); /* timeout */ } } else if (strcmp(type, "ata") == 0) { bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr)); if (!sim_mode) { uint32_t off; cam_fill_ataio(&ccb->ataio, (last_pkt) ? 256 : retry_count, NULL, /*flags*/CAM_DIR_OUT | CAM_DEV_QFRZDIS, CAM_TAG_ACTION_NONE, /*data_ptr*/(uint8_t *)pkt_ptr, /*dxfer_len*/pkt_size, timeout ? timeout : 30 * 1000); off = (uint32_t)(pkt_ptr - buf); ata_28bit_cmd(&ccb->ataio, ATA_DOWNLOAD_MICROCODE, USE_OFFSETS_FEATURE, ATA_MAKE_LBA(off, pkt_size), ATA_MAKE_SECTORS(pkt_size)); } } if (!sim_mode) { /* Execute the command. */ if (cam_send_ccb(cam_dev, ccb) < 0 || (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { warnx("Error writing image to device"); if (printerrors) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); goto bailout; } } /* Prepare next round. */ pkt_count++; pkt_ptr += pkt_size; img_size -= pkt_size; } while(!last_pkt); progress_complete(&progress, size - img_size); cam_freeccb(ccb); return (0); bailout: progress_complete(&progress, size - img_size); cam_freeccb(ccb); return (1); }