예제 #1
0
파일: ch.c 프로젝트: darksoul42/bitrig
int
ch_getelemstatus(struct ch_softc *sc, int first, int count, caddr_t data,
    size_t datalen, int voltag)
{
	struct scsi_read_element_status *cmd;
	struct scsi_xfer *xs;
	int error;

	/*
	 * Build SCSI command.
	 */
	xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
	if (xs == NULL)
		return (ENOMEM);
	xs->cmdlen = sizeof(*cmd);
	xs->data = data;
	xs->datalen = datalen;
	xs->retries = CHRETRIES;
	xs->timeout = 100000;

	cmd = (struct scsi_read_element_status *)xs->cmd;
	cmd->opcode = READ_ELEMENT_STATUS;
	_lto2b(first, cmd->sea);
	_lto2b(count, cmd->count);
	_lto3b(datalen, cmd->len);
	if (voltag)
		cmd->byte2 |= READ_ELEMENT_STATUS_VOLTAG;

	error = scsi_xs_sync(xs);
	scsi_xs_put(xs);

	return (error);
}
예제 #2
0
static int
ch_getelemstatus(struct ch_softc *sc, int first, int count, void *data,
    size_t datalen, int scsiflags, int flags)
{
	struct scsi_read_element_status cmd;

	/*
	 * Build SCSI command.
	 */
	memset(&cmd, 0, sizeof(cmd));
	cmd.opcode = READ_ELEMENT_STATUS;
	cmd.byte2 = ELEMENT_TYPE_ALL;
	if (flags & CESR_VOLTAGS)
		cmd.byte2 |= READ_ELEMENT_STATUS_VOLTAG;
	_lto2b(first, cmd.sea);
	_lto2b(count, cmd.count);
	_lto3b(datalen, cmd.len);

	/*
	 * Send command to changer.
	 */
	return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd),
	    (void *)data, datalen,
	    CHRETRIES, CHTIMEOUT, NULL, scsiflags | XS_CTL_DATA_IN));
}
예제 #3
0
/*
 * read the requested number of bytes/lines from the scanner
 */
