123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259 |
- /* $OpenBSD: cd.c,v 1.217 2015/06/07 19:13:27 krw Exp $ */
- /* $NetBSD: cd.c,v 1.100 1997/04/02 02:29:30 mycroft Exp $ */
- /*
- * Copyright (c) 1994, 1995, 1997 Charles M. Hannum. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Charles M. Hannum.
- * 4. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- * Originally written by Julian Elischer (julian@tfs.com)
- * for TRW Financial Systems for use under the MACH(2.5) operating system.
- *
- * TRW Financial Systems, in accordance with their agreement with Carnegie
- * Mellon University, makes this software available to CMU to distribute
- * or use in any manner that they see fit as long as this message is kept with
- * the software. For this reason TFS also grants any other persons or
- * organisations permission to use or modify this software.
- *
- * TFS supplies this software to be publicly redistributed
- * on the understanding that TFS is not responsible for the correct
- * functioning of this software in any circumstances.
- *
- * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
- */
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/systm.h>
- #include <sys/timeout.h>
- #include <sys/file.h>
- #include <sys/stat.h>
- #include <sys/ioctl.h>
- #include <sys/mtio.h>
- #include <sys/buf.h>
- #include <sys/uio.h>
- #include <sys/malloc.h>
- #include <sys/pool.h>
- #include <sys/errno.h>
- #include <sys/device.h>
- #include <sys/disklabel.h>
- #include <sys/disk.h>
- #include <sys/cdio.h>
- #include <sys/conf.h>
- #include <sys/scsiio.h>
- #include <sys/dkio.h>
- #include <sys/vnode.h>
- #include <scsi/scsi_all.h>
- #include <scsi/cd.h>
- #include <scsi/scsi_disk.h> /* rw_big and start_stop come from there */
- #include <scsi/scsiconf.h>
- #include <ufs/ffs/fs.h> /* for BBSIZE and SBSIZE */
- #define CDOUTSTANDING 4
- #define MAXTRACK 99
- #define CD_FRAMES 75
- #define CD_SECS 60
- struct cd_toc {
- struct ioc_toc_header header;
- struct cd_toc_entry entries[MAXTRACK+1]; /* One extra for the */
- /* leadout */
- };
- int cdmatch(struct device *, void *, void *);
- void cdattach(struct device *, struct device *, void *);
- int cdactivate(struct device *, int);
- int cddetach(struct device *, int);
- struct cd_softc {
- struct device sc_dev;
- struct disk sc_dk;
- int sc_flags;
- #define CDF_ANCIENT 0x10 /* disk is ancient; for minphys */
- #define CDF_DYING 0x40 /* dying, when deactivated */
- #define CDF_WAITING 0x100
- struct scsi_link *sc_link; /* contains our targ, lun, etc. */
- struct cd_parms {
- u_int32_t secsize;
- u_int64_t disksize; /* total number sectors */
- } params;
- struct bufq sc_bufq;
- struct scsi_xshandler sc_xsh;
- struct timeout sc_timeout;
- };
- void cdstart(struct scsi_xfer *);
- void cd_buf_done(struct scsi_xfer *);
- void cdminphys(struct buf *);
- int cdgetdisklabel(dev_t, struct cd_softc *, struct disklabel *, int);
- int cd_setchan(struct cd_softc *, int, int, int, int, int);
- int cd_getvol(struct cd_softc *cd, struct ioc_vol *, int);
- int cd_setvol(struct cd_softc *, const struct ioc_vol *, int);
- int cd_load_unload(struct cd_softc *, int, int);
- int cd_set_pa_immed(struct cd_softc *, int);
- int cd_play(struct cd_softc *, int, int);
- int cd_play_tracks(struct cd_softc *, int, int, int, int);
- int cd_play_msf(struct cd_softc *, int, int, int, int, int, int);
- int cd_pause(struct cd_softc *, int);
- int cd_reset(struct cd_softc *);
- int cd_read_subchannel(struct cd_softc *, int, int, int,
- struct cd_sub_channel_info *, int );
- int cd_read_toc(struct cd_softc *, int, int, void *, int, int);
- int cd_get_parms(struct cd_softc *, int);
- int cd_load_toc(struct cd_softc *, struct cd_toc *, int);
- int cd_interpret_sense(struct scsi_xfer *);
- u_int64_t cd_size(struct scsi_link *, int, u_int32_t *);
- int dvd_auth(struct cd_softc *, union dvd_authinfo *);
- int dvd_read_physical(struct cd_softc *, union dvd_struct *);
- int dvd_read_copyright(struct cd_softc *, union dvd_struct *);
- int dvd_read_disckey(struct cd_softc *, union dvd_struct *);
- int dvd_read_bca(struct cd_softc *, union dvd_struct *);
- int dvd_read_manufact(struct cd_softc *, union dvd_struct *);
- int dvd_read_struct(struct cd_softc *, union dvd_struct *);
- #if defined(__macppc__)
- int cd_eject(void);
- #endif
- struct cfattach cd_ca = {
- sizeof(struct cd_softc), cdmatch, cdattach,
- cddetach, cdactivate
- };
- struct cfdriver cd_cd = {
- NULL, "cd", DV_DISK
- };
- const struct scsi_inquiry_pattern cd_patterns[] = {
- {T_CDROM, T_REMOV,
- "", "", ""},
- {T_CDROM, T_FIXED,
- "", "", ""},
- {T_WORM, T_REMOV,
- "", "", ""},
- {T_WORM, T_FIXED,
- "", "", ""},
- {T_DIRECT, T_REMOV,
- "NEC CD-ROM DRIVE:260", "", ""},
- #if 0
- {T_CDROM, T_REMOV, /* more luns */
- "PIONEER ", "CD-ROM DRM-600 ", ""},
- #endif
- };
- #define cdlookup(unit) (struct cd_softc *)disk_lookup(&cd_cd, (unit))
- int
- cdmatch(struct device *parent, void *match, void *aux)
- {
- struct scsi_attach_args *sa = aux;
- int priority;
- scsi_inqmatch(sa->sa_inqbuf, cd_patterns, nitems(cd_patterns),
- sizeof(cd_patterns[0]), &priority);
- return (priority);
- }
- /*
- * The routine called by the low level scsi routine when it discovers
- * A device suitable for this driver
- */
- void
- cdattach(struct device *parent, struct device *self, void *aux)
- {
- struct cd_softc *sc = (struct cd_softc *)self;
- struct scsi_attach_args *sa = aux;
- struct scsi_link *sc_link = sa->sa_sc_link;
- SC_DEBUG(sc_link, SDEV_DB2, ("cdattach:\n"));
- /*
- * Store information needed to contact our base driver
- */
- sc->sc_link = sc_link;
- sc_link->interpret_sense = cd_interpret_sense;
- sc_link->device_softc = sc;
- if (sc_link->openings > CDOUTSTANDING)
- sc_link->openings = CDOUTSTANDING;
- /*
- * Initialize disk structures.
- */
- sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
- bufq_init(&sc->sc_bufq, BUFQ_DEFAULT);
- /*
- * Note if this device is ancient. This is used in cdminphys().
- */
- if (!(sc_link->flags & SDEV_ATAPI) &&
- SCSISPC(sa->sa_inqbuf->version) == 0)
- sc->sc_flags |= CDF_ANCIENT;
- printf("\n");
- scsi_xsh_set(&sc->sc_xsh, sc_link, cdstart);
- timeout_set(&sc->sc_timeout, (void (*)(void *))scsi_xsh_add,
- &sc->sc_xsh);
- /* Attach disk. */
- sc->sc_dk.dk_flags = DKF_NOLABELREAD;
- disk_attach(&sc->sc_dev, &sc->sc_dk);
- }
- int
- cdactivate(struct device *self, int act)
- {
- struct cd_softc *sc = (struct cd_softc *)self;
- int rv = 0;
- switch (act) {
- case DVACT_RESUME:
- /*
- * When resuming, hardware may have forgotten we locked it. So if
- * there are any open partitions, lock the CD.
- */
- if (sc->sc_dk.dk_openmask != 0)
- scsi_prevent(sc->sc_link, PR_PREVENT,
- SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE |
- SCSI_SILENT | SCSI_AUTOCONF);
- break;
- case DVACT_DEACTIVATE:
- sc->sc_flags |= CDF_DYING;
- scsi_xsh_del(&sc->sc_xsh);
- break;
- }
- return (rv);
- }
- int
- cddetach(struct device *self, int flags)
- {
- struct cd_softc *sc = (struct cd_softc *)self;
- bufq_drain(&sc->sc_bufq);
- disk_gone(cdopen, self->dv_unit);
- /* Detach disk. */
- bufq_destroy(&sc->sc_bufq);
- disk_detach(&sc->sc_dk);
- return (0);
- }
- /*
- * Open the device. Make sure the partition info is as up-to-date as can be.
- */
- int
- cdopen(dev_t dev, int flag, int fmt, struct proc *p)
- {
- struct scsi_link *sc_link;
- struct cd_softc *sc;
- int error = 0, part, rawopen, unit;
- unit = DISKUNIT(dev);
- part = DISKPART(dev);
- rawopen = (part == RAW_PART) && (fmt == S_IFCHR);
- sc = cdlookup(unit);
- if (sc == NULL)
- return (ENXIO);
- if (sc->sc_flags & CDF_DYING) {
- device_unref(&sc->sc_dev);
- return (ENXIO);
- }
- sc_link = sc->sc_link;
- SC_DEBUG(sc_link, SDEV_DB1,
- ("cdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit,
- cd_cd.cd_ndevs, part));
- if ((error = disk_lock(&sc->sc_dk)) != 0) {
- device_unref(&sc->sc_dev);
- return (error);
- }
- if (sc->sc_dk.dk_openmask != 0) {
- /*
- * If any partition is open, but the disk has been invalidated,
- * disallow further opens.
- */
- if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
- if (rawopen)
- goto out;
- error = EIO;
- goto bad;
- }
- } else {
- /*
- * Check that it is still responding and ok. Drive can be in
- * progress of loading media so use increased retries number
- * and don't ignore NOT_READY.
- */
- /* Use cd_interpret_sense() now. */
- sc_link->flags |= SDEV_OPEN;
- error = scsi_test_unit_ready(sc_link, TEST_READY_RETRIES,
- (rawopen ? SCSI_SILENT : 0) | SCSI_IGNORE_ILLEGAL_REQUEST |
- SCSI_IGNORE_MEDIA_CHANGE);
- /* Start the cd spinning if necessary. */
- if (error == EIO)
- error = scsi_start(sc_link, SSS_START,
- SCSI_IGNORE_ILLEGAL_REQUEST |
- SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT);
- if (error) {
- if (rawopen) {
- error = 0;
- goto out;
- } else
- goto bad;
- }
- /* Lock the cd in. */
- error = scsi_prevent(sc_link, PR_PREVENT,
- SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE |
- SCSI_SILENT);
- if (error)
- goto bad;
- /* Load the physical device parameters. */
- sc_link->flags |= SDEV_MEDIA_LOADED;
- if (cd_get_parms(sc, (rawopen ? SCSI_SILENT : 0) |
- SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE)) {
- sc_link->flags &= ~SDEV_MEDIA_LOADED;
- error = ENXIO;
- goto bad;
- }
- SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded\n"));
- /* Fabricate a disk label. */
- cdgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0);
- SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel fabricated\n"));
- }
- out:
- if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0)
- goto bad;
- sc_link->flags |= SDEV_OPEN;
- SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n"));
- /* It's OK to fall through because dk_openmask is now non-zero. */
- bad:
- if (sc->sc_dk.dk_openmask == 0) {
- scsi_prevent(sc_link, PR_ALLOW,
- SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE |
- SCSI_SILENT);
- sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);
- }
- disk_unlock(&sc->sc_dk);
- device_unref(&sc->sc_dev);
- return (error);
- }
- /*
- * Close the device. Only called if we are the last occurrence of an open
- * device.
- */
- int
- cdclose(dev_t dev, int flag, int fmt, struct proc *p)
- {
- struct cd_softc *sc;
- int part = DISKPART(dev);
- sc = cdlookup(DISKUNIT(dev));
- if (sc == NULL)
- return ENXIO;
- if (sc->sc_flags & CDF_DYING) {
- device_unref(&sc->sc_dev);
- return (ENXIO);
- }
- disk_lock_nointr(&sc->sc_dk);
- disk_closepart(&sc->sc_dk, part, fmt);
- if (sc->sc_dk.dk_openmask == 0) {
- /* XXXX Must wait for I/O to complete! */
- scsi_prevent(sc->sc_link, PR_ALLOW,
- SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY |
- SCSI_SILENT);
- sc->sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED);
- if (sc->sc_link->flags & SDEV_EJECTING) {
- scsi_start(sc->sc_link, SSS_STOP|SSS_LOEJ, 0);
- sc->sc_link->flags &= ~SDEV_EJECTING;
- }
- timeout_del(&sc->sc_timeout);
- scsi_xsh_del(&sc->sc_xsh);
- }
- disk_unlock(&sc->sc_dk);
- device_unref(&sc->sc_dev);
- return 0;
- }
- /*
- * Actually translate the requested transfer into one the physical driver can
- * understand. The transfer is described by a buf and will include only one
- * physical transfer.
- */
- void
- cdstrategy(struct buf *bp)
- {
- struct cd_softc *sc;
- int s;
- sc = cdlookup(DISKUNIT(bp->b_dev));
- if (sc == NULL) {
- bp->b_error = ENXIO;
- goto bad;
- }
- if (sc->sc_flags & CDF_DYING) {
- bp->b_error = ENXIO;
- goto bad;
- }
- SC_DEBUG(sc->sc_link, SDEV_DB2, ("cdstrategy: %ld bytes @ blk %lld\n",
- bp->b_bcount, (long long)bp->b_blkno));
- /*
- * If the device has been made invalid, error out
- * maybe the media changed, or no media loaded
- */
- if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
- bp->b_error = EIO;
- goto bad;
- }
- /* Validate the request. */
- if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
- goto done;
- /* Place it in the queue of disk activities for this disk. */
- bufq_queue(&sc->sc_bufq, bp);
- /*
- * Tell the device to get going on the transfer if it's
- * not doing anything, otherwise just wait for completion
- */
- scsi_xsh_add(&sc->sc_xsh);
- device_unref(&sc->sc_dev);
- return;
- bad:
- bp->b_flags |= B_ERROR;
- bp->b_resid = bp->b_bcount;
- done:
- s = splbio();
- biodone(bp);
- splx(s);
- if (sc != NULL)
- device_unref(&sc->sc_dev);
- }
- /*
- * 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 and scsi_done
- */
- void
- cdstart(struct scsi_xfer *xs)
- {
- struct scsi_link *sc_link = xs->sc_link;
- struct cd_softc *sc = sc_link->device_softc;
- struct buf *bp;
- struct scsi_rw_big *cmd_big;
- struct scsi_rw *cmd_small;
- u_int64_t secno, nsecs;
- struct partition *p;
- int read;
- SC_DEBUG(sc_link, SDEV_DB2, ("cdstart\n"));
- if (sc->sc_flags & CDF_DYING) {
- scsi_xs_put(xs);
- return;
- }
- /*
- * 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) {
- bufq_drain(&sc->sc_bufq);
- scsi_xs_put(xs);
- return;
- }
- bp = bufq_dequeue(&sc->sc_bufq);
- if (bp == NULL) {
- scsi_xs_put(xs);
- return;
- }
- /*
- * 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.
- */
- secno = DL_BLKTOSEC(sc->sc_dk.dk_label, bp->b_blkno);
- p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
- secno += DL_GETPOFFSET(p);
- nsecs = howmany(bp->b_bcount, sc->sc_dk.dk_label->d_secsize);
- read = (bp->b_flags & B_READ);
- /*
- * 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) &&
- ((secno & 0x1fffff) == secno) &&
- ((nsecs & 0xff) == nsecs)) {
- /*
- * We can fit in a small cdb.
- */
- cmd_small = (struct scsi_rw *)xs->cmd;
- cmd_small->opcode = read ?
- READ_COMMAND : WRITE_COMMAND;
- _lto3b(secno, cmd_small->addr);
- cmd_small->length = nsecs & 0xff;
- xs->cmdlen = sizeof(*cmd_small);
- } else {
- /*
- * Need a large cdb.
- */
- cmd_big = (struct scsi_rw_big *)xs->cmd;
- cmd_big->opcode = read ?
- READ_BIG : WRITE_BIG;
- _lto4b(secno, cmd_big->addr);
- _lto2b(nsecs, cmd_big->length);
- xs->cmdlen = sizeof(*cmd_big);
- }
- xs->flags |= (read ? SCSI_DATA_IN : SCSI_DATA_OUT);
- xs->timeout = 30000;
- xs->data = bp->b_data;
- xs->datalen = bp->b_bcount;
- xs->done = cd_buf_done;
- xs->cookie = bp;
- xs->bp = bp;
- /* Instrumentation. */
- disk_busy(&sc->sc_dk);
- scsi_xs_exec(xs);
- if (ISSET(sc->sc_flags, CDF_WAITING))
- CLR(sc->sc_flags, CDF_WAITING);
- else if (bufq_peek(&sc->sc_bufq))
- scsi_xsh_add(&sc->sc_xsh);
- }
- void
- cd_buf_done(struct scsi_xfer *xs)
- {
- struct cd_softc *sc = xs->sc_link->device_softc;
- struct buf *bp = xs->cookie;
- int error, s;
- switch (xs->error) {
- case XS_NOERROR:
- bp->b_error = 0;
- bp->b_resid = xs->resid;
- break;
- case XS_NO_CCB:
- /* The adapter is busy, requeue the buf and try it later. */
- disk_unbusy(&sc->sc_dk, bp->b_bcount - xs->resid,
- bp->b_flags & B_READ);
- bufq_requeue(&sc->sc_bufq, bp);
- scsi_xs_put(xs);
- SET(sc->sc_flags, CDF_WAITING);
- timeout_add(&sc->sc_timeout, 1);
- return;
- case XS_SENSE:
- case XS_SHORTSENSE:
- #ifdef SCSIDEBUG
- scsi_sense_print_debug(xs);
- #endif
- error = cd_interpret_sense(xs);
- if (error == 0) {
- bp->b_error = 0;
- bp->b_resid = xs->resid;
- break;
- }
- if (error != ERESTART)
- xs->retries = 0;
- goto retry;
- case XS_BUSY:
- if (xs->retries) {
- if (scsi_delay(xs, 1) != ERESTART)
- xs->retries = 0;
- }
- goto retry;
- case XS_TIMEOUT:
- retry:
- if (xs->retries--) {
- scsi_xs_exec(xs);
- return;
- }
- /* FALLTHROUGH */
- default:
- bp->b_error = EIO;
- bp->b_flags |= B_ERROR;
- bp->b_resid = bp->b_bcount;
- break;
- }
- disk_unbusy(&sc->sc_dk, bp->b_bcount - xs->resid,
- bp->b_flags & B_READ);
- s = splbio();
- biodone(bp);
- splx(s);
- scsi_xs_put(xs);
- }
- void
- cdminphys(struct buf *bp)
- {
- struct cd_softc *sc;
- long max;
- sc = cdlookup(DISKUNIT(bp->b_dev));
- if (sc == NULL)
- return;
- /*
- * If the device is ancient, we want to make sure that
- * the transfer fits into a 6-byte cdb.
- *
- * XXX Note that the SCSI-I spec says that 256-block transfers
- * are allowed in a 6-byte read/write, and are specified
- * by setting the "length" to 0. However, we're conservative
- * here, allowing only 255-block transfers in case an
- * ancient device gets confused by length == 0. A length of 0
- * in a 10-byte read/write actually means 0 blocks.
- */
- if (sc->sc_flags & CDF_ANCIENT) {
- max = sc->sc_dk.dk_label->d_secsize * 0xff;
- if (bp->b_bcount > max)
- bp->b_bcount = max;
- }
- (*sc->sc_link->adapter->scsi_minphys)(bp, sc->sc_link);
- device_unref(&sc->sc_dev);
- }
- int
- cdread(dev_t dev, struct uio *uio, int ioflag)
- {
- return (physio(cdstrategy, dev, B_READ, cdminphys, uio));
- }
- int
- cdwrite(dev_t dev, struct uio *uio, int ioflag)
- {
- return (physio(cdstrategy, dev, B_WRITE, cdminphys, uio));
- }
- /*
- * Perform special action on behalf of the user.
- * Knows about the internals of this device
- */
- int
- cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
- {
- struct cd_softc *sc;
- struct disklabel *lp;
- int part = DISKPART(dev);
- int error = 0;
- sc = cdlookup(DISKUNIT(dev));
- if (sc == NULL)
- return ENXIO;
- if (sc->sc_flags & CDF_DYING) {
- device_unref(&sc->sc_dev);
- return (ENXIO);
- }
- SC_DEBUG(sc->sc_link, SDEV_DB2, ("cdioctl 0x%lx\n", cmd));
- /*
- * If the device is not valid.. abandon ship
- */
- if ((sc->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
- switch (cmd) {
- case DIOCLOCK:
- case DIOCEJECT:
- case SCIOCIDENTIFY:
- case SCIOCCOMMAND:
- case SCIOCDEBUG:
- case CDIOCLOADUNLOAD:
- case SCIOCRESET:
- case CDIOCGETVOL:
- case CDIOCSETVOL:
- case CDIOCSETMONO:
- case CDIOCSETSTEREO:
- case CDIOCSETMUTE:
- case CDIOCSETLEFT:
- case CDIOCSETRIGHT:
- case CDIOCCLOSE:
- case CDIOCEJECT:
- case CDIOCALLOW:
- case CDIOCPREVENT:
- case CDIOCSETDEBUG:
- case CDIOCCLRDEBUG:
- case CDIOCRESET:
- case DVD_AUTH:
- case DVD_READ_STRUCT:
- case MTIOCTOP:
- if (part == RAW_PART)
- break;
- /* FALLTHROUGH */
- default:
- if ((sc->sc_link->flags & SDEV_OPEN) == 0)
- error = ENODEV;
- else
- error = EIO;
- goto exit;
- }
- }
- switch (cmd) {
- case DIOCRLDINFO:
- lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
- cdgetdisklabel(dev, sc, lp, 0);
- memcpy(sc->sc_dk.dk_label, lp, sizeof(*lp));
- free(lp, M_TEMP, sizeof(*lp));
- break;
- case DIOCGPDINFO:
- cdgetdisklabel(dev, sc, (struct disklabel *)addr, 1);
- break;
- case DIOCGDINFO:
- *(struct disklabel *)addr = *(sc->sc_dk.dk_label);
- break;
- case DIOCGPART:
- ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
- ((struct partinfo *)addr)->part =
- &sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
- break;
- case DIOCWDINFO:
- case DIOCSDINFO:
- if ((flag & FWRITE) == 0) {
- error = EBADF;
- break;
- }
- if ((error = disk_lock(&sc->sc_dk)) != 0)
- break;
- error = setdisklabel(sc->sc_dk.dk_label,
- (struct disklabel *)addr, sc->sc_dk.dk_openmask);
- if (error == 0) {
- }
- disk_unlock(&sc->sc_dk);
- break;
- case CDIOCPLAYTRACKS: {
- struct ioc_play_track *args = (struct ioc_play_track *)addr;
- if ((error = cd_set_pa_immed(sc, 0)) != 0)
- break;
- error = cd_play_tracks(sc, args->start_track,
- args->start_index, args->end_track, args->end_index);
- break;
- }
- case CDIOCPLAYMSF: {
- struct ioc_play_msf *args = (struct ioc_play_msf *)addr;
- if ((error = cd_set_pa_immed(sc, 0)) != 0)
- break;
- error = cd_play_msf(sc, args->start_m, args->start_s,
- args->start_f, args->end_m, args->end_s, args->end_f);
- break;
- }
- case CDIOCPLAYBLOCKS: {
- struct ioc_play_blocks *args = (struct ioc_play_blocks *)addr;
- if ((error = cd_set_pa_immed(sc, 0)) != 0)
- break;
- error = cd_play(sc, args->blk, args->len);
- break;
- }
- case CDIOCREADSUBCHANNEL: {
- struct ioc_read_subchannel *args =
- (struct ioc_read_subchannel *)addr;
- struct cd_sub_channel_info *data;
- int len = args->data_len;
- if (len > sizeof(*data) ||
- len < sizeof(struct cd_sub_channel_header)) {
- error = EINVAL;
- break;
- }
- data = dma_alloc(sizeof(*data), PR_WAITOK);
- error = cd_read_subchannel(sc, args->address_format,
- args->data_format, args->track, data, len);
- if (error) {
- dma_free(data, sizeof(*data));
- break;
- }
- len = min(len, _2btol(data->header.data_len) +
- sizeof(struct cd_sub_channel_header));
- error = copyout(data, args->data, len);
- dma_free(data, sizeof(*data));
- break;
- }
- case CDIOREADTOCHEADER: {
- struct ioc_toc_header *th;
- th = dma_alloc(sizeof(*th), PR_WAITOK);
- if ((error = cd_read_toc(sc, 0, 0, th, sizeof(*th), 0)) != 0) {
- dma_free(th, sizeof(*th));
- break;
- }
- if (sc->sc_link->quirks & ADEV_LITTLETOC)
- th->len = letoh16(th->len);
- else
- th->len = betoh16(th->len);
- if (th->len > 0)
- memcpy(addr, th, sizeof(*th));
- else
- error = EIO;
- dma_free(th, sizeof(*th));
- break;
- }
- case CDIOREADTOCENTRYS: {
- struct cd_toc *toc;
- struct ioc_read_toc_entry *te =
- (struct ioc_read_toc_entry *)addr;
- struct ioc_toc_header *th;
- struct cd_toc_entry *cte;
- int len = te->data_len;
- int ntracks;
- toc = dma_alloc(sizeof(*toc), PR_WAITOK | PR_ZERO);
- th = &toc->header;
- if (len > sizeof(toc->entries) ||
- len < sizeof(struct cd_toc_entry)) {
- dma_free(toc, sizeof(*toc));
- error = EINVAL;
- break;
- }
- error = cd_read_toc(sc, te->address_format, te->starting_track,
- toc, len + sizeof(struct ioc_toc_header), 0);
- if (error) {
- dma_free(toc, sizeof(*toc));
- break;
- }
- if (te->address_format == CD_LBA_FORMAT)
- for (ntracks =
- th->ending_track - th->starting_track + 1;
- ntracks >= 0; ntracks--) {
- cte = &toc->entries[ntracks];
- cte->addr_type = CD_LBA_FORMAT;
- if (sc->sc_link->quirks & ADEV_LITTLETOC) {
- #if BYTE_ORDER == BIG_ENDIAN
- swap16_multi((u_int16_t *)&cte->addr,
- sizeof(cte->addr) / 2);
- #endif
- } else
- cte->addr.lba = betoh32(cte->addr.lba);
- }
- if (sc->sc_link->quirks & ADEV_LITTLETOC) {
- th->len = letoh16(th->len);
- } else
- th->len = betoh16(th->len);
- len = min(len, th->len - (sizeof(th->starting_track) +
- sizeof(th->ending_track)));
- error = copyout(toc->entries, te->data, len);
- dma_free(toc, sizeof(*toc));
- break;
- }
- case CDIOREADMSADDR: {
- struct cd_toc *toc;
- int sessno = *(int *)addr;
- struct cd_toc_entry *cte;
- if (sessno != 0) {
- error = EINVAL;
- break;
- }
- toc = dma_alloc(sizeof(*toc), PR_WAITOK | PR_ZERO);
- error = cd_read_toc(sc, 0, 0, toc,
- sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry),
- 0x40 /* control word for "get MS info" */);
- if (error) {
- dma_free(toc, sizeof(*toc));
- break;
- }
- cte = &toc->entries[0];
- if (sc->sc_link->quirks & ADEV_LITTLETOC) {
- #if BYTE_ORDER == BIG_ENDIAN
- swap16_multi((u_int16_t *)&cte->addr,
- sizeof(cte->addr) / 2);
- #endif
- } else
- cte->addr.lba = betoh32(cte->addr.lba);
- if (sc->sc_link->quirks & ADEV_LITTLETOC)
- toc->header.len = letoh16(toc->header.len);
- else
- toc->header.len = betoh16(toc->header.len);
- *(int *)addr = (toc->header.len >= 10 && cte->track > 1) ?
- cte->addr.lba : 0;
- dma_free(toc, sizeof(*toc));
- break;
- }
- case CDIOCSETPATCH: {
- struct ioc_patch *arg = (struct ioc_patch *)addr;
- error = cd_setchan(sc, arg->patch[0], arg->patch[1],
- arg->patch[2], arg->patch[3], 0);
- break;
- }
- case CDIOCGETVOL: {
- struct ioc_vol *arg = (struct ioc_vol *)addr;
- error = cd_getvol(sc, arg, 0);
- break;
- }
- case CDIOCSETVOL: {
- struct ioc_vol *arg = (struct ioc_vol *)addr;
- error = cd_setvol(sc, arg, 0);
- break;
- }
- case CDIOCSETMONO:
- error = cd_setchan(sc, BOTH_CHANNEL, BOTH_CHANNEL, MUTE_CHANNEL,
- MUTE_CHANNEL, 0);
- break;
- case CDIOCSETSTEREO:
- error = cd_setchan(sc, LEFT_CHANNEL, RIGHT_CHANNEL,
- MUTE_CHANNEL, MUTE_CHANNEL, 0);
- break;
- case CDIOCSETMUTE:
- error = cd_setchan(sc, MUTE_CHANNEL, MUTE_CHANNEL, MUTE_CHANNEL,
- MUTE_CHANNEL, 0);
- break;
- case CDIOCSETLEFT:
- error = cd_setchan(sc, LEFT_CHANNEL, LEFT_CHANNEL, MUTE_CHANNEL,
- MUTE_CHANNEL, 0);
- break;
- case CDIOCSETRIGHT:
- error = cd_setchan(sc, RIGHT_CHANNEL, RIGHT_CHANNEL,
- MUTE_CHANNEL, MUTE_CHANNEL, 0);
- break;
- case CDIOCRESUME:
- error = cd_pause(sc, 1);
- break;
- case CDIOCPAUSE:
- error = cd_pause(sc, 0);
- break;
- case CDIOCSTART:
- error = scsi_start(sc->sc_link, SSS_START, 0);
- break;
- case CDIOCSTOP:
- error = scsi_start(sc->sc_link, SSS_STOP, 0);
- break;
- close_tray:
- case CDIOCCLOSE:
- error = scsi_start(sc->sc_link, SSS_START|SSS_LOEJ,
- SCSI_IGNORE_NOT_READY | SCSI_IGNORE_MEDIA_CHANGE);
- break;
- case MTIOCTOP:
- if (((struct mtop *)addr)->mt_op == MTRETEN)
- goto close_tray;
- if (((struct mtop *)addr)->mt_op != MTOFFL) {
- error = EIO;
- break;
- }
- /* FALLTHROUGH */
- case CDIOCEJECT: /* FALLTHROUGH */
- case DIOCEJECT:
- sc->sc_link->flags |= SDEV_EJECTING;
- break;
- case CDIOCALLOW:
- error = scsi_prevent(sc->sc_link, PR_ALLOW, 0);
- break;
- case CDIOCPREVENT:
- error = scsi_prevent(sc->sc_link, PR_PREVENT, 0);
- break;
- case DIOCLOCK:
- error = scsi_prevent(sc->sc_link,
- (*(int *)addr) ? PR_PREVENT : PR_ALLOW, 0);
- break;
- case CDIOCSETDEBUG:
- sc->sc_link->flags |= (SDEV_DB1 | SDEV_DB2);
- break;
- case CDIOCCLRDEBUG:
- sc->sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2);
- break;
- case CDIOCRESET:
- case SCIOCRESET:
- error = cd_reset(sc);
- break;
- case CDIOCLOADUNLOAD: {
- struct ioc_load_unload *args = (struct ioc_load_unload *)addr;
- error = cd_load_unload(sc, args->options, args->slot);
- break;
- }
- case DVD_AUTH:
- error = dvd_auth(sc, (union dvd_authinfo *)addr);
- break;
- case DVD_READ_STRUCT:
- error = dvd_read_struct(sc, (union dvd_struct *)addr);
- break;
- default:
- if (DISKPART(dev) != RAW_PART) {
- error = ENOTTY;
- break;
- }
- error = scsi_do_ioctl(sc->sc_link, cmd, addr, flag);
- break;
- }
- exit:
- device_unref(&sc->sc_dev);
- return (error);
- }
- /*
- * Load the label information on the named device
- * Actually fabricate a disklabel
- *
- * EVENTUALLY take information about different
- * data tracks from the TOC and put it in the disklabel
- */
- int
- cdgetdisklabel(dev_t dev, struct cd_softc *sc, struct disklabel *lp,
- int spoofonly)
- {
- struct cd_toc *toc;
- int tocidx, n, audioonly = 1;
- bzero(lp, sizeof(struct disklabel));
- lp->d_secsize = sc->params.secsize;
- lp->d_ntracks = 1;
- lp->d_nsectors = 100;
- lp->d_secpercyl = 100;
- lp->d_ncylinders = (sc->params.disksize / 100) + 1;
- if (sc->sc_link->flags & SDEV_ATAPI) {
- strncpy(lp->d_typename, "ATAPI CD-ROM", sizeof(lp->d_typename));
- lp->d_type = DTYPE_ATAPI;
- } else {
- strncpy(lp->d_typename, "SCSI CD-ROM", sizeof(lp->d_typename));
- lp->d_type = DTYPE_SCSI;
- }
- strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
- DL_SETDSIZE(lp, sc->params.disksize);
- lp->d_version = 1;
- /* XXX - these values for BBSIZE and SBSIZE assume ffs */
- lp->d_bbsize = BBSIZE;
- lp->d_sbsize = SBSIZE;
- lp->d_magic = DISKMAGIC;
- lp->d_magic2 = DISKMAGIC;
- lp->d_checksum = dkcksum(lp);
- toc = dma_alloc(sizeof(*toc), PR_WAITOK | PR_ZERO);
- if (cd_load_toc(sc, toc, CD_LBA_FORMAT)) {
- audioonly = 0; /* No valid TOC found == not an audio CD. */
- goto done;
- }
- n = toc->header.ending_track - toc->header.starting_track + 1;
- for (tocidx = 0; tocidx < n; tocidx++)
- if (toc->entries[tocidx].control & 4) {
- audioonly = 0; /* Found a non-audio track. */
- goto done;
- }
- done:
- dma_free(toc, sizeof(*toc));
- if (audioonly)
- return (0);
- return readdisklabel(DISKLABELDEV(dev), cdstrategy, lp, spoofonly);
- }
- int
- cd_setchan(struct cd_softc *sc, int p0, int p1, int p2, int p3, int flags)
- {
- union scsi_mode_sense_buf *data;
- struct cd_audio_page *audio = NULL;
- int error, big;
- data = dma_alloc(sizeof(*data), PR_NOWAIT);
- if (data == NULL)
- return (ENOMEM);
- error = scsi_do_mode_sense(sc->sc_link, AUDIO_PAGE, data,
- (void **)&audio, NULL, NULL, NULL, sizeof(*audio), flags, &big);
- if (error == 0 && audio == NULL)
- error = EIO;
- if (error == 0) {
- audio->port[LEFT_PORT].channels = p0;
- audio->port[RIGHT_PORT].channels = p1;
- audio->port[2].channels = p2;
- audio->port[3].channels = p3;
- if (big)
- error = scsi_mode_select_big(sc->sc_link, SMS_PF,
- &data->hdr_big, flags, 20000);
- else
- error = scsi_mode_select(sc->sc_link, SMS_PF,
- &data->hdr, flags, 20000);
- }
- dma_free(data, sizeof(*data));
- return (error);
- }
- int
- cd_getvol(struct cd_softc *sc, struct ioc_vol *arg, int flags)
- {
- union scsi_mode_sense_buf *data;
- struct cd_audio_page *audio = NULL;
- int error;
- data = dma_alloc(sizeof(*data), PR_NOWAIT);
- if (data == NULL)
- return (ENOMEM);
- error = scsi_do_mode_sense(sc->sc_link, AUDIO_PAGE, data,
- (void **)&audio, NULL, NULL, NULL, sizeof(*audio), flags, NULL);
- if (error == 0 && audio == NULL)
- error = EIO;
- if (error == 0) {
- arg->vol[0] = audio->port[0].volume;
- arg->vol[1] = audio->port[1].volume;
- arg->vol[2] = audio->port[2].volume;
- arg->vol[3] = audio->port[3].volume;
- }
- dma_free(data, sizeof(*data));
- return (0);
- }
- int
- cd_setvol(struct cd_softc *sc, const struct ioc_vol *arg, int flags)
- {
- union scsi_mode_sense_buf *data;
- struct cd_audio_page *audio = NULL;
- u_int8_t mask_volume[4];
- int error, big;
- data = dma_alloc(sizeof(*data), PR_NOWAIT);
- if (data == NULL)
- return (ENOMEM);
- error = scsi_do_mode_sense(sc->sc_link,
- AUDIO_PAGE | SMS_PAGE_CTRL_CHANGEABLE, data, (void **)&audio, NULL,
- NULL, NULL, sizeof(*audio), flags, NULL);
- if (error == 0 && audio == NULL)
- error = EIO;
- if (error != 0) {
- dma_free(data, sizeof(*data));
- return (error);
- }
- mask_volume[0] = audio->port[0].volume;
- mask_volume[1] = audio->port[1].volume;
- mask_volume[2] = audio->port[2].volume;
- mask_volume[3] = audio->port[3].volume;
- error = scsi_do_mode_sense(sc->sc_link, AUDIO_PAGE, data,
- (void **)&audio, NULL, NULL, NULL, sizeof(*audio), flags, &big);
- if (error == 0 && audio == NULL)
- error = EIO;
- if (error != 0) {
- dma_free(data, sizeof(*data));
- return (error);
- }
- audio->port[0].volume = arg->vol[0] & mask_volume[0];
- audio->port[1].volume = arg->vol[1] & mask_volume[1];
- audio->port[2].volume = arg->vol[2] & mask_volume[2];
- audio->port[3].volume = arg->vol[3] & mask_volume[3];
- if (big)
- error = scsi_mode_select_big(sc->sc_link, SMS_PF,
- &data->hdr_big, flags, 20000);
- else
- error = scsi_mode_select(sc->sc_link, SMS_PF,
- &data->hdr, flags, 20000);
- dma_free(data, sizeof(*data));
- return (error);
- }
- int
- cd_load_unload(struct cd_softc *sc, int options, int slot)
- {
- struct scsi_load_unload *cmd;
- struct scsi_xfer *xs;
- int error;
- xs = scsi_xs_get(sc->sc_link, 0);
- if (xs == NULL)
- return (ENOMEM);
- xs->cmdlen = sizeof(*cmd);
- xs->timeout = 200000;
- cmd = (struct scsi_load_unload *)xs->cmd;
- cmd->opcode = LOAD_UNLOAD;
- cmd->options = options; /* ioctl uses ATAPI values */
- cmd->slot = slot;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- return (error);
- }
- int
- cd_set_pa_immed(struct cd_softc *sc, int flags)
- {
- union scsi_mode_sense_buf *data;
- struct cd_audio_page *audio = NULL;
- int error, oflags, big;
- if (sc->sc_link->flags & SDEV_ATAPI)
- /* XXX Noop? */
- return (0);
- data = dma_alloc(sizeof(*data), PR_NOWAIT);
- if (data == NULL)
- return (ENOMEM);
- error = scsi_do_mode_sense(sc->sc_link, AUDIO_PAGE, data,
- (void **)&audio, NULL, NULL, NULL, sizeof(*audio), flags, &big);
- if (error == 0 && audio == NULL)
- error = EIO;
- if (error == 0) {
- oflags = audio->flags;
- audio->flags &= ~CD_PA_SOTC;
- audio->flags |= CD_PA_IMMED;
- if (audio->flags != oflags) {
- if (big)
- error = scsi_mode_select_big(sc->sc_link,
- SMS_PF, &data->hdr_big, flags, 20000);
- else
- error = scsi_mode_select(sc->sc_link, SMS_PF,
- &data->hdr, flags, 20000);
- }
- }
- dma_free(data, sizeof(*data));
- return (error);
- }
- /*
- * Get scsi driver to send a "start playing" command
- */
- int
- cd_play(struct cd_softc *sc, int secno, int nsecs)
- {
- struct scsi_play *cmd;
- struct scsi_xfer *xs;
- int error;
- xs = scsi_xs_get(sc->sc_link, 0);
- if (xs == NULL)
- return (ENOMEM);
- xs->cmdlen = sizeof(*cmd);
- xs->timeout = 200000;
- cmd = (struct scsi_play *)xs->cmd;
- cmd->opcode = PLAY;
- _lto4b(secno, cmd->blk_addr);
- _lto2b(nsecs, cmd->xfer_len);
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- return (error);
- }
- /*
- * Get scsi driver to send a "start playing" command
- */
- int
- cd_play_tracks(struct cd_softc *sc, int strack, int sindex, int etrack,
- int eindex)
- {
- struct cd_toc *toc;
- u_char endf, ends, endm;
- int error;
- if (!etrack)
- return (EIO);
- if (strack > etrack)
- return (EINVAL);
- toc = dma_alloc(sizeof(*toc), PR_WAITOK | PR_ZERO);
- if ((error = cd_load_toc(sc, toc, CD_MSF_FORMAT)) != 0)
- goto done;
- if (++etrack > (toc->header.ending_track+1))
- etrack = toc->header.ending_track+1;
- strack -= toc->header.starting_track;
- etrack -= toc->header.starting_track;
- if (strack < 0) {
- error = EINVAL;
- goto done;
- }
- /*
- * The track ends one frame before the next begins. The last track
- * is taken care of by the leadoff track.
- */
- endm = toc->entries[etrack].addr.msf.minute;
- ends = toc->entries[etrack].addr.msf.second;
- endf = toc->entries[etrack].addr.msf.frame;
- if (endf-- == 0) {
- endf = CD_FRAMES - 1;
- if (ends-- == 0) {
- ends = CD_SECS - 1;
- if (endm-- == 0) {
- error = EINVAL;
- goto done;
- }
- }
- }
- error = cd_play_msf(sc, toc->entries[strack].addr.msf.minute,
- toc->entries[strack].addr.msf.second,
- toc->entries[strack].addr.msf.frame,
- endm, ends, endf);
- done:
- dma_free(toc, sizeof(*toc));
- return (error);
- }
- /*
- * Get scsi driver to send a "play msf" command
- */
- int
- cd_play_msf(struct cd_softc *sc, int startm, int starts, int startf, int endm,
- int ends, int endf)
- {
- struct scsi_play_msf *cmd;
- struct scsi_xfer *xs;
- int error;
- xs = scsi_xs_get(sc->sc_link, 0);
- if (xs == NULL)
- return (ENOMEM);
- xs->cmdlen = sizeof(*cmd);
- xs->timeout = 20000;
- cmd = (struct scsi_play_msf *)xs->cmd;
- cmd->opcode = PLAY_MSF;
- cmd->start_m = startm;
- cmd->start_s = starts;
- cmd->start_f = startf;
- cmd->end_m = endm;
- cmd->end_s = ends;
- cmd->end_f = endf;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- return (error);
- }
- /*
- * Get scsi driver to send a "start up" command
- */
- int
- cd_pause(struct cd_softc *sc, int go)
- {
- struct scsi_pause *cmd;
- struct scsi_xfer *xs;
- int error;
- xs = scsi_xs_get(sc->sc_link, 0);
- if (xs == NULL)
- return (ENOMEM);
- xs->cmdlen = sizeof(*cmd);
- xs->timeout = 2000;
- cmd = (struct scsi_pause *)xs->cmd;
- cmd->opcode = PAUSE;
- cmd->resume = go;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- return (error);
- }
- /*
- * Get scsi driver to send a "RESET" command
- */
- int
- cd_reset(struct cd_softc *sc)
- {
- struct scsi_xfer *xs;
- int error;
- xs = scsi_xs_get(sc->sc_link, SCSI_RESET);
- if (xs == NULL)
- return (ENOMEM);
- xs->timeout = 2000;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- return (error);
- }
- /*
- * Read subchannel
- */
- int
- cd_read_subchannel(struct cd_softc *sc, int mode, int format, int track,
- struct cd_sub_channel_info *data, int len)
- {
- struct scsi_read_subchannel *cmd;
- struct scsi_xfer *xs;
- int error;
- xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN | SCSI_SILENT);
- if (xs == NULL)
- return (ENOMEM);
- xs->cmdlen = sizeof(*cmd);
- xs->data = (void *)data;
- xs->datalen = len;
- xs->timeout = 5000;
- cmd = (struct scsi_read_subchannel *)xs->cmd;
- cmd->opcode = READ_SUBCHANNEL;
- if (mode == CD_MSF_FORMAT)
- cmd->byte2 |= CD_MSF;
- cmd->byte3 = SRS_SUBQ;
- cmd->subchan_format = format;
- cmd->track = track;
- _lto2b(len, cmd->data_len);
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- return (error);
- }
- /*
- * Read table of contents
- */
- int
- cd_read_toc(struct cd_softc *sc, int mode, int start, void *data, int len,
- int control)
- {
- struct scsi_read_toc *cmd;
- struct scsi_xfer *xs;
- int error;
- xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN |
- SCSI_IGNORE_ILLEGAL_REQUEST);
- if (xs == NULL)
- return (ENOMEM);
- xs->cmdlen = sizeof(*cmd);
- xs->data = data;
- xs->datalen = len;
- xs->timeout = 5000;
- bzero(data, len);
- cmd = (struct scsi_read_toc *)xs->cmd;
- cmd->opcode = READ_TOC;
- if (mode == CD_MSF_FORMAT)
- cmd->byte2 |= CD_MSF;
- cmd->from_track = start;
- _lto2b(len, cmd->data_len);
- cmd->control = control;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- return (error);
- }
- int
- cd_load_toc(struct cd_softc *sc, struct cd_toc *toc, int fmt)
- {
- int n, len, error;
- error = cd_read_toc(sc, 0, 0, toc, sizeof(toc->header), 0);
- if (error == 0) {
- if (toc->header.ending_track < toc->header.starting_track)
- return (EIO);
- /* +2 to account for leading out track. */
- n = toc->header.ending_track - toc->header.starting_track + 2;
- len = n * sizeof(struct cd_toc_entry) + sizeof(toc->header);
- error = cd_read_toc(sc, fmt, 0, toc, len, 0);
- }
- return (error);
- }
- /*
- * Get the scsi driver to send a full inquiry to the device and use the
- * results to fill out the disk parameter structure.
- */
- int
- cd_get_parms(struct cd_softc *sc, int flags)
- {
- /* Reasonable defaults for drives that don't support READ_CAPACITY */
- sc->params.secsize = 2048;
- sc->params.disksize = 400000;
- if (sc->sc_link->quirks & ADEV_NOCAPACITY)
- return (0);
- sc->params.disksize = cd_size(sc->sc_link, flags, &sc->params.secsize);
- if ((sc->params.secsize < 512) ||
- ((sc->params.secsize & 511) != 0))
- sc->params.secsize = 2048; /* some drives lie ! */
- if (sc->params.disksize < 100)
- sc->params.disksize = 400000;
- return (0);
- }
- daddr_t
- cdsize(dev_t dev)
- {
- /* CD-ROMs are read-only. */
- return -1;
- }
- int
- cddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
- {
- /* Not implemented. */
- return ENXIO;
- }
- #define dvd_copy_key(dst, src) memcpy((dst), (src), DVD_KEY_SIZE)
- #define dvd_copy_challenge(dst, src) memcpy((dst), (src), DVD_CHALLENGE_SIZE)
- #define DVD_AUTH_BUFSIZE 20
- int
- dvd_auth(struct cd_softc *sc, union dvd_authinfo *a)
- {
- struct scsi_generic *cmd;
- struct scsi_xfer *xs;
- u_int8_t *buf;
- int error;
- buf = dma_alloc(DVD_AUTH_BUFSIZE, PR_WAITOK | PR_ZERO);
- if (buf == NULL)
- return (ENOMEM);
- xs = scsi_xs_get(sc->sc_link, 0);
- if (xs == NULL) {
- error = ENOMEM;
- goto done;
- }
- xs->cmdlen = sizeof(*cmd);
- xs->timeout = 30000;
- xs->data = buf;
- cmd = xs->cmd;
- switch (a->type) {
- case DVD_LU_SEND_AGID:
- cmd->opcode = GPCMD_REPORT_KEY;
- cmd->bytes[8] = 8;
- cmd->bytes[9] = 0 | (0 << 6);
- xs->datalen = 8;
- xs->flags |= SCSI_DATA_IN;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0)
- a->lsa.agid = buf[7] >> 6;
- break;
- case DVD_LU_SEND_CHALLENGE:
- cmd->opcode = GPCMD_REPORT_KEY;
- cmd->bytes[8] = 16;
- cmd->bytes[9] = 1 | (a->lsc.agid << 6);
- xs->datalen = 16;
- xs->flags |= SCSI_DATA_IN;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0)
- dvd_copy_challenge(a->lsc.chal, &buf[4]);
- break;
- case DVD_LU_SEND_KEY1:
- cmd->opcode = GPCMD_REPORT_KEY;
- cmd->bytes[8] = 12;
- cmd->bytes[9] = 2 | (a->lsk.agid << 6);
- xs->datalen = 12;
- xs->flags |= SCSI_DATA_IN;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0)
- dvd_copy_key(a->lsk.key, &buf[4]);
- break;
- case DVD_LU_SEND_TITLE_KEY:
- cmd->opcode = GPCMD_REPORT_KEY;
- _lto4b(a->lstk.lba, &cmd->bytes[1]);
- cmd->bytes[8] = 12;
- cmd->bytes[9] = 4 | (a->lstk.agid << 6);
- xs->datalen = 12;
- xs->flags |= SCSI_DATA_IN;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0) {
- a->lstk.cpm = (buf[4] >> 7) & 1;
- a->lstk.cp_sec = (buf[4] >> 6) & 1;
- a->lstk.cgms = (buf[4] >> 4) & 3;
- dvd_copy_key(a->lstk.title_key, &buf[5]);
- }
- break;
- case DVD_LU_SEND_ASF:
- cmd->opcode = GPCMD_REPORT_KEY;
- cmd->bytes[8] = 8;
- cmd->bytes[9] = 5 | (a->lsasf.agid << 6);
- xs->datalen = 8;
- xs->flags |= SCSI_DATA_IN;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0)
- a->lsasf.asf = buf[7] & 1;
- break;
- case DVD_HOST_SEND_CHALLENGE:
- cmd->opcode = GPCMD_SEND_KEY;
- cmd->bytes[8] = 16;
- cmd->bytes[9] = 1 | (a->hsc.agid << 6);
- buf[1] = 14;
- dvd_copy_challenge(&buf[4], a->hsc.chal);
- xs->datalen = 16;
- xs->flags |= SCSI_DATA_OUT;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0)
- a->type = DVD_LU_SEND_KEY1;
- break;
- case DVD_HOST_SEND_KEY2:
- cmd->opcode = GPCMD_SEND_KEY;
- cmd->bytes[8] = 12;
- cmd->bytes[9] = 3 | (a->hsk.agid << 6);
- buf[1] = 10;
- dvd_copy_key(&buf[4], a->hsk.key);
- xs->datalen = 12;
- xs->flags |= SCSI_DATA_OUT;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0)
- a->type = DVD_AUTH_ESTABLISHED;
- else
- a->type = DVD_AUTH_FAILURE;
- break;
- case DVD_INVALIDATE_AGID:
- cmd->opcode = GPCMD_REPORT_KEY;
- cmd->bytes[9] = 0x3f | (a->lsa.agid << 6);
- xs->data = NULL;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- break;
- case DVD_LU_SEND_RPC_STATE:
- cmd->opcode = GPCMD_REPORT_KEY;
- cmd->bytes[8] = 8;
- cmd->bytes[9] = 8 | (0 << 6);
- xs->datalen = 8;
- xs->flags |= SCSI_DATA_IN;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0) {
- a->lrpcs.type = (buf[4] >> 6) & 3;
- a->lrpcs.vra = (buf[4] >> 3) & 7;
- a->lrpcs.ucca = (buf[4]) & 7;
- a->lrpcs.region_mask = buf[5];
- a->lrpcs.rpc_scheme = buf[6];
- }
- break;
- case DVD_HOST_SEND_RPC_STATE:
- cmd->opcode = GPCMD_SEND_KEY;
- cmd->bytes[8] = 8;
- cmd->bytes[9] = 6 | (0 << 6);
- buf[1] = 6;
- buf[4] = a->hrpcs.pdrc;
- xs->datalen = 8;
- xs->flags |= SCSI_DATA_OUT;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- break;
- default:
- scsi_xs_put(xs);
- error = ENOTTY;
- break;
- }
- done:
- dma_free(buf, DVD_AUTH_BUFSIZE);
- return (error);
- }
- #define DVD_READ_PHYSICAL_BUFSIZE (4 + 4 * 20)
- int
- dvd_read_physical(struct cd_softc *sc, union dvd_struct *s)
- {
- struct scsi_generic *cmd;
- struct dvd_layer *layer;
- struct scsi_xfer *xs;
- u_int8_t *buf, *bufp;
- int error, i;
- buf = dma_alloc(DVD_READ_PHYSICAL_BUFSIZE, PR_WAITOK | PR_ZERO);
- if (buf == NULL)
- return (ENOMEM);
- xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
- if (xs == NULL) {
- error = ENOMEM;
- goto done;
- }
- xs->cmdlen = sizeof(*cmd);
- xs->data = buf;
- xs->datalen = DVD_READ_PHYSICAL_BUFSIZE;
- xs->timeout = 30000;
- cmd = xs->cmd;
- cmd->opcode = GPCMD_READ_DVD_STRUCTURE;
- cmd->bytes[6] = s->type;
- _lto2b(xs->datalen, &cmd->bytes[7]);
- cmd->bytes[5] = s->physical.layer_num;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0) {
- for (i = 0, bufp = &buf[4], layer = &s->physical.layer[0];
- i < 4; i++, bufp += 20, layer++) {
- bzero(layer, sizeof(*layer));
- layer->book_version = bufp[0] & 0xf;
- layer->book_type = bufp[0] >> 4;
- layer->min_rate = bufp[1] & 0xf;
- layer->disc_size = bufp[1] >> 4;
- layer->layer_type = bufp[2] & 0xf;
- layer->track_path = (bufp[2] >> 4) & 1;
- layer->nlayers = (bufp[2] >> 5) & 3;
- layer->track_density = bufp[3] & 0xf;
- layer->linear_density = bufp[3] >> 4;
- layer->start_sector = _4btol(&bufp[4]);
- layer->end_sector = _4btol(&bufp[8]);
- layer->end_sector_l0 = _4btol(&bufp[12]);
- layer->bca = bufp[16] >> 7;
- }
- }
- done:
- dma_free(buf, DVD_READ_PHYSICAL_BUFSIZE);
- return (error);
- }
- #define DVD_READ_COPYRIGHT_BUFSIZE 8
- int
- dvd_read_copyright(struct cd_softc *sc, union dvd_struct *s)
- {
- struct scsi_generic *cmd;
- struct scsi_xfer *xs;
- u_int8_t *buf;
- int error;
- buf = dma_alloc(DVD_READ_COPYRIGHT_BUFSIZE, PR_WAITOK | PR_ZERO);
- if (buf == NULL)
- return (ENOMEM);
- xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
- if (xs == NULL) {
- error = ENOMEM;
- goto done;
- }
- xs->cmdlen = sizeof(*cmd);
- xs->data = buf;
- xs->datalen = DVD_READ_COPYRIGHT_BUFSIZE;
- xs->timeout = 30000;
- cmd = xs->cmd;
- cmd->opcode = GPCMD_READ_DVD_STRUCTURE;
- cmd->bytes[6] = s->type;
- _lto2b(xs->datalen, &cmd->bytes[7]);
- cmd->bytes[5] = s->copyright.layer_num;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0) {
- s->copyright.cpst = buf[4];
- s->copyright.rmi = buf[5];
- }
- done:
- dma_free(buf, DVD_READ_COPYRIGHT_BUFSIZE);
- return (error);
- }
- int
- dvd_read_disckey(struct cd_softc *sc, union dvd_struct *s)
- {
- struct scsi_read_dvd_structure_data *buf;
- struct scsi_read_dvd_structure *cmd;
- struct scsi_xfer *xs;
- int error;
- buf = dma_alloc(sizeof(*buf), PR_WAITOK | PR_ZERO);
- if (buf == NULL)
- return (ENOMEM);
- xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
- if (xs == NULL) {
- error = ENOMEM;
- goto done;
- }
- xs->cmdlen = sizeof(*cmd);
- xs->data = (void *)buf;
- xs->datalen = sizeof(*buf);
- xs->timeout = 30000;
- cmd = (struct scsi_read_dvd_structure *)xs->cmd;
- cmd->opcode = GPCMD_READ_DVD_STRUCTURE;
- cmd->format = s->type;
- cmd->agid = s->disckey.agid << 6;
- _lto2b(xs->datalen, cmd->length);
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0)
- memcpy(s->disckey.value, buf->data, sizeof(s->disckey.value));
- done:
- dma_free(buf, sizeof(*buf));
- return (error);
- }
- #define DVD_READ_BCA_BUFLEN (4 + 188)
- int
- dvd_read_bca(struct cd_softc *sc, union dvd_struct *s)
- {
- struct scsi_generic *cmd;
- struct scsi_xfer *xs;
- u_int8_t *buf;
- int error;
- buf = dma_alloc(DVD_READ_BCA_BUFLEN, PR_WAITOK | PR_ZERO);
- if (buf == NULL)
- return (ENOMEM);
- xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
- if (xs == NULL) {
- error = ENOMEM;
- goto done;
- }
- xs->cmdlen = sizeof(*cmd);
- xs->data = buf;
- xs->datalen = DVD_READ_BCA_BUFLEN;
- xs->timeout = 30000;
- cmd = xs->cmd;
- cmd->opcode = GPCMD_READ_DVD_STRUCTURE;
- cmd->bytes[6] = s->type;
- _lto2b(xs->datalen, &cmd->bytes[7]);
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0) {
- s->bca.len = _2btol(&buf[0]);
- if (s->bca.len < 12 || s->bca.len > 188)
- return (EIO);
- memcpy(s->bca.value, &buf[4], s->bca.len);
- }
- done:
- dma_free(buf, DVD_READ_BCA_BUFLEN);
- return (error);
- }
- int
- dvd_read_manufact(struct cd_softc *sc, union dvd_struct *s)
- {
- struct scsi_read_dvd_structure_data *buf;
- struct scsi_read_dvd_structure *cmd;
- struct scsi_xfer *xs;
- int error;
- buf = dma_alloc(sizeof(*buf), PR_WAITOK | PR_ZERO);
- if (buf == NULL)
- return (ENOMEM);
- xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
- if (xs == NULL) {
- error = ENOMEM;
- goto done;
- }
- xs->cmdlen = sizeof(*cmd);
- xs->data = (void *)buf;
- xs->datalen = sizeof(*buf);
- xs->timeout = 30000;
- cmd = (struct scsi_read_dvd_structure *)xs->cmd;
- cmd->opcode = GPCMD_READ_DVD_STRUCTURE;
- cmd->format = s->type;
- _lto2b(xs->datalen, cmd->length);
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error == 0) {
- s->manufact.len = _2btol(buf->len);
- if (s->manufact.len >= 0 && s->manufact.len <= 2048)
- memcpy(s->manufact.value, buf->data, s->manufact.len);
- else
- error = EIO;
- }
- done:
- dma_free(buf, sizeof(*buf));
- return (error);
- }
- int
- dvd_read_struct(struct cd_softc *sc, union dvd_struct *s)
- {
- switch (s->type) {
- case DVD_STRUCT_PHYSICAL:
- return (dvd_read_physical(sc, s));
- case DVD_STRUCT_COPYRIGHT:
- return (dvd_read_copyright(sc, s));
- case DVD_STRUCT_DISCKEY:
- return (dvd_read_disckey(sc, s));
- case DVD_STRUCT_BCA:
- return (dvd_read_bca(sc, s));
- case DVD_STRUCT_MANUFACT:
- return (dvd_read_manufact(sc, s));
- default:
- return (EINVAL);
- }
- }
- int
- cd_interpret_sense(struct scsi_xfer *xs)
- {
- struct scsi_sense_data *sense = &xs->sense;
- struct scsi_link *sc_link = xs->sc_link;
- u_int8_t skey = sense->flags & SSD_KEY;
- u_int8_t serr = sense->error_code & SSD_ERRCODE;
- if (((sc_link->flags & SDEV_OPEN) == 0) ||
- (serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED))
- return (scsi_interpret_sense(xs));
- /*
- * We do custom processing in cd for the unit becoming ready
- * case. We do not allow xs->retries to be decremented on the
- * "Unit Becoming Ready" case. This is because CD drives
- * report "Unit Becoming Ready" when loading media and can
- * take a long time. Rather than having a massive timeout for
- * all operations (which would cause other problems), we allow
- * operations to wait (but be interruptable with Ctrl-C)
- * forever as long as the drive is reporting that it is
- * becoming ready. All other cases of not being ready are
- * handled by the default handler.
- */
- switch(skey) {
- case SKEY_NOT_READY:
- if ((xs->flags & SCSI_IGNORE_NOT_READY) != 0)
- return (0);
- if (ASC_ASCQ(sense) == SENSE_NOT_READY_BECOMING_READY) {
- SC_DEBUG(sc_link, SDEV_DB1, ("not ready: busy (%#x)\n",
- sense->add_sense_code_qual));
- /* don't count this as a retry */
- xs->retries++;
- return (scsi_delay(xs, 1));
- }
- break;
- /* XXX more to come here for a few other cases */
- default:
- break;
- }
- return (scsi_interpret_sense(xs));
- }
- /*
- * Find out from the device what its capacity is.
- */
- u_int64_t
- cd_size(struct scsi_link *sc_link, int flags, u_int32_t *blksize)
- {
- struct scsi_read_cap_data_16 *rdcap16;
- struct scsi_read_capacity_16 *cmd;
- struct scsi_read_cap_data *rdcap;
- struct scsi_read_capacity *cmd10;
- struct scsi_xfer *xs;
- u_int64_t max_addr;
- int error;
- if (blksize != NULL)
- *blksize = 0;
- CLR(flags, SCSI_IGNORE_ILLEGAL_REQUEST);
- /*
- * Start with a READ CAPACITY(10).
- */
- rdcap = dma_alloc(sizeof(*rdcap), ((flags & SCSI_NOSLEEP) ?
- PR_NOWAIT : PR_WAITOK) | PR_ZERO);
- if (rdcap == NULL)
- return (0);
- xs = scsi_xs_get(sc_link, flags | SCSI_DATA_IN | SCSI_SILENT);
- if (xs == NULL) {
- dma_free(rdcap, sizeof(*rdcap));
- return (0);
- }
- xs->cmdlen = sizeof(*cmd10);
- xs->data = (void *)rdcap;
- xs->datalen = sizeof(*rdcap);
- xs->timeout = 20000;
- cmd10 = (struct scsi_read_capacity *)xs->cmd;
- cmd10->opcode = READ_CAPACITY;
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error) {
- SC_DEBUG(sc_link, SDEV_DB1, ("READ CAPACITY error (%#x)\n",
- error));
- dma_free(rdcap, sizeof(*rdcap));
- return (0);
- }
- max_addr = _4btol(rdcap->addr);
- if (blksize != NULL)
- *blksize = _4btol(rdcap->length);
- dma_free(rdcap, sizeof(*rdcap));
- if (SCSISPC(sc_link->inqdata.version) < 3 && max_addr != 0xffffffff)
- goto exit;
- /*
- * SCSI-3 devices, or devices reporting more than 2^32-1 sectors can
- * try READ CAPACITY(16).
- */
- rdcap16 = dma_alloc(sizeof(*rdcap16), ((flags & SCSI_NOSLEEP) ?
- PR_NOWAIT : PR_WAITOK) | PR_ZERO);
- if (rdcap16 == NULL)
- goto exit;
- xs = scsi_xs_get(sc_link, flags | SCSI_DATA_IN | SCSI_SILENT);
- if (xs == NULL) {
- dma_free(rdcap16, sizeof(*rdcap16));
- goto exit;
- }
- xs->cmdlen = sizeof(*cmd);
- xs->data = (void *)rdcap16;
- xs->datalen = sizeof(*rdcap16);
- xs->timeout = 20000;
- cmd = (struct scsi_read_capacity_16 *)xs->cmd;
- cmd->opcode = READ_CAPACITY_16;
- cmd->byte2 = SRC16_SERVICE_ACTION;
- _lto4b(sizeof(*rdcap16), cmd->length);
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
- if (error) {
- SC_DEBUG(sc_link, SDEV_DB1, ("READ CAPACITY 16 error (%#x)\n",
- error));
- dma_free(rdcap16, sizeof(*rdcap16));
- goto exit;
- }
- max_addr = _8btol(rdcap16->addr);
- if (blksize != NULL)
- *blksize = _4btol(rdcap16->length);
- /* XXX The other READ CAPACITY(16) info could be stored away. */
- dma_free(rdcap16, sizeof(*rdcap16));
- return (max_addr + 1);
- exit:
- /* Return READ CAPACITY 10 values. */
- if (max_addr != 0xffffffff)
- return (max_addr + 1);
- else if (blksize != NULL)
- *blksize = 0;
- return (0);
- }
- #if defined(__macppc__)
- int
- cd_eject(void)
- {
- struct cd_softc *sc;
- int error = 0;
- if (cd_cd.cd_ndevs == 0 || (sc = cd_cd.cd_devs[0]) == NULL)
- return (ENXIO);
- if ((error = disk_lock(&sc->sc_dk)) != 0)
- return (error);
- if (sc->sc_dk.dk_openmask == 0) {
- sc->sc_link->flags |= SDEV_EJECTING;
- scsi_prevent(sc->sc_link, PR_ALLOW,
- SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY |
- SCSI_SILENT | SCSI_IGNORE_MEDIA_CHANGE);
- sc->sc_link->flags &= ~SDEV_MEDIA_LOADED;
- scsi_start(sc->sc_link, SSS_STOP|SSS_LOEJ, 0);
- sc->sc_link->flags &= ~SDEV_EJECTING;
- }
- disk_unlock(&sc->sc_dk);
- return (error);
- }
- #endif
|