Author: avg
Date: Wed Sep 25 19:46:17 2019
New Revision: 352714
URL: https://svnweb.freebsd.org/changeset/base/352714

Log:
  MFC r351599,r351600: scsi_cd: make the media check asynchronous
  
  PR:           219857

Modified:
  stable/12/sys/cam/scsi/scsi_cd.c
  stable/12/sys/cam/scsi/scsi_cd.h
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/cam/scsi/scsi_cd.c
==============================================================================
--- stable/12/sys/cam/scsi/scsi_cd.c    Wed Sep 25 19:29:09 2019        
(r352713)
+++ stable/12/sys/cam/scsi/scsi_cd.c    Wed Sep 25 19:46:17 2019        
(r352714)
@@ -114,13 +114,21 @@ typedef enum {
        CD_FLAG_RETRY_UA        = 0x0200,
        CD_FLAG_VALID_MEDIA     = 0x0400,
        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;
 
 typedef enum {
        CD_CCB_PROBE            = 0x01,
        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_RETRY_UA         = 0x10
 } cd_ccb_state;
@@ -140,7 +148,13 @@ struct cd_toc_single {
 
 typedef enum {
        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;
 
 struct cd_softc {
@@ -161,6 +175,8 @@ struct cd_softc {
        struct sysctl_oid       *sysctl_tree;
        STAILQ_HEAD(, cd_mode_params)   mode_queue;
        struct cd_tocdata       toc;
+       int                     toc_read_len;
+       struct cd_toc_single    leadout;
        struct disk             *disk;
        struct callout          mediapoll_c;
 
@@ -246,8 +262,11 @@ static     void            cddone(struct cam_periph 
*periph,
 static union cd_pages  *cdgetpage(struct cd_mode_params *mode_params);
 static int             cdgetpagesize(int page_num);
 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);
+#if 0
 static int             cdsize(struct cam_periph *periph, u_int32_t *size);
+#endif
 static int             cd6byteworkaround(union ccb *ccb);
 static int             cderror(union ccb *ccb, u_int32_t cam_flags,
                                u_int32_t sense_flags);
@@ -755,7 +774,7 @@ cdopen(struct disk *dp)
         * if we don't have media, but then we don't allow anything but the
         * 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_periph_unhold(periph);
@@ -851,27 +870,19 @@ cdstrategy(struct bio *bp)
                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
         */
        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);
        return;
@@ -883,7 +894,6 @@ cdstart(struct cam_periph *periph, union ccb *start_cc
        struct cd_softc *softc;
        struct bio *bp;
        struct ccb_scsiio *csio;
-       struct scsi_read_capacity_data *rcap;
 
        softc = (struct cd_softc *)periph->softc;
 
@@ -964,16 +974,40 @@ cdstart(struct cam_periph *periph, union ccb *start_cc
                break;
        }
        case CD_STATE_PROBE:
+       case CD_STATE_MEDIA_SIZE:
        {
+               struct scsi_read_capacity_data *rcap;
 
                rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap),
                    M_SCSICD, M_NOWAIT | M_ZERO);
                if (rcap == NULL) {
                        xpt_print(periph->path,
-                           "cdstart: Couldn't malloc read_capacity data\n");
-                       /* cd_free_periph??? */
+                           "%s: Couldn't malloc read_capacity data\n",
+                           __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;
                }
+
+               /*
+                * 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;
                scsi_read_capacity(csio,
                                   /*retries*/ cd_retry_count,
@@ -983,11 +1017,112 @@ cdstart(struct cam_periph *periph, union ccb *start_cc
                                   SSD_FULL_SIZE,
                                   /*timeout*/20000);
                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);
+               break;
+       }
+       }
 }
 
 static void
@@ -1251,6 +1386,293 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
                cam_periph_release_locked(periph);
                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:
                break;
        }
@@ -1343,7 +1765,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int f
         && ((cmd != CDIOCCLOSE)
          && (cmd != CDIOCEJECT))
         && (IOCGROUP(cmd) == 'c')) {
-               error = cdcheckmedia(periph);
+               error = cdcheckmedia(periph, /*do_wait*/ 1);
                if (error != 0) {
                        cam_periph_unhold(periph);
                        cam_periph_unlock(periph);
@@ -2227,11 +2649,66 @@ 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: 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
 cdcheckmedia(struct cam_periph *periph)
 {
        struct cd_softc *softc;
@@ -2427,6 +2904,7 @@ cdsize(struct cam_periph *periph, u_int32_t *size)
        return (error);
 
 }
+#endif
 
 static int
 cd6byteworkaround(union ccb *ccb)
@@ -2650,7 +3128,6 @@ static int 
 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)
 {
-       struct scsi_read_toc *scsi_cmd;
        u_int32_t ntoc;
         struct ccb_scsiio *csio;
        union ccb *ccb;
@@ -2663,29 +3140,18 @@ cdreadtoc(struct cam_periph *periph, u_int32_t mode, u
 
        csio = &ccb->csio;
 
-       cam_fill_csio(csio, 
+       scsi_read_toc(csio,
                      /* retries */ cd_retry_count, 
                      /* cbfcnp */ NULL,
-                     /* flags */ CAM_DIR_IN,
                      /* tag_action */ MSG_SIMPLE_Q_TAG,
+                     /* byte1_flags */ (mode == CD_MSF_FORMAT) ? CD_MSF : 0,
+                     /* format */ SRTOC_FORMAT_TOC,
+                     /* track*/ start,
                      /* data_ptr */ data,
                      /* dxfer_len */ len,
                      /* 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,
                         /*sense_flags*/SF_RETRY_UA | sense_flags);
 
@@ -3742,4 +4208,39 @@ scsi_read_dvd_structure(struct ccb_scsiio *csio, u_int
                      sense_len,
                      sizeof(*scsi_cmd),
                      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);
 }

Modified: stable/12/sys/cam/scsi/scsi_cd.h
==============================================================================
--- stable/12/sys/cam/scsi/scsi_cd.h    Wed Sep 25 19:29:09 2019        
(r352713)
+++ stable/12/sys/cam/scsi/scsi_cd.h    Wed Sep 25 19:46:17 2019        
(r352714)
@@ -231,6 +231,12 @@ struct scsi_read_toc
        u_int8_t op_code;
        u_int8_t byte2;
        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 from_track;
        u_int8_t data_len[2];
@@ -870,6 +876,12 @@ void scsi_read_dvd_structure(struct ccb_scsiio *csio, 
                             u_int8_t agid, u_int8_t *data_ptr,
                             u_int32_t dxfer_len, u_int8_t sense_len,
                             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
 
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to