static int
mustek_read(struct ss_softc *ss, struct buf *bp)
{
	struct mustek_read_cmd cmd;
	struct scsipi_xfer *xs;
	struct scsipi_periph *periph = ss->sc_periph;
	u_long lines_to_read;
	int error;

	SC_DEBUG(periph, SCSIPI_DB1, ("mustek_read: start\n"));

	memset(&cmd, 0, sizeof(cmd));
	cmd.opcode = MUSTEK_READ;

	/* instead of the bytes, the mustek wants the number of lines */
	lines_to_read = bp->b_bcount /
	    ((ss->sio.scan_pixels_per_line * ss->sio.scan_bits_per_pixel) / 8);
	SC_DEBUG(periph, SCSIPI_DB1, ("mustek_read: read %ld lines\n",
	    lines_to_read));
	_lto3b(lines_to_read, cmd.length);

	/*
	 * go ask the adapter to do all this for us
	 */
	xs = scsipi_make_xs(periph,
	    (struct scsipi_generic *) &cmd, sizeof(cmd),
	    (u_char *) bp->b_data, bp->b_bcount,
	    MUSTEK_RETRIES, 10000, bp,
	    XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_IN);
	if (xs == NULL) {
		/*
		 * out of memory. Keep this buffer in the queue, and
		 * retry later.
		 */
		callout_reset(&ss->sc_callout, hz / 2, ssrestart,
		    periph);
		return(0);
	}
#ifdef DIAGNOSTIC
	if (BUFQ_GET(ss->buf_queue) != bp)
		panic("ssstart(): dequeued wrong buf");
#else
	BUFQ_GET(ss->buf_queue);
#endif
	error = scsipi_execute_xs(xs);
	/* with a scsipi_xfer preallocated, scsipi_command can't fail */
	KASSERT(error == 0);
	ss->sio.scan_lines -= lines_to_read;
#if 0
	if (ss->sio.scan_lines < 0)
		ss->sio.scan_lines = 0;
#endif
	ss->sio.scan_window_size -= bp->b_bcount;
#if 0
	if (ss->sio.scan_window_size < 0)
		ss->sio.scan_window_size = 0;
#endif
	return (0);
}
예제 #4
0
void
sd_cmd_rw6(struct scsi_xfer *xs, int read, u_int64_t secno, u_int nsecs)
{
	struct scsi_rw *cmd = (struct scsi_rw *)xs->cmd;

	cmd->opcode = read ? READ_COMMAND : WRITE_COMMAND;
	_lto3b(secno, cmd->addr);
	cmd->length = nsecs;

	xs->cmdlen = sizeof(*cmd);
}
예제 #5
0
static int
scanjet_read(struct ss_softc *ss, struct buf *bp)
{
	struct scsi_rw_scanner cmd;
	struct scsipi_xfer *xs;
	struct scsipi_periph *periph = ss->sc_periph;
	int error;

	/*
	 *  Fill out the scsi command
	 */
	memset(&cmd, 0, sizeof(cmd));
	cmd.opcode = READ;

	/*
	 * Handle "fixed-block-mode" tape drives by using the
	 * block count instead of the length.
	 */
	_lto3b(bp->b_bcount, cmd.len);

	/*
	 * go ask the adapter to do all this for us
	 */
	xs = scsipi_make_xs(periph,
	    (struct scsipi_generic *) &cmd, sizeof(cmd),
	    (u_char *) bp->b_data, bp->b_bcount,
	    SCANJET_RETRIES, 100000, bp,
	    XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_IN);
	if (xs == NULL) {
		/*
		 * out of memory. Keep this buffer in the queue, and
		 * retry later.
		 */
		callout_reset(&ss->sc_callout, hz / 2, ssrestart,
		    periph);
		return(0);
	}
#ifdef DIAGNOSTIC
	if (BUFQ_GET(ss->buf_queue) != bp)
		panic("ssstart(): dequeued wrong buf");
#else
	BUFQ_GET(ss->buf_queue);
#endif
	error = scsipi_execute_xs(xs);
	/* with a scsipi_xfer preallocated, scsipi_command can't fail */
	KASSERT(error == 0);
	ss->sio.scan_window_size -= bp->b_bcount;
#if 0
	if (ss->sio.scan_window_size < 0)
		ss->sio.scan_window_size = 0;
#endif
	return (0);
}
예제 #6
0
/*
 * Send a filled out parameter structure to the drive to
 * set it into the desire modes etc.
 */
static int
st_scsibus_mode_select(struct st_softc *st, int flags)
{
	u_int scsi_select_len;
	struct scsi_select {
		struct scsi_mode_parameter_header_6 header;
		struct scsi_general_block_descriptor blk_desc;
		u_char sense_data[MAX_PAGE_0_SIZE];
	} scsi_select;
	struct scsipi_periph *periph = st->sc_periph;

	scsi_select_len = 12 + st->page_0_size;

	/*
	 * This quirk deals with drives that have only one valid mode
	 * and think this gives them license to reject all mode selects,
	 * even if the selected mode is the one that is supported.
	 */
	if (st->quirks & ST_Q_UNIMODAL) {
		SC_DEBUG(periph, SCSIPI_DB3,
		    ("not setting density 0x%x blksize 0x%x\n",
		    st->density, st->blksize));
		return (0);
	}

	/*
	 * Set up for a mode select
	 */
	memset(&scsi_select, 0, scsi_select_len);
	scsi_select.header.blk_desc_len = sizeof(struct scsi_general_block_descriptor);
	scsi_select.header.dev_spec &= ~SMH_DSP_BUFF_MODE;
	scsi_select.blk_desc.density = st->density;
	if (st->flags & ST_DONTBUFFER)
		scsi_select.header.dev_spec |= SMH_DSP_BUFF_MODE_OFF;
	else
		scsi_select.header.dev_spec |= SMH_DSP_BUFF_MODE_ON;
	if (st->flags & ST_FIXEDBLOCKS)
		_lto3b(st->blksize, scsi_select.blk_desc.blklen);
	if (st->page_0_size)
		memcpy(scsi_select.sense_data, st->sense_data, st->page_0_size);

	/*
	 * do the command
	 */
	return scsipi_mode_select(periph, 0, &scsi_select.header,
	    scsi_select_len, flags | XS_CTL_DATA_ONSTACK,
	    ST_RETRIES, ST_CTL_TIME);
}
예제 #7
0
/*---------------------------------------------------------------------------
 *
 *--------------------------------------------------------------------------*/
