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);
}
Example #2
0
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;
	}
}
Example #3
0
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);
		}
	}
}
Example #4
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);
}
Example #5
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;
}
Example #6
0
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');
}
Example #7
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;
}
Example #8
0
/* 
 * 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);
}
Example #9
0
/* 
 * 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);
}