scsi_cd: make the media check asynchronous
This makes the media check process asynchronous, so we no longer block in cdstrategy() to check for media. PR: 219857 Obtained from: ken MFC after: 3 weeks
This commit is contained in:
parent
54ba4b35c0
commit
01f4cc17c5
@ -114,13 +114,21 @@ typedef enum {
|
|||||||
CD_FLAG_RETRY_UA = 0x0200,
|
CD_FLAG_RETRY_UA = 0x0200,
|
||||||
CD_FLAG_VALID_MEDIA = 0x0400,
|
CD_FLAG_VALID_MEDIA = 0x0400,
|
||||||
CD_FLAG_VALID_TOC = 0x0800,
|
CD_FLAG_VALID_TOC = 0x0800,
|
||||||
CD_FLAG_SCTX_INIT = 0x1000
|
CD_FLAG_SCTX_INIT = 0x1000,
|
||||||
|
CD_FLAG_MEDIA_WAIT = 0x2000,
|
||||||
|
CD_FLAG_MEDIA_SCAN_ACT = 0x4000
|
||||||
} cd_flags;
|
} cd_flags;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CD_CCB_PROBE = 0x01,
|
CD_CCB_PROBE = 0x01,
|
||||||
CD_CCB_BUFFER_IO = 0x02,
|
CD_CCB_BUFFER_IO = 0x02,
|
||||||
CD_CCB_TUR = 0x04,
|
CD_CCB_TUR = 0x03,
|
||||||
|
CD_CCB_MEDIA_PREVENT = 0x04,
|
||||||
|
CD_CCB_MEDIA_ALLOW = 0x05,
|
||||||
|
CD_CCB_MEDIA_SIZE = 0x06,
|
||||||
|
CD_CCB_MEDIA_TOC_HDR = 0x07,
|
||||||
|
CD_CCB_MEDIA_TOC_FULL = 0x08,
|
||||||
|
CD_CCB_MEDIA_TOC_LEAD = 0x09,
|
||||||
CD_CCB_TYPE_MASK = 0x0F,
|
CD_CCB_TYPE_MASK = 0x0F,
|
||||||
CD_CCB_RETRY_UA = 0x10
|
CD_CCB_RETRY_UA = 0x10
|
||||||
} cd_ccb_state;
|
} cd_ccb_state;
|
||||||
@ -140,7 +148,13 @@ struct cd_toc_single {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CD_STATE_PROBE,
|
CD_STATE_PROBE,
|
||||||
CD_STATE_NORMAL
|
CD_STATE_NORMAL,
|
||||||
|
CD_STATE_MEDIA_PREVENT,
|
||||||
|
CD_STATE_MEDIA_ALLOW,
|
||||||
|
CD_STATE_MEDIA_SIZE,
|
||||||
|
CD_STATE_MEDIA_TOC_HDR,
|
||||||
|
CD_STATE_MEDIA_TOC_FULL,
|
||||||
|
CD_STATE_MEDIA_TOC_LEAD
|
||||||
} cd_state;
|
} cd_state;
|
||||||
|
|
||||||
struct cd_softc {
|
struct cd_softc {
|
||||||
@ -161,6 +175,8 @@ struct cd_softc {
|
|||||||
struct sysctl_oid *sysctl_tree;
|
struct sysctl_oid *sysctl_tree;
|
||||||
STAILQ_HEAD(, cd_mode_params) mode_queue;
|
STAILQ_HEAD(, cd_mode_params) mode_queue;
|
||||||
struct cd_tocdata toc;
|
struct cd_tocdata toc;
|
||||||
|
int toc_read_len;
|
||||||
|
struct cd_toc_single leadout;
|
||||||
struct disk *disk;
|
struct disk *disk;
|
||||||
struct callout mediapoll_c;
|
struct callout mediapoll_c;
|
||||||
|
|
||||||
@ -246,7 +262,8 @@ static void cddone(struct cam_periph *periph,
|
|||||||
static union cd_pages *cdgetpage(struct cd_mode_params *mode_params);
|
static union cd_pages *cdgetpage(struct cd_mode_params *mode_params);
|
||||||
static int cdgetpagesize(int page_num);
|
static int cdgetpagesize(int page_num);
|
||||||
static void cdprevent(struct cam_periph *periph, int action);
|
static void cdprevent(struct cam_periph *periph, int action);
|
||||||
static int cdcheckmedia(struct cam_periph *periph);
|
static void cdmediaprobedone(struct cam_periph *periph);
|
||||||
|
static int cdcheckmedia(struct cam_periph *periph, int do_wait);
|
||||||
static int cdsize(struct cam_periph *periph, u_int32_t *size);
|
static int cdsize(struct cam_periph *periph, u_int32_t *size);
|
||||||
static int cd6byteworkaround(union ccb *ccb);
|
static int cd6byteworkaround(union ccb *ccb);
|
||||||
static int cderror(union ccb *ccb, u_int32_t cam_flags,
|
static int cderror(union ccb *ccb, u_int32_t cam_flags,
|
||||||
@ -755,7 +772,7 @@ cdopen(struct disk *dp)
|
|||||||
* if we don't have media, but then we don't allow anything but the
|
* if we don't have media, but then we don't allow anything but the
|
||||||
* CDIOCEJECT/CDIOCCLOSE ioctls if there is no media.
|
* CDIOCEJECT/CDIOCCLOSE ioctls if there is no media.
|
||||||
*/
|
*/
|
||||||
cdcheckmedia(periph);
|
cdcheckmedia(periph, /*do_wait*/ 1);
|
||||||
|
|
||||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdopen\n"));
|
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdopen\n"));
|
||||||
cam_periph_unhold(periph);
|
cam_periph_unhold(periph);
|
||||||
@ -851,27 +868,19 @@ cdstrategy(struct bio *bp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* If we don't have valid media, look for it before trying to
|
|
||||||
* schedule the I/O.
|
|
||||||
*/
|
|
||||||
if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0) {
|
|
||||||
int error;
|
|
||||||
|
|
||||||
error = cdcheckmedia(periph);
|
|
||||||
if (error != 0) {
|
|
||||||
cam_periph_unlock(periph);
|
|
||||||
biofinish(bp, NULL, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Place it in the queue of disk activities for this disk
|
* Place it in the queue of disk activities for this disk
|
||||||
*/
|
*/
|
||||||
bioq_disksort(&softc->bio_queue, bp);
|
bioq_disksort(&softc->bio_queue, bp);
|
||||||
|
|
||||||
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
/*
|
||||||
|
* If we don't know that we have valid media, schedule the media
|
||||||
|
* check first. The I/O will get executed after the media check.
|
||||||
|
*/
|
||||||
|
if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0)
|
||||||
|
cdcheckmedia(periph, /*do_wait*/ 0);
|
||||||
|
else
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
||||||
|
|
||||||
cam_periph_unlock(periph);
|
cam_periph_unlock(periph);
|
||||||
return;
|
return;
|
||||||
@ -883,7 +892,6 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb)
|
|||||||
struct cd_softc *softc;
|
struct cd_softc *softc;
|
||||||
struct bio *bp;
|
struct bio *bp;
|
||||||
struct ccb_scsiio *csio;
|
struct ccb_scsiio *csio;
|
||||||
struct scsi_read_capacity_data *rcap;
|
|
||||||
|
|
||||||
softc = (struct cd_softc *)periph->softc;
|
softc = (struct cd_softc *)periph->softc;
|
||||||
|
|
||||||
@ -964,16 +972,40 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CD_STATE_PROBE:
|
case CD_STATE_PROBE:
|
||||||
|
case CD_STATE_MEDIA_SIZE:
|
||||||
{
|
{
|
||||||
|
struct scsi_read_capacity_data *rcap;
|
||||||
|
|
||||||
rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap),
|
rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap),
|
||||||
M_SCSICD, M_NOWAIT | M_ZERO);
|
M_SCSICD, M_NOWAIT | M_ZERO);
|
||||||
if (rcap == NULL) {
|
if (rcap == NULL) {
|
||||||
xpt_print(periph->path,
|
xpt_print(periph->path,
|
||||||
"cdstart: Couldn't malloc read_capacity data\n");
|
"%s: Couldn't malloc read_capacity data\n",
|
||||||
/* cd_free_periph??? */
|
__func__);
|
||||||
|
xpt_release_ccb(start_ccb);
|
||||||
|
/*
|
||||||
|
* We can't probe because we can't allocate memory,
|
||||||
|
* so invalidate the peripheral. The system probably
|
||||||
|
* has larger problems at this stage. If we've
|
||||||
|
* already probed (and are re-probing capacity), we
|
||||||
|
* don't need to invalidate.
|
||||||
|
*
|
||||||
|
* XXX KDM need to reset probe state and kick out
|
||||||
|
* pending I/O.
|
||||||
|
*/
|
||||||
|
if (softc->state == CD_STATE_PROBE)
|
||||||
|
cam_periph_invalidate(periph);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the default capacity and sector size to something that
|
||||||
|
* GEOM can handle. This will get reset when a read capacity
|
||||||
|
* completes successfully.
|
||||||
|
*/
|
||||||
|
softc->disk->d_sectorsize = 2048;
|
||||||
|
softc->disk->d_mediasize = 0;
|
||||||
|
|
||||||
csio = &start_ccb->csio;
|
csio = &start_ccb->csio;
|
||||||
scsi_read_capacity(csio,
|
scsi_read_capacity(csio,
|
||||||
/*retries*/ cd_retry_count,
|
/*retries*/ cd_retry_count,
|
||||||
@ -983,7 +1015,108 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb)
|
|||||||
SSD_FULL_SIZE,
|
SSD_FULL_SIZE,
|
||||||
/*timeout*/20000);
|
/*timeout*/20000);
|
||||||
start_ccb->ccb_h.ccb_bp = NULL;
|
start_ccb->ccb_h.ccb_bp = NULL;
|
||||||
start_ccb->ccb_h.ccb_state = CD_CCB_PROBE;
|
if (softc->state == CD_STATE_PROBE)
|
||||||
|
start_ccb->ccb_h.ccb_state = CD_CCB_PROBE;
|
||||||
|
else
|
||||||
|
start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_SIZE;
|
||||||
|
xpt_action(start_ccb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CD_STATE_MEDIA_ALLOW:
|
||||||
|
case CD_STATE_MEDIA_PREVENT:
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If the CD is already locked, we don't need to do this.
|
||||||
|
* Move on to the capacity check.
|
||||||
|
*/
|
||||||
|
if ((softc->flags & CD_FLAG_DISC_LOCKED) != 0) {
|
||||||
|
softc->state = CD_STATE_MEDIA_SIZE;
|
||||||
|
xpt_release_ccb(start_ccb);
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
scsi_prevent(&start_ccb->csio,
|
||||||
|
/*retries*/ cd_retry_count,
|
||||||
|
/*cbfcnp*/ cddone,
|
||||||
|
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||||
|
/*action*/ (softc->state == CD_STATE_MEDIA_ALLOW) ?
|
||||||
|
PR_ALLOW : PR_PREVENT,
|
||||||
|
/*sense_len*/ SSD_FULL_SIZE,
|
||||||
|
/*timeout*/ 60000);
|
||||||
|
|
||||||
|
start_ccb->ccb_h.ccb_bp = NULL;
|
||||||
|
if (softc->state == CD_STATE_MEDIA_ALLOW)
|
||||||
|
start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_ALLOW;
|
||||||
|
else
|
||||||
|
start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_PREVENT;
|
||||||
|
xpt_action(start_ccb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CD_STATE_MEDIA_TOC_HDR: {
|
||||||
|
struct ioc_toc_header *toch;
|
||||||
|
|
||||||
|
bzero(&softc->toc, sizeof(softc->toc));
|
||||||
|
|
||||||
|
toch = &softc->toc.header;
|
||||||
|
|
||||||
|
scsi_read_toc(&start_ccb->csio,
|
||||||
|
/*retries*/ cd_retry_count,
|
||||||
|
/*cbfcnp*/ cddone,
|
||||||
|
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||||
|
/*byte1_flags*/ 0,
|
||||||
|
/*format*/ SRTOC_FORMAT_TOC,
|
||||||
|
/*track*/ 0,
|
||||||
|
/*data_ptr*/ (uint8_t *)toch,
|
||||||
|
/*dxfer_len*/ sizeof(*toch),
|
||||||
|
/*sense_len*/ SSD_FULL_SIZE,
|
||||||
|
/*timeout*/ 50000);
|
||||||
|
start_ccb->ccb_h.ccb_bp = NULL;
|
||||||
|
start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_TOC_HDR;
|
||||||
|
xpt_action(start_ccb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CD_STATE_MEDIA_TOC_FULL: {
|
||||||
|
|
||||||
|
bzero(&softc->toc, sizeof(softc->toc));
|
||||||
|
|
||||||
|
scsi_read_toc(&start_ccb->csio,
|
||||||
|
/*retries*/ cd_retry_count,
|
||||||
|
/*cbfcnp*/ cddone,
|
||||||
|
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||||
|
/*byte1_flags*/ 0,
|
||||||
|
/*format*/ SRTOC_FORMAT_TOC,
|
||||||
|
/*track*/ 0,
|
||||||
|
/*data_ptr*/ (uint8_t *)&softc->toc,
|
||||||
|
/*dxfer_len*/ softc->toc_read_len ?
|
||||||
|
softc->toc_read_len :
|
||||||
|
sizeof(softc->toc),
|
||||||
|
/*sense_len*/ SSD_FULL_SIZE,
|
||||||
|
/*timeout*/ 50000);
|
||||||
|
start_ccb->ccb_h.ccb_bp = NULL;
|
||||||
|
start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_TOC_FULL;
|
||||||
|
xpt_action(start_ccb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CD_STATE_MEDIA_TOC_LEAD: {
|
||||||
|
struct cd_toc_single *leadout;
|
||||||
|
|
||||||
|
leadout = &softc->leadout;
|
||||||
|
bzero(leadout, sizeof(*leadout));
|
||||||
|
|
||||||
|
scsi_read_toc(&start_ccb->csio,
|
||||||
|
/*retries*/ cd_retry_count,
|
||||||
|
/*cbfcnp*/ cddone,
|
||||||
|
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||||
|
/*byte1_flags*/ CD_MSF,
|
||||||
|
/*format*/ SRTOC_FORMAT_TOC,
|
||||||
|
/*track*/ LEADOUT,
|
||||||
|
/*data_ptr*/ (uint8_t *)leadout,
|
||||||
|
/*dxfer_len*/ sizeof(*leadout),
|
||||||
|
/*sense_len*/ SSD_FULL_SIZE,
|
||||||
|
/*timeout*/ 50000);
|
||||||
|
start_ccb->ccb_h.ccb_bp = NULL;
|
||||||
|
start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_TOC_LEAD;
|
||||||
xpt_action(start_ccb);
|
xpt_action(start_ccb);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1251,6 +1384,293 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
|
|||||||
cam_periph_release_locked(periph);
|
cam_periph_release_locked(periph);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case CD_CCB_MEDIA_ALLOW:
|
||||||
|
case CD_CCB_MEDIA_PREVENT:
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
int is_prevent;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
|
||||||
|
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||||
|
error = cderror(done_ccb, CAM_RETRY_SELTO,
|
||||||
|
SF_RETRY_UA | SF_NO_PRINT);
|
||||||
|
}
|
||||||
|
if (error == ERESTART)
|
||||||
|
return;
|
||||||
|
if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
|
||||||
|
cam_release_devq(done_ccb->ccb_h.path,
|
||||||
|
/*relsim_flags*/0,
|
||||||
|
/*reduction*/0,
|
||||||
|
/*timeout*/0,
|
||||||
|
/*getcount_only*/0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that just like the original cdcheckmedia(), we do
|
||||||
|
* a prevent without failing the whole operation if the
|
||||||
|
* prevent fails. We try, but keep going if it doesn't
|
||||||
|
* work.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((done_ccb->ccb_h.ccb_state & CD_CCB_TYPE_MASK) ==
|
||||||
|
CD_CCB_MEDIA_PREVENT)
|
||||||
|
is_prevent = 1;
|
||||||
|
else
|
||||||
|
is_prevent = 0;
|
||||||
|
|
||||||
|
xpt_release_ccb(done_ccb);
|
||||||
|
|
||||||
|
if (is_prevent != 0) {
|
||||||
|
if (error == 0)
|
||||||
|
softc->flags |= CD_FLAG_DISC_LOCKED;
|
||||||
|
else
|
||||||
|
softc->flags &= ~CD_FLAG_DISC_LOCKED;
|
||||||
|
softc->state = CD_STATE_MEDIA_SIZE;
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
||||||
|
} else {
|
||||||
|
if (error == 0)
|
||||||
|
softc->flags &= ~CD_FLAG_DISC_LOCKED;
|
||||||
|
softc->state = CD_STATE_NORMAL;
|
||||||
|
if (bioq_first(&softc->bio_queue) != NULL)
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case CD_CCB_MEDIA_SIZE:
|
||||||
|
{
|
||||||
|
struct scsi_read_capacity_data *rdcap;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
if ((csio->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||||
|
error = cderror(done_ccb, CAM_RETRY_SELTO,
|
||||||
|
SF_RETRY_UA | SF_NO_PRINT);
|
||||||
|
}
|
||||||
|
if (error == ERESTART)
|
||||||
|
return;
|
||||||
|
if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
|
||||||
|
cam_release_devq(done_ccb->ccb_h.path,
|
||||||
|
/*relsim_flags*/0,
|
||||||
|
/*reduction*/0,
|
||||||
|
/*timeout*/0,
|
||||||
|
/*getcount_only*/0);
|
||||||
|
rdcap = (struct scsi_read_capacity_data *)csio->data_ptr;
|
||||||
|
|
||||||
|
if (error == 0) {
|
||||||
|
softc->params.disksize =scsi_4btoul(rdcap->addr) + 1;
|
||||||
|
softc->params.blksize = scsi_4btoul(rdcap->length);
|
||||||
|
|
||||||
|
/* Make sure we got at least some block size. */
|
||||||
|
if (softc->params.blksize == 0)
|
||||||
|
error = EIO;
|
||||||
|
/*
|
||||||
|
* SCSI-3 mandates that the reported blocksize shall be
|
||||||
|
* 2048. Older drives sometimes report funny values,
|
||||||
|
* trim it down to 2048, or other parts of the kernel
|
||||||
|
* will get confused.
|
||||||
|
*
|
||||||
|
* XXX we leave drives alone that might report 512
|
||||||
|
* bytes, as well as drives reporting more weird
|
||||||
|
* sizes like perhaps 4K.
|
||||||
|
*/
|
||||||
|
if (softc->params.blksize > 2048
|
||||||
|
&& softc->params.blksize <= 2352)
|
||||||
|
softc->params.blksize = 2048;
|
||||||
|
}
|
||||||
|
free(rdcap, M_SCSICD);
|
||||||
|
|
||||||
|
if (error == 0) {
|
||||||
|
softc->disk->d_sectorsize = softc->params.blksize;
|
||||||
|
softc->disk->d_mediasize =
|
||||||
|
(off_t)softc->params.blksize *
|
||||||
|
softc->params.disksize;
|
||||||
|
softc->flags |= CD_FLAG_SAW_MEDIA | CD_FLAG_VALID_MEDIA;
|
||||||
|
softc->state = CD_STATE_MEDIA_TOC_HDR;
|
||||||
|
} else {
|
||||||
|
softc->flags &= ~(CD_FLAG_VALID_MEDIA |
|
||||||
|
CD_FLAG_VALID_TOC);
|
||||||
|
bioq_flush(&softc->bio_queue, NULL, EINVAL);
|
||||||
|
softc->state = CD_STATE_MEDIA_ALLOW;
|
||||||
|
cdmediaprobedone(periph);
|
||||||
|
}
|
||||||
|
xpt_release_ccb(done_ccb);
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case CD_CCB_MEDIA_TOC_HDR:
|
||||||
|
case CD_CCB_MEDIA_TOC_FULL:
|
||||||
|
case CD_CCB_MEDIA_TOC_LEAD:
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
struct ioc_toc_header *toch;
|
||||||
|
int num_entries;
|
||||||
|
int cdindex;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
|
||||||
|
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||||
|
error = cderror(done_ccb, CAM_RETRY_SELTO,
|
||||||
|
SF_RETRY_UA | SF_NO_PRINT);
|
||||||
|
}
|
||||||
|
if (error == ERESTART)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
|
||||||
|
cam_release_devq(done_ccb->ccb_h.path,
|
||||||
|
/*relsim_flags*/0,
|
||||||
|
/*reduction*/0,
|
||||||
|
/*timeout*/0,
|
||||||
|
/*getcount_only*/0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We will get errors here for media that doesn't have a table
|
||||||
|
* of contents. According to the MMC-3 spec: "When a Read
|
||||||
|
* TOC/PMA/ATIP command is presented for a DDCD/CD-R/RW media,
|
||||||
|
* where the first TOC has not been recorded (no complete
|
||||||
|
* session) and the Format codes 0000b, 0001b, or 0010b are
|
||||||
|
* specified, this command shall be rejected with an INVALID
|
||||||
|
* FIELD IN CDB. Devices that are not capable of reading an
|
||||||
|
* incomplete session on DDC/CD-R/RW media shall report
|
||||||
|
* CANNOT READ MEDIUM - INCOMPATIBLE FORMAT."
|
||||||
|
*
|
||||||
|
* So this isn't fatal if we can't read the table of contents,
|
||||||
|
* it just means that the user won't be able to issue the
|
||||||
|
* play tracks ioctl, and likely lots of other stuff won't
|
||||||
|
* work either. They need to burn the CD before we can do
|
||||||
|
* a whole lot with it. So we don't print anything here if
|
||||||
|
* we get an error back.
|
||||||
|
*
|
||||||
|
* We also bail out if the drive doesn't at least give us
|
||||||
|
* the full TOC header.
|
||||||
|
*/
|
||||||
|
if ((error != 0)
|
||||||
|
|| ((csio->dxfer_len - csio->resid) <
|
||||||
|
sizeof(struct ioc_toc_header))) {
|
||||||
|
softc->flags &= ~CD_FLAG_VALID_TOC;
|
||||||
|
bzero(&softc->toc, sizeof(softc->toc));
|
||||||
|
/*
|
||||||
|
* Failing the TOC read is not an error.
|
||||||
|
*/
|
||||||
|
softc->state = CD_STATE_NORMAL;
|
||||||
|
xpt_release_ccb(done_ccb);
|
||||||
|
|
||||||
|
cdmediaprobedone(periph);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Go ahead and schedule I/O execution if there is
|
||||||
|
* anything in the queue. It'll probably get
|
||||||
|
* kicked out with an error.
|
||||||
|
*/
|
||||||
|
if (bioq_first(&softc->bio_queue) != NULL)
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that this is NOT the storage location used for the
|
||||||
|
* leadout!
|
||||||
|
*/
|
||||||
|
toch = &softc->toc.header;
|
||||||
|
|
||||||
|
if (softc->quirks & CD_Q_BCD_TRACKS) {
|
||||||
|
toch->starting_track = bcd2bin(toch->starting_track);
|
||||||
|
toch->ending_track = bcd2bin(toch->ending_track);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Number of TOC entries, plus leadout */
|
||||||
|
num_entries = (toch->ending_track - toch->starting_track) + 2;
|
||||||
|
cdindex = toch->starting_track + num_entries -1;
|
||||||
|
|
||||||
|
if ((done_ccb->ccb_h.ccb_state & CD_CCB_TYPE_MASK) ==
|
||||||
|
CD_CCB_MEDIA_TOC_HDR) {
|
||||||
|
if (num_entries <= 0) {
|
||||||
|
softc->flags &= ~CD_FLAG_VALID_TOC;
|
||||||
|
bzero(&softc->toc, sizeof(softc->toc));
|
||||||
|
/*
|
||||||
|
* Failing the TOC read is not an error.
|
||||||
|
*/
|
||||||
|
softc->state = CD_STATE_NORMAL;
|
||||||
|
xpt_release_ccb(done_ccb);
|
||||||
|
|
||||||
|
cdmediaprobedone(periph);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Go ahead and schedule I/O execution if
|
||||||
|
* there is anything in the queue. It'll
|
||||||
|
* probably get kicked out with an error.
|
||||||
|
*/
|
||||||
|
if (bioq_first(&softc->bio_queue) != NULL)
|
||||||
|
xpt_schedule(periph,
|
||||||
|
CAM_PRIORITY_NORMAL);
|
||||||
|
} else {
|
||||||
|
softc->toc_read_len = num_entries *
|
||||||
|
sizeof(struct cd_toc_entry);
|
||||||
|
softc->toc_read_len += sizeof(*toch);
|
||||||
|
|
||||||
|
softc->state = CD_STATE_MEDIA_TOC_FULL;
|
||||||
|
xpt_release_ccb(done_ccb);
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if ((done_ccb->ccb_h.ccb_state & CD_CCB_TYPE_MASK) ==
|
||||||
|
CD_CCB_MEDIA_TOC_LEAD) {
|
||||||
|
struct cd_toc_single *leadout;
|
||||||
|
|
||||||
|
leadout = (struct cd_toc_single *)csio->data_ptr;
|
||||||
|
softc->toc.entries[cdindex - toch->starting_track] =
|
||||||
|
leadout->entry;
|
||||||
|
} else if (((done_ccb->ccb_h.ccb_state & CD_CCB_TYPE_MASK) ==
|
||||||
|
CD_CCB_MEDIA_TOC_FULL)
|
||||||
|
&& (cdindex == toch->ending_track + 1)) {
|
||||||
|
/*
|
||||||
|
* XXX KDM is this necessary? Probably only if the
|
||||||
|
* drive doesn't return leadout information with the
|
||||||
|
* table of contents.
|
||||||
|
*/
|
||||||
|
softc->state = CD_STATE_MEDIA_TOC_LEAD;
|
||||||
|
xpt_release_ccb(done_ccb);
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (softc->quirks & CD_Q_BCD_TRACKS) {
|
||||||
|
for (cdindex = 0; cdindex < num_entries - 1; cdindex++){
|
||||||
|
softc->toc.entries[cdindex].track =
|
||||||
|
bcd2bin(softc->toc.entries[cdindex].track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
softc->flags |= CD_FLAG_VALID_TOC;
|
||||||
|
/* If the first track is audio, correct sector size. */
|
||||||
|
if ((softc->toc.entries[0].control & 4) == 0) {
|
||||||
|
softc->disk->d_sectorsize =softc->params.blksize = 2352;
|
||||||
|
softc->disk->d_mediasize =
|
||||||
|
(off_t)softc->params.blksize *
|
||||||
|
softc->params.disksize;
|
||||||
|
}
|
||||||
|
softc->state = CD_STATE_NORMAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We unconditionally (re)set the blocksize each time the
|
||||||
|
* CD device is opened. This is because the CD can change,
|
||||||
|
* and therefore the blocksize might change.
|
||||||
|
* XXX problems here if some slice or partition is still
|
||||||
|
* open with the old size?
|
||||||
|
*/
|
||||||
|
if ((softc->disk->d_devstat->flags & DEVSTAT_BS_UNAVAILABLE)!=0)
|
||||||
|
softc->disk->d_devstat->flags &=
|
||||||
|
~DEVSTAT_BS_UNAVAILABLE;
|
||||||
|
softc->disk->d_devstat->block_size = softc->params.blksize;
|
||||||
|
|
||||||
|
xpt_release_ccb(done_ccb);
|
||||||
|
|
||||||
|
cdmediaprobedone(periph);
|
||||||
|
|
||||||
|
if (bioq_first(&softc->bio_queue) != NULL)
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1343,7 +1763,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td)
|
|||||||
&& ((cmd != CDIOCCLOSE)
|
&& ((cmd != CDIOCCLOSE)
|
||||||
&& (cmd != CDIOCEJECT))
|
&& (cmd != CDIOCEJECT))
|
||||||
&& (IOCGROUP(cmd) == 'c')) {
|
&& (IOCGROUP(cmd) == 'c')) {
|
||||||
error = cdcheckmedia(periph);
|
error = cdcheckmedia(periph, /*do_wait*/ 1);
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
cam_periph_unhold(periph);
|
cam_periph_unhold(periph);
|
||||||
cam_periph_unlock(periph);
|
cam_periph_unlock(periph);
|
||||||
@ -2227,10 +2647,65 @@ cdprevent(struct cam_periph *periph, int action)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cdmediaprobedone(struct cam_periph *periph)
|
||||||
|
{
|
||||||
|
struct cd_softc *softc;
|
||||||
|
|
||||||
|
softc = (struct cd_softc *)periph->softc;
|
||||||
|
|
||||||
|
softc->flags &= ~CD_FLAG_MEDIA_SCAN_ACT;
|
||||||
|
|
||||||
|
if ((softc->flags & CD_FLAG_MEDIA_WAIT) != 0) {
|
||||||
|
softc->flags &= ~CD_FLAG_MEDIA_WAIT;
|
||||||
|
wakeup(&softc->toc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX: the disk media and sector size is only really able to change
|
* XXX: the disk media and sector size is only really able to change
|
||||||
* XXX: while the device is closed.
|
* XXX: while the device is closed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
cdcheckmedia(struct cam_periph *periph, int do_wait)
|
||||||
|
{
|
||||||
|
struct cd_softc *softc;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
softc = (struct cd_softc *)periph->softc;
|
||||||
|
error = 0;
|
||||||
|
|
||||||
|
if ((do_wait != 0)
|
||||||
|
&& ((softc->flags & CD_FLAG_MEDIA_WAIT) == 0)) {
|
||||||
|
softc->flags |= CD_FLAG_MEDIA_WAIT;
|
||||||
|
}
|
||||||
|
if ((softc->flags & CD_FLAG_MEDIA_SCAN_ACT) == 0) {
|
||||||
|
softc->state = CD_STATE_MEDIA_PREVENT;
|
||||||
|
softc->flags |= CD_FLAG_MEDIA_SCAN_ACT;
|
||||||
|
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_wait == 0)
|
||||||
|
goto bailout;
|
||||||
|
|
||||||
|
error = msleep(&softc->toc, cam_periph_mtx(periph), PRIBIO,"cdmedia",0);
|
||||||
|
|
||||||
|
if (error != 0)
|
||||||
|
goto bailout;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to see whether we have a valid size from the media. We
|
||||||
|
* may or may not have a valid TOC.
|
||||||
|
*/
|
||||||
|
if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0)
|
||||||
|
error = EINVAL;
|
||||||
|
bailout:
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
static int
|
static int
|
||||||
cdcheckmedia(struct cam_periph *periph)
|
cdcheckmedia(struct cam_periph *periph)
|
||||||
{
|
{
|
||||||
@ -2371,6 +2846,7 @@ cdcheckmedia(struct cam_periph *periph)
|
|||||||
|
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cdsize(struct cam_periph *periph, u_int32_t *size)
|
cdsize(struct cam_periph *periph, u_int32_t *size)
|
||||||
@ -2650,7 +3126,6 @@ static int
|
|||||||
cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start,
|
cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start,
|
||||||
u_int8_t *data, u_int32_t len, u_int32_t sense_flags)
|
u_int8_t *data, u_int32_t len, u_int32_t sense_flags)
|
||||||
{
|
{
|
||||||
struct scsi_read_toc *scsi_cmd;
|
|
||||||
u_int32_t ntoc;
|
u_int32_t ntoc;
|
||||||
struct ccb_scsiio *csio;
|
struct ccb_scsiio *csio;
|
||||||
union ccb *ccb;
|
union ccb *ccb;
|
||||||
@ -2663,28 +3138,17 @@ cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start,
|
|||||||
|
|
||||||
csio = &ccb->csio;
|
csio = &ccb->csio;
|
||||||
|
|
||||||
cam_fill_csio(csio,
|
scsi_read_toc(csio,
|
||||||
/* retries */ cd_retry_count,
|
/* retries */ cd_retry_count,
|
||||||
/* cbfcnp */ NULL,
|
/* cbfcnp */ NULL,
|
||||||
/* flags */ CAM_DIR_IN,
|
|
||||||
/* tag_action */ MSG_SIMPLE_Q_TAG,
|
/* tag_action */ MSG_SIMPLE_Q_TAG,
|
||||||
|
/* byte1_flags */ (mode == CD_MSF_FORMAT) ? CD_MSF : 0,
|
||||||
|
/* format */ SRTOC_FORMAT_TOC,
|
||||||
|
/* track*/ start,
|
||||||
/* data_ptr */ data,
|
/* data_ptr */ data,
|
||||||
/* dxfer_len */ len,
|
/* dxfer_len */ len,
|
||||||
/* sense_len */ SSD_FULL_SIZE,
|
/* sense_len */ SSD_FULL_SIZE,
|
||||||
sizeof(struct scsi_read_toc),
|
/* timeout */ 50000);
|
||||||
/* timeout */ 50000);
|
|
||||||
|
|
||||||
scsi_cmd = (struct scsi_read_toc *)&csio->cdb_io.cdb_bytes;
|
|
||||||
bzero (scsi_cmd, sizeof(*scsi_cmd));
|
|
||||||
|
|
||||||
if (mode == CD_MSF_FORMAT)
|
|
||||||
scsi_cmd->byte2 |= CD_MSF;
|
|
||||||
scsi_cmd->from_track = start;
|
|
||||||
/* scsi_ulto2b(ntoc, (u_int8_t *)scsi_cmd->data_len); */
|
|
||||||
scsi_cmd->data_len[0] = (ntoc) >> 8;
|
|
||||||
scsi_cmd->data_len[1] = (ntoc) & 0xff;
|
|
||||||
|
|
||||||
scsi_cmd->op_code = READ_TOC;
|
|
||||||
|
|
||||||
error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
|
error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
|
||||||
/*sense_flags*/SF_RETRY_UA | sense_flags);
|
/*sense_flags*/SF_RETRY_UA | sense_flags);
|
||||||
@ -3743,3 +4207,38 @@ scsi_read_dvd_structure(struct ccb_scsiio *csio, u_int32_t retries,
|
|||||||
sizeof(*scsi_cmd),
|
sizeof(*scsi_cmd),
|
||||||
timeout);
|
timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scsi_read_toc(struct ccb_scsiio *csio, uint32_t retries,
|
||||||
|
void (*cbfcnp)(struct cam_periph *, union ccb *),
|
||||||
|
uint8_t tag_action, uint8_t byte1_flags, uint8_t format,
|
||||||
|
uint8_t track, uint8_t *data_ptr, uint32_t dxfer_len,
|
||||||
|
int sense_len, int timeout)
|
||||||
|
{
|
||||||
|
struct scsi_read_toc *scsi_cmd;
|
||||||
|
|
||||||
|
scsi_cmd = (struct scsi_read_toc *)&csio->cdb_io.cdb_bytes;
|
||||||
|
bzero(scsi_cmd, sizeof(*scsi_cmd));
|
||||||
|
scsi_cmd->op_code = READ_TOC;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The structure is counting from 1, the function counting from 0.
|
||||||
|
* The spec counts from 0. In MMC-6, there is only one flag, the
|
||||||
|
* MSF flag. But we put the whole byte in for a bit a future-proofing.
|
||||||
|
*/
|
||||||
|
scsi_cmd->byte2 = byte1_flags;
|
||||||
|
scsi_cmd->format = format;
|
||||||
|
scsi_cmd->from_track = track;
|
||||||
|
scsi_ulto2b(dxfer_len, scsi_cmd->data_len);
|
||||||
|
|
||||||
|
cam_fill_csio(csio,
|
||||||
|
/* retries */ retries,
|
||||||
|
/* cbfcnp */ cbfcnp,
|
||||||
|
/* flags */ CAM_DIR_IN,
|
||||||
|
/* tag_action */ tag_action,
|
||||||
|
/* data_ptr */ data_ptr,
|
||||||
|
/* dxfer_len */ dxfer_len,
|
||||||
|
/* sense_len */ sense_len,
|
||||||
|
sizeof(*scsi_cmd),
|
||||||
|
/* timeout */ timeout);
|
||||||
|
}
|
||||||
|
@ -231,6 +231,12 @@ struct scsi_read_toc
|
|||||||
u_int8_t op_code;
|
u_int8_t op_code;
|
||||||
u_int8_t byte2;
|
u_int8_t byte2;
|
||||||
u_int8_t format;
|
u_int8_t format;
|
||||||
|
#define SRTOC_FORMAT_TOC 0x00
|
||||||
|
#define SRTOC_FORMAT_LAST_ADDR 0x01
|
||||||
|
#define SRTOC_FORMAT_QSUB_TOC 0x02
|
||||||
|
#define SRTOC_FORMAT_QSUB_PMA 0x03
|
||||||
|
#define SRTOC_FORMAT_ATIP 0x04
|
||||||
|
#define SRTOC_FORMAT_CD_TEXT 0x05
|
||||||
u_int8_t unused[3];
|
u_int8_t unused[3];
|
||||||
u_int8_t from_track;
|
u_int8_t from_track;
|
||||||
u_int8_t data_len[2];
|
u_int8_t data_len[2];
|
||||||
@ -871,6 +877,12 @@ void scsi_read_dvd_structure(struct ccb_scsiio *csio, u_int32_t retries,
|
|||||||
u_int32_t dxfer_len, u_int8_t sense_len,
|
u_int32_t dxfer_len, u_int8_t sense_len,
|
||||||
u_int32_t timeout);
|
u_int32_t timeout);
|
||||||
|
|
||||||
|
void scsi_read_toc(struct ccb_scsiio *csio, uint32_t retries,
|
||||||
|
void (*cbfcnp)(struct cam_periph *, union ccb *),
|
||||||
|
uint8_t tag_action, uint8_t byte1_flags, uint8_t format,
|
||||||
|
uint8_t track, uint8_t *data_ptr, uint32_t dxfer_len,
|
||||||
|
int sense_len, int timeout);
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
||||||
#endif /*_SCSI_SCSI_CD_H*/
|
#endif /*_SCSI_SCSI_CD_H*/
|
||||||
|
Loading…
Reference in New Issue
Block a user