int 
create_phys_to_lba_page(char *p,int cyl,int head,int sector) 
{
  memset(p,0,TRANS_ADDR_PAGE_LEN+4);
  p[0]=TRANS_ADDR_PAGE;
  p[1]=0;
  p[2]=0;
  p[3]=TRANS_ADDR_PAGE_LEN;
  
  p[4] = 0x05; /* supplied format */
  p[5] = 0x0; /* translate format */
  
  _lto3b(cyl,(u_int8_t *) &p[6]);
  p[9] = (u_int8_t) head;
  _lto4b(sector,(u_int8_t *) &p[10]);
  return(TRANS_ADDR_PAGE_LEN+4);
}
예제 #8
0
/*
 * Do a synchronous read.  Used to read responses to control messages.
 */
static int
scanjet_ctl_read(struct ss_softc *ss, char *tbuf, u_int size)
{
	struct scsi_rw_scanner cmd;
	int flags;

	flags = 0;
	if ((ss->flags & SSF_AUTOCONF) != 0)
		flags |= XS_CTL_DISCOVERY;

	memset(&cmd, 0, sizeof(cmd));
	cmd.opcode = READ;
	_lto3b(size, cmd.len);

	return (scsipi_command(ss->sc_periph,
	    (void *)&cmd, sizeof(cmd), (void *)tbuf, size, 0, 100000, NULL,
	    flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK));
}
예제 #9
0
int
scsi_read(struct scsi_private *priv, daddr32_t blk, size_t size, void *buf,
    size_t *rsize)
{
	union {
		struct scsi_rw rw;
		struct scsi_rw_big rw_big;
		struct scsi_rw_12 rw_12;
	} cmd;
	int nsecs;
	size_t cmdlen;
	int i, rc;

	nsecs = (size + DEV_BSIZE - 1) >> _DEV_BSHIFT;

	for (i = SCSI_RETRIES; i != 0; i--) {
		memset(&cmd, 0, sizeof cmd);

		/* XXX SDEV_ONLYBIG quirk */
		if ((blk & 0x1fffff) == blk && (nsecs & 0xff) == nsecs) {
			cmd.rw.opcode = READ_COMMAND;
			_lto3b(blk, cmd.rw.addr);
			cmd.rw.length = nsecs;
			cmdlen = sizeof cmd.rw;
		} else if ((nsecs & 0xffff) == nsecs) {
			cmd.rw_big.opcode = READ_BIG;
			_lto4b(blk, cmd.rw_big.addr);
			_lto2b(nsecs, cmd.rw_big.length);
			cmdlen = sizeof cmd.rw_big;
		} else {
			cmd.rw_12.opcode = READ_12;
			_lto4b(blk, cmd.rw_12.addr);
			_lto4b(nsecs, cmd.rw_12.length);
			cmdlen = sizeof cmd.rw_12;
		}

		rc = (*priv->scsicmd)(priv->scsicookie,
		    &cmd, sizeof cmd, buf, size, rsize);
		if (rc == 0)
			break;
	}

	return rc;
}
예제 #10
0
void
wdc_atapi_tape_done(struct channel_softc *chp, struct wdc_xfer *xfer,
    int timeout, struct atapi_return_args *ret)
{
	struct scsi_xfer *sc_xfer = xfer->cmd;

	if (sc_xfer->error != XS_NOERROR) {
		xfer->next = wdc_atapi_done;
		return;
	}

	_lto3b(xfer->transfer_len,
	    ((struct scsi_rw_tape *)
		sc_xfer->cmd)->len);

	xfer->c_bcount = sc_xfer->datalen;
	xfer->c_done = NULL;
	xfer->c_skip = 0;

	xfer->next = wdc_atapi_real_start;
	return;
}
예제 #11
0
파일: scsicmd.c 프로젝트: aunali1/exopc
struct scsi_generic * build_scsi_rw_command (int rw, uint blkno, uint bcount, int *cmdlen)
{
   uint nblks = howmany (bcount, SECT_SIZE);
   if (((blkno & 0x1fffff) == blkno) && ((nblks & 0xff) == nblks)) {
	/* transfer can be described in small RW SCSI command, so do it */
      struct scsi_rw *cmd_small = (struct scsi_rw *) malloc (sizeof(struct scsi_rw));
      bzero (cmd_small, sizeof(struct scsi_rw));
      cmd_small->opcode = (rw == B_READ) ? READ_COMMAND : WRITE_COMMAND;
      _lto3b (blkno, cmd_small->addr);
      cmd_small->length = nblks & 0xff;
      *cmdlen = sizeof (struct scsi_rw);
      return ((struct scsi_generic *) cmd_small);
      
   } else {
      struct scsi_rw_big *cmd_big = (struct scsi_rw_big *) malloc (sizeof(struct scsi_rw_big));
      bzero (cmd_big, sizeof(struct scsi_rw_big));
      cmd_big->opcode = (rw == B_READ) ? READ_BIG : WRITE_BIG;
      _lto4b (blkno, cmd_big->addr);
      _lto2b (nblks, cmd_big->length);
      *cmdlen = sizeof (struct scsi_rw_big);
      return ((struct scsi_generic *) cmd_big);
   }
}
예제 #12
0
/*
 * cdstart looks to see if there is a buf waiting for the device
 * and that the device is not already busy. If both are true,
 * It deques the buf and creates a scsi command to perform the
 * transfer in the buf. The transfer request will call scsi_done
 * on completion, which will in turn call this routine again
 * so that the next queued transfer is performed.
 * The bufs are queued by the strategy routine (cdstrategy)
 *
 * This routine is also called after other non-queued requests
 * have been made of the scsi driver, to ensure that the queue
 * continues to be drained.
 *
 * must be called at the correct (highish) spl level
 * cdstart() is called at splbio from cdstrategy, cdrestart and scsi_done
 */
void
cdstart(void *v)
{
	struct cd_softc *cd = v;
	struct scsi_link *sc_link = cd->sc_link;
	struct buf *bp = 0;
	struct buf *dp;
	struct scsi_rw_big cmd_big;
	struct scsi_rw cmd_small;
	struct scsi_generic *cmdp;
	int blkno, nblks, cmdlen, error;
	struct partition *p;

	splassert(IPL_BIO);

	SC_DEBUG(sc_link, SDEV_DB2, ("cdstart\n"));
	/*
	 * Check if the device has room for another command
	 */
	while (sc_link->openings > 0) {
		/*
		 * there is excess capacity, but a special waits
		 * It'll need the adapter as soon as we clear out of the
		 * way and let it run (user level wait).
		 */
		if (sc_link->flags & SDEV_WAITING) {
			sc_link->flags &= ~SDEV_WAITING;
			wakeup((caddr_t)sc_link);
			return;
		}

		/*
		 * See if there is a buf with work for us to do..
		 */
		dp = &cd->buf_queue;
		if ((bp = dp->b_actf) == NULL)	/* yes, an assign */
			return;
		dp->b_actf = bp->b_actf;

		/*
		 * If the device has become invalid, abort all the
		 * reads and writes until all files have been closed and
		 * re-opened
		 */
		if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
			bp->b_error = EIO;
			bp->b_flags |= B_ERROR;
			bp->b_resid = bp->b_bcount;
			biodone(bp);
			continue;
		}

		/*
		 * We have a buf, now we should make a command
		 *
		 * First, translate the block to absolute and put it in terms
		 * of the logical blocksize of the device.
		 */
		blkno =
		    bp->b_blkno / (cd->sc_dk.dk_label->d_secsize / DEV_BSIZE);
		p = &cd->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
		blkno += DL_GETPOFFSET(p);
		nblks = howmany(bp->b_bcount, cd->sc_dk.dk_label->d_secsize);

		/*
		 *  Fill out the scsi command.  If the transfer will
		 *  fit in a "small" cdb, use it.
		 */
		if (!(sc_link->flags & SDEV_ATAPI) &&
		    !(sc_link->quirks & SDEV_ONLYBIG) && 
		    ((blkno & 0x1fffff) == blkno) &&
		    ((nblks & 0xff) == nblks)) {
			/*
			 * We can fit in a small cdb.
			 */
			bzero(&cmd_small, sizeof(cmd_small));
			cmd_small.opcode = (bp->b_flags & B_READ) ?
			    READ_COMMAND : WRITE_COMMAND;
			_lto3b(blkno, cmd_small.addr);
			cmd_small.length = nblks & 0xff;
			cmdlen = sizeof(cmd_small);
			cmdp = (struct scsi_generic *)&cmd_small;
		} else {
			/*
			 * Need a large cdb.
			 */
			bzero(&cmd_big, sizeof(cmd_big));
			cmd_big.opcode = (bp->b_flags & B_READ) ?
			    READ_BIG : WRITE_BIG;
			_lto4b(blkno, cmd_big.addr);
			_lto2b(nblks, cmd_big.length);
			cmdlen = sizeof(cmd_big);
			cmdp = (struct scsi_generic *)&cmd_big;
		}

		/* Instrumentation. */
		disk_busy(&cd->sc_dk);

		/*
		 * Call the routine that chats with the adapter.
		 * Note: we cannot sleep as we may be an interrupt
		 */
		error = scsi_scsi_cmd(sc_link, cmdp, cmdlen,
		    (u_char *) bp->b_data, bp->b_bcount, SCSI_RETRIES, 30000,
		    bp, SCSI_NOSLEEP | ((bp->b_flags & B_READ) ? SCSI_DATA_IN :
		    SCSI_DATA_OUT));
		switch (error) {
		case 0:
			timeout_del(&cd->sc_timeout);
			break;
		case EAGAIN:
			/*
			 * The device can't start another i/o. Try again later.
			 */
			dp->b_actf = bp;
			disk_unbusy(&cd->sc_dk, 0, 0);
			timeout_add(&cd->sc_timeout, 1);
			return;
		default:
			disk_unbusy(&cd->sc_dk, 0, 0);
			printf("%s: not queued, error %d\n",
			    cd->sc_dev.dv_xname, error);
			break;
		}
	}
}
예제 #13
0
/*
 * Read some data.
 */
int
sdstrategy(void *f, int rw, daddr_t dblk, size_t size, void *p, size_t *rsize)
{
	struct sd_softc *sd;
	struct disklabel *lp;
	struct partition *pp;
	struct scsipi_generic *cmdp;
	struct scsipi_rw_16 cmd16;
	struct scsipi_rw_10 cmd_big;
	struct scsi_rw_6 cmd_small;
	daddr_t blkno;
	int cmdlen, nsect, i;
	uint8_t *buf;

	if (size == 0)
		return 0;
    
	if (rw != F_READ)
		return EOPNOTSUPP;

	buf = p;
	sd = f;
	lp = &sd->sc_label;
	pp = &lp->d_partitions[sd->sc_part];

	if (!(sd->sc_flags & FLAGS_MEDIA_LOADED))
		return EIO;

	nsect = howmany(size, lp->d_secsize);
	blkno = dblk + pp->p_offset;

	for (i = 0; i < nsect; i++, blkno++) {
		int error;

		/*
		 * Fill out the scsi command.  Use the smallest CDB possible
		 * (6-byte, 10-byte, or 16-byte).
		 */
		if ((blkno & 0x1fffff) == blkno) {
			/* 6-byte CDB */
			memset(&cmd_small, 0, sizeof(cmd_small));
			cmd_small.opcode = SCSI_READ_6_COMMAND;
			_lto3b(blkno, cmd_small.addr);
			cmd_small.length = 1;
			cmdlen = sizeof(cmd_small);
			cmdp = (struct scsipi_generic *)&cmd_small;
		} else if ((blkno & 0xffffffff) == blkno) {
			/* 10-byte CDB */
			memset(&cmd_big, 0, sizeof(cmd_big));
			cmd_small.opcode = READ_10;
			_lto4b(blkno, cmd_big.addr);
			_lto2b(1, cmd_big.length);
			cmdlen = sizeof(cmd_big);
			cmdp = (struct scsipi_generic *)&cmd_big;
		} else {
			/* 16-byte CDB */
			memset(&cmd16, 0, sizeof(cmd16));
			cmd_small.opcode = READ_16;
			_lto8b(blkno, cmd16.addr);
			_lto4b(1, cmd16.length);
			cmdlen = sizeof(cmd16);
			cmdp = (struct scsipi_generic *)&cmd16;
		}

		error = scsi_command(sd, cmdp, cmdlen, buf, lp->d_secsize);
		if (error)
			return error;

		buf += lp->d_secsize;
	}

	*rsize = size;
	return 0;
}
예제 #14
0
void
wdc_atapi_send_cmd(struct scsi_xfer *sc_xfer)
{
	struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc;
 	struct channel_softc *chp = as->chp;
	struct ata_drive_datas *drvp = &chp->ch_drive[as->drive];
	struct wdc_xfer *xfer;
	int s;
	int idx;

	WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d start\n",
	    chp->wdc->sc_dev.dv_xname, chp->channel, as->drive), DEBUG_XFERS);

	if (sc_xfer->sc_link->target != 0) {
		sc_xfer->error = XS_DRIVER_STUFFUP;
		scsi_done(sc_xfer);
		return;
	}

	xfer = sc_xfer->io;
	wdc_scrub_xfer(xfer);
	if (sc_xfer->flags & SCSI_POLL)
		xfer->c_flags |= C_POLL;
	xfer->drive = as->drive;
	xfer->c_flags |= C_ATAPI;
	xfer->cmd = sc_xfer;
	xfer->databuf = sc_xfer->data;
	xfer->c_bcount = sc_xfer->datalen;
	xfer->c_start = wdc_atapi_start;
	xfer->c_intr = wdc_atapi_intr;

	timeout_set(&xfer->atapi_poll_to, wdc_atapi_timer_handler, chp);

	WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d ",
	    chp->wdc->sc_dev.dv_xname, chp->channel, as->drive),
	    DEBUG_XFERS | DEBUG_ERRORS);

	for (idx = 0; idx < sc_xfer->cmdlen; idx++) {
		WDCDEBUG_PRINT((" %02x",
				   ((unsigned char *)sc_xfer->cmd)[idx]),
		    DEBUG_XFERS | DEBUG_ERRORS);
	}
	WDCDEBUG_PRINT(("\n"), DEBUG_XFERS | DEBUG_ERRORS);

	s = splbio();

	if (drvp->atapi_cap & ACAP_DSC) {
		WDCDEBUG_PRINT(("about to send cmd 0x%x ",
		    sc_xfer->cmd->opcode), DEBUG_DSC);
		switch (sc_xfer->cmd->opcode) {
		case READ:
		case WRITE:
			xfer->c_flags |= C_MEDIA_ACCESS;

			/* If we are not in buffer availability mode,
			   we limit the first request to 0 bytes, which
			   gets us into buffer availability mode without
			   holding the bus.  */
			if (!(drvp->drive_flags & DRIVE_DSCBA)) {
				xfer->c_bcount = 0;
				xfer->transfer_len =
				  _3btol(((struct scsi_rw_tape *)
					  sc_xfer->cmd)->len);
				_lto3b(0,
				    ((struct scsi_rw_tape *)
				    sc_xfer->cmd)->len);
				xfer->c_done = wdc_atapi_tape_done;
				WDCDEBUG_PRINT(
				    ("R/W in completion mode, do 0 blocks\n"),
				    DEBUG_DSC);
			} else
				WDCDEBUG_PRINT(("R/W %d blocks %d bytes\n",
				    _3btol(((struct scsi_rw_tape *)
					sc_xfer->cmd)->len),
				    sc_xfer->datalen),
				    DEBUG_DSC);

			/* DSC will change to buffer availability mode.
			   We reflect this in wdc_atapi_intr.  */
			break;

		case ERASE:		/* Media access commands */
		case LOAD:
		case REWIND:
		case SPACE:
		case WRITE_FILEMARKS:
#if 0
		case LOCATE:
		case READ_POSITION:
#endif

			xfer->c_flags |= C_MEDIA_ACCESS;
			break;

		default:
			WDCDEBUG_PRINT(("no media access\n"), DEBUG_DSC);
		}
	}

	wdc_exec_xfer(chp, xfer);
	splx(s);
}