A few improvements to the worm driver.

- remove all calls to scsi_stop_unit(). Some drives refuse commands when
  stopped. This will fix the 'device not configured' message which was
  cleared after opening/closing the tray.
- Never set the logical block address in the scsi_cmd struct when writing.
  The computation was bogus for block sizes not a multiple of DEV_BSIZE.
  (the bug is still there in the READ case)
- reset the block size to the 2048 bytes in finalize_track() track to avoid
  an error when mounting a disk after an audio write.
- remove the WORMIOCQUIRKSELECT ioctl. Quirks are now recorded at probe time
  (see scsiconf.c)
- change and expand the argument to the WORMIOCPREPTRACK ioctl. It now possible
  to select more track options (copy bits, ISRC codes, track type,
  track number)
- add an error handler to catch false errors (warnings in fact) and record
  the error type.
- add an ioctl call (WORMIOERROR) to get more information on the nature of the
  error when a command or a write failed.
- add an ioctl call (WORMIOCFINISHTRACK) to finalize a track without closing
  the device (closing the device still finalize the track if the command was
  not performed)

Approved by:	joerg
This commit is contained in:
Jean-Marc Zucconi 1997-05-19 17:30:50 +00:00
parent 7136226da3
commit 1ad8b2cb84
3 changed files with 482 additions and 232 deletions

View File

@ -32,7 +32,7 @@
.\" USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
.\" DAMAGE.
.\"
.\" $Id$
.\" $Id: worm.4,v 1.10 1997/02/22 13:24:55 peter Exp $
.\" "
.Dd January 27, 1996
.Dt WORM 4
@ -125,23 +125,7 @@ The following
calls apply to CD-R devices. Their declaration can be found in the
header file
.Pa <sys/wormio.h> .
.Bl -tag -width WORMIOCQUIRKSELECT
.It Dv WORMIOCQUIRKSELECT
Select the set of quirk functions to use for this device. This is the
only allowed action on a virgin device after booting. The argument
structure takes a vendor and a model string, which are matched against
the vendor and model strings of all quirk records that have been
registered with the driver. Currently, the only known quirks must
have been statically compiled into the driver.
.Pp
If the driver fails to match the vendor and model string against any
known quirk record, the system call returns with an error, and the
variable
.Va errno
will be set to
.Er EINVAL .
The system call argument is a pointer to a
.Dv struct wormio_quirk_select .
.Bl -tag -width WORMIOFINISHTRACK
.It Dv WORMIOCPREPDISK
This command selects several session-wide parameters in the driver.
The argument structure,
@ -161,22 +145,98 @@ is device-dependent, where a speed value of one generally applies to
audio disks, and a speed value of 2 (or more) is used for recording
data.
.It Dv WORMIOCPREPTRACK
The two parameters
This command selects several options for writing the next track.
The argument structure,
.Dv struct wormio_prepare_track
is as follows:
.Bd -literal -offset indent
struct wormio_prepare_track {
int audio;
int preemp;
int track_type;
int copy_bits;
int track_number;
char ISRC_country[2];
char ISRC_owner[3];
int ISRC_year;
char ISRC_serial[5];
};
.Ed
.Dv audio
and
.Dv preemp
are being passed as arguments in a
.Dv struct wormio_prepare_track .
Both are Boolean, i.e. can be either 0 or 1. If
.Dv audio
is set to 1, the next track will be recorded in audio format, with
2352 bytes per block. If
should be set to 1 if you are recording an audio track.
If
.Dv preemp
is also set to 1, the audio data are assumed to be recorded with
preemphasis. If
preemphasis.
.Dv track_type
defines both the the specific data fields in a user data block and
its size. Currently available types are
.Bl -tag -width BLOCK_MODE_2_FORM_2b
.It Dv BLOCK_RAW
2352 bytes, raw data.
.It Dv BLOCK_RAWPQ
2368 bytes, raw data with P and Q subchannels.
.It Dv BLOCK_RAWPW
2448 bytes, raw data with P-W subchannel appended.
.It Dv BLOCK_MODE_1
2048 bytes, mode 1 (ISO/IEC 10149).
.It Dv BLOCK_MODE_2
2336 bytes, mode 2 (ISO/IEC 10149).
.It Dv BLOCK_MODE_2_FORM_1
2048 bytes, CD-ROM XA form 1.
.It Dv BLOCK_MODE_2_FORM_1b
2056 bytes, CD-ROM XA form 1.
.It Dv BLOCK_MODE_2_FORM_2
2324 bytes, CD-ROM XA form 2.
.It Dv BLOCK_MODE_2_FORM_2b
2332 bytes, CD-ROM XA form 2.
.El
.Pp
Note that not all track types are supported for a given drive.
.Pp
.Dv copy_bits
define the permissions for copying the track. Available values are
.Bl -tag -width COPY_PERMITTED
.It Dv COPY_INHIBIT
No copy is allowed.
.It Dv COPY_PERMITTED
The track can be copied.
.It Dv COPY_SCMS
The track can be copied once.
.El
.Dv track_number :
if the track number is zero, a new track will be created with a track
number one higher than the previous track. If the track number is not
zero, then this track number must point to a reserved track, unless it
is an empty disc which will start with the given track number.
.Dv ISRC_country :
two characters in the range [0-9A-Z] defining the country code.
.Dv ISRC_owner :
three characters in the range [0-9A-Z] defining the owner code.
.Dv ISRC_year :
the year of recording.
.Dv ISRC_serial :
a serial number, composed of 5 digits.
.Pp
For writing an audio track, setting
.Dv audio
is 0, CD-ROM data with a block length of 2048 bytes are about to be
written next.
to 1,
.Dv preemp
to 0 or 1 and all the other field to 0 will do the job. For writing a
data track, you can just set
.Dv track_type
to
.Dv BLOCK_MODE_1 .
.It Dv WORMIOCFINISHTRACK
Will terminate the track. It takes no argument. Note that closing the
device will also terminate the track.
.It Dv WORMIOCFIXATION
This closes the current session. The argument is a pointer to
.Dv struct wormio_fixation ,
@ -190,15 +250,50 @@ to 1 will cause the next session being opened, so further recording
can be performed into the remaining space. If
.Dv onp
is 0, the disk will be closed once and for all.
.It Dv WORMIOERROR
This call may be used to get additional information when a I/O error
occured or to check if the last command ended with a recovered
error or a warning. The argument is a pointer to an integer. The
returned value can be:
.Bl -tag -width WORM_
.It Dv WORM_SEQUENCE_ERROR
Occurs if a write is performed when the track has not been prepared or
if
.Dv WORMCPREPTRACK
is done without a prior
.Dv WORMIOCPREPDISK .
.It Dv WORM_BUFFER_UNDERRUN
Indicates that the write action stopped because the cache buffer emptied.
.It Dv WORM_DUMMY_BLOCKS_ADDED
This a warning which may occur when the track is closed. Indicates
that during writing dummy blocks are added to meet the disc
specification (minimum of 300 blocks for a track).
.It Dv WORM_CALIBRATION_AREA_ALMOST_FULL
This is warning which indicates that a few Optimum Power Calibration
areas are left. It is recommended to fixate after the tracks are written.
.It Dv WORM_CALIBRATION_AREA_FULL
Indicates that the Calibration area is full. This means that no further
writes can be performed on this disc.
.It Dv WORM_ABSORPTION_CONTROL_ERROR
Indicates that an error might have occurred in the recorded data that
was written, caused by laser power clipping. This is a warning.
.It Dv WORM_END_OF_MEDIUM
Indicates that during writing the end of medium is detected or the
amount of track reached the limit of 99.
.It Dv WORM_OPTIMUM_POWER_CALIBRATION_ERROR
Indicates that power calibration failed. This could indicate: Wrong WO
medium installed, laser failure or drive failure.
.Pp
.El
If an unknown error occured, the returned value will be -1.
.El
Specifying wrong argument values to the above ioctl command will cause
the driver to return an error condition with
.Va errno
set to
.Er EINVAL .
Any attempt to perform something else then selecting a quirks record
on a device where this has not been done yet will return an error of
.Er ENXIO .
.Pp
In addition, the
.Xr scsi 4

View File

@ -43,7 +43,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: worm.c,v 1.37 1997/05/05 13:35:01 joerg Exp $
* $Id: worm.c,v 1.38 1997/05/10 12:12:47 joerg Exp $
*/
#include "opt_bounce.h"
@ -72,18 +72,12 @@
struct worm_quirks
{
/*
* Vendor and model are used for comparision; the model may be
* abbreviated (or could even be empty at all).
*/
const char *vendor;
const char *model;
/*
* The device-specific functions that need to be called during
* the several steps.
*/
errval (*prepare_disk)(struct scsi_link *, int dummy, int speed);
errval (*prepare_track)(struct scsi_link *, int audio, int preemp);
errval (*prepare_track)(struct scsi_link *, struct wormio_prepare_track *t);
errval (*finalize_track)(struct scsi_link *);
errval (*finalize_disk)(struct scsi_link *, int toc_type, int onp);
};
@ -105,14 +99,30 @@ struct scsi_data
u_int8_t dummy; /* use dummy writes */
u_int8_t speed; /* select drive speed */
u_int8_t audio; /* write audio data */
u_int8_t preemp; /* audio only: use preemphasis */
u_int32_t worm_flags; /* driver-internal flags */
#define WORMFL_DISK_PREPED 0x01 /* disk parameters have been spec'ed */
#define WORMFL_TRACK_PREPED 0x02 /* track parameters have been spec'ed */
#define WORMFL_WRITTEN 0x04 /* track has been written */
#define WORMFL_IOCTL_ONLY 0x08 /* O_NDELAY, only ioctls allowed */
int error; /* last error */
};
struct {
int asc;
int devmode;
int error;
int ret;
} worm_error[] = {
{0x24, WORM_Q_PLASMON|WORM_Q_PHILIPS, WORM_ABSORPTION_CONTROL_ERROR, 0},
{0xb0, WORM_Q_PLASMON|WORM_Q_PHILIPS, WORM_CALIBRATION_AREA_ALMOST_FULL, 0},
{0xb4, WORM_Q_PLASMON|WORM_Q_PHILIPS, WORM_CALIBRATION_AREA_FULL, SCSIRET_CONTINUE},
{0xb5, WORM_Q_PLASMON|WORM_Q_PHILIPS, WORM_DUMMY_BLOCKS_ADDED, 0},
{0xaa, WORM_Q_PLASMON|WORM_Q_PHILIPS, WORM_END_OF_MEDIUM, SCSIRET_CONTINUE},
{0xad, WORM_Q_PLASMON|WORM_Q_PHILIPS, WORM_BUFFER_UNDERRUN, SCSIRET_CONTINUE},
{0xaf, WORM_Q_PLASMON|WORM_Q_PHILIPS, WORM_OPTIMUM_POWER_CALIBRATION_ERROR, SCSIRET_CONTINUE},
{0, 0, 0, 0}
};
static void wormstart(u_int32_t unit, u_int32_t flags);
@ -124,22 +134,20 @@ static errval worm_ioctl(dev_t dev, int cmd, caddr_t addr, int flag,
static errval worm_close(dev_t dev, int flag, int fmt, struct proc *p,
struct scsi_link *sc_link);
static void worm_strategy(struct buf *bp, struct scsi_link *sc_link);
static errval worm_quirk_select(struct scsi_link *sc_link, u_int32_t unit,
struct wormio_quirk_select *);
static errval worm_read_toc(struct scsi_link *sc_link,
u_int32_t mode, u_int32_t start,
struct cd_toc_entry *data, u_int32_t len);
static errval worm_rezero_unit(struct scsi_link *sc_link);
static int worm_sense_handler(struct scsi_xfer *);
/* XXX should be moved out to an LKM */
static errval rf4100_prepare_disk(struct scsi_link *, int dummy, int speed);
static errval rf4100_prepare_track(struct scsi_link *, int audio, int preemp);
static errval rf4100_prepare_track(struct scsi_link *, struct wormio_prepare_track *);
static errval rf4100_finalize_track(struct scsi_link *);
static errval rf4100_finalize_disk(struct scsi_link *, int toc_type, int onp);
static errval hp4020i_prepare_disk(struct scsi_link *, int dummy, int speed);
static errval hp4020i_prepare_track(struct scsi_link *, int audio, int preemp);
static errval hp4020i_prepare_track(struct scsi_link *, struct wormio_prepare_track *);
static errval hp4020i_finalize_track(struct scsi_link *);
static errval hp4020i_finalize_disk(struct scsi_link *, int toc_type, int onp);
@ -168,7 +176,7 @@ SCSI_DEVICE_ENTRIES(worm)
static struct scsi_device worm_switch =
{
NULL,
worm_sense_handler,
wormstart, /* we have a queue, and this is how we service it */
NULL,
NULL,
@ -189,24 +197,13 @@ static struct scsi_device worm_switch =
worm_strategy,
};
/* XXX This should become the registration table for the LKMs. */
struct worm_quirks worm_quirks[] = {
{
"PLASMON", "RF410",
rf4100_prepare_disk, rf4100_prepare_track,
rf4100_finalize_track, rf4100_finalize_disk
},
{
"HP", "4020i",
hp4020i_prepare_disk, hp4020i_prepare_track,
hp4020i_finalize_track, hp4020i_finalize_disk
},
{
"PHILIPS", "CDD2000",
hp4020i_prepare_disk, hp4020i_prepare_track,
hp4020i_finalize_track, hp4020i_finalize_disk
},
{0}
static struct worm_quirks worm_quirks_plasmon = {
rf4100_prepare_disk, rf4100_prepare_track,
rf4100_finalize_track, rf4100_finalize_disk
};
static struct worm_quirks worm_quirks_philips = {
hp4020i_prepare_disk, hp4020i_prepare_track,
hp4020i_finalize_track, hp4020i_finalize_disk
};
static inline void
@ -238,7 +235,6 @@ worm_size(struct scsi_link *sc_link, int flags)
* doesn't give us any good results. Make a more educated
* guess instead.
*/
worm->blk_size = (worm->audio? 2352: 2048);
if (worm->n_blks)
{
@ -298,6 +294,9 @@ wormattach(struct scsi_link *sc_link)
* XXX It looks like we need a "scsistart" to hoist common code up
* into. In particular, the removable media checking should be
* handled in one place.
*
* Writes will fail if the disk and track not been prepared via the control
* device.
*/
static void
wormstart(unit, flags)
@ -309,7 +308,7 @@ wormstart(unit, flags)
register struct buf *bp = 0;
struct scsi_rw_big cmd;
u_int32_t lba; /* Logical block address */
u_int32_t lba; /* Logical block address */
u_int32_t tl; /* Transfer length */
SC_DEBUG(sc_link, SDEV_DB2, ("wormstart "));
@ -340,6 +339,15 @@ wormstart(unit, flags)
}
TAILQ_REMOVE( &worm->buf_queue, bp, b_act);
if (((bp->b_flags & B_READ) == B_WRITE)
&& ((worm->worm_flags & WORMFL_TRACK_PREPED) == 0)) {
SC_DEBUG(sc_link, SDEV_DB3, ("sequence error\n"));
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
worm->error = WORM_SEQUENCE_ERROR;
biodone(bp);
goto badnews;
}
/*
* Fill out the scsi command
*/
@ -352,11 +360,19 @@ wormstart(unit, flags)
flags |= SCSI_DATA_IN;
}
worm->error = 0;
lba = bp->b_blkno / (worm->blk_size / DEV_BSIZE);
lba = bp->b_blkno / (worm->blk_size / DEV_BSIZE);
tl = bp->b_bcount / worm->blk_size;
scsi_uto4b(lba, &cmd.addr_3);
if (bp->b_flags & B_READ)
/*
* Leave the LBA as 0 for write operations, it
* is reserved in this case (and wouldn't make
* any sense to set it at all, since CD-R write
* operations are in `streaming' mode anyway.
*/
scsi_uto4b(lba, &cmd.addr_3);
scsi_uto2b(tl, &cmd.length2);
/*
@ -459,8 +475,6 @@ worm_strategy(struct buf *bp, struct scsi_link *sc_link)
/*
* Open the device.
* Only called for the "real" device, not for the control device.
* Will fail if the disk and track not been prepared via the control
* device.
*/
static int
worm_open(dev_t dev, int flags, int fmt, struct proc *p,
@ -495,18 +509,7 @@ worm_open(dev_t dev, int flags, int fmt, struct proc *p,
* If the device has been opened with O_NONBLOCK set, no
* actual IO will be allowed, and the command sequence is only
* subject to the restrictions as in worm_ioctl() below.
*
* If the device is to be opened with O_RDWR/O_WRONLY, the
* disk and track must have been prepared accordingly by
* preceding ioctls (on an O_NONBLOCK descriptor for the device),
* or a sequence error will result here.
*/
if ((flags & FWRITE) != 0 &&
(worm->worm_flags & WORMFL_TRACK_PREPED) == 0) {
SC_DEBUG(sc_link, SDEV_DB3, ("sequence error\n"));
return ENXIO;
}
/*
* Next time actually take notice of error returns,
* unit attn errors are now errors.
@ -527,12 +530,9 @@ worm_open(dev_t dev, int flags, int fmt, struct proc *p,
if((flags & FWRITE) != 0) {
if ((error = worm_rezero_unit(sc_link)) != 0 ||
(error = worm_size(sc_link, 0)) != 0 ||
(error = (worm->quirks->prepare_track)
(sc_link, worm->audio, worm->preemp)) != 0) {
(error = worm_size(sc_link, 0)) != 0) {
SC_DEBUG(sc_link, SDEV_DB3,
("rezero, get size, or prepare_track failed\n"));
scsi_stop_unit(sc_link, 0, SCSI_SILENT);
("rezero, or get size failed\n"));
scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
worm->worm_flags &= ~WORMFL_TRACK_PREPED;
sc_link->flags &= ~SDEV_OPEN;
@ -542,7 +542,6 @@ worm_open(dev_t dev, int flags, int fmt, struct proc *p,
if ((error = worm_size(sc_link, 0)) != 0) {
SC_DEBUG(sc_link, SDEV_DB3,
("get size failed\n"));
scsi_stop_unit(sc_link, 0, SCSI_SILENT);
scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
worm->worm_flags &= ~WORMFL_TRACK_PREPED;
sc_link->flags &= ~SDEV_OPEN;
@ -551,6 +550,17 @@ worm_open(dev_t dev, int flags, int fmt, struct proc *p,
} else
worm->worm_flags |= WORMFL_IOCTL_ONLY;
switch (*(int *) sc_link->devmodes) {
case WORM_Q_PLASMON:
worm->quirks = &worm_quirks_plasmon;
break;
case WORM_Q_PHILIPS:
worm->quirks = &worm_quirks_philips;
break;
default:
error = EINVAL;
}
worm->error = 0;
return error;
}
@ -564,15 +574,13 @@ worm_close(dev_t dev, int flags, int fmt, struct proc *p,
error = 0;
if ((worm->worm_flags & WORMFL_IOCTL_ONLY) == 0) {
scsi_stop_unit(sc_link, 0, SCSI_SILENT);
scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
sc_link->flags &= ~SDEV_OPEN;
if ((flags & FWRITE) != 0) {
worm->worm_flags &= ~WORMFL_TRACK_PREPED;
worm->error = 0;
if ((worm->worm_flags & WORMFL_TRACK_PREPED) != 0)
error = (worm->quirks->finalize_track)(sc_link);
worm->worm_flags &= ~WORMFL_TRACK_PREPED;
}
scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
}
sc_link->flags &= ~SDEV_OPEN;
worm->worm_flags &= ~WORMFL_IOCTL_ONLY;
@ -600,75 +608,76 @@ worm_ioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p,
SC_DEBUG(sc_link, SDEV_DB2, ("wormioctl 0x%x ", cmd));
switch (cmd) {
case WORMIOCQUIRKSELECT:
error = worm_quirk_select(sc_link, unit,
(struct wormio_quirk_select *)addr);
break;
case WORMIOCPREPDISK:
if (worm->quirks == 0)
error = ENXIO;
else {
struct wormio_prepare_disk *w =
(struct wormio_prepare_disk *)addr;
if (w->dummy != 0 && w->dummy != 1)
error = EINVAL;
else {
error = (worm->quirks->prepare_disk)
(sc_link, w->dummy, w->speed);
if (error == 0) {
worm->worm_flags |= WORMFL_DISK_PREPED;
worm->dummy = w->dummy;
worm->speed = w->speed;
}
}
{
struct wormio_prepare_disk *w =
(struct wormio_prepare_disk *)addr;
if (w->dummy != 0 && w->dummy != 1)
error = EINVAL;
else {
error = (worm->quirks->prepare_disk)
(sc_link, w->dummy, w->speed);
if (error == 0) {
worm->worm_flags |= WORMFL_DISK_PREPED;
worm->dummy = w->dummy;
worm->speed = w->speed;
}
break;
}
}
break;
case WORMIOCPREPTRACK:
if (worm->quirks == 0)
error = ENXIO;
else {
struct wormio_prepare_track *w =
(struct wormio_prepare_track *)addr;
if (w->audio != 0 && w->audio != 1)
error = EINVAL;
else if (w->audio == 0 && w->preemp)
error = EINVAL;
else if ((worm->worm_flags & WORMFL_DISK_PREPED)==0)
error = EINVAL;
else {
worm->audio = w->audio;
worm->preemp = w->preemp;
worm->worm_flags |=
WORMFL_TRACK_PREPED;
}
}
break;
{
struct wormio_prepare_track *w =
(struct wormio_prepare_track *)addr;
if (w->audio != 0 && w->audio != 1)
error = EINVAL;
else if (w->audio == 0 && w->preemp)
error = EINVAL;
else if ((worm->worm_flags & WORMFL_DISK_PREPED)==0) {
error = EINVAL;
worm->error = WORM_SEQUENCE_ERROR;
} else {
if ((error = (worm->quirks->prepare_track)
(sc_link, w)) == 0)
worm->worm_flags |=
WORMFL_TRACK_PREPED;
}
}
break;
case WORMIOCFINISHTRACK:
error = (worm->quirks->finalize_track)(sc_link);
worm->worm_flags &= ~WORMFL_TRACK_PREPED;
break;
case WORMIOCFIXATION:
if (worm->quirks == 0)
error = ENXIO;
else {
struct wormio_fixation *w =
(struct wormio_fixation *)addr;
if ((worm->worm_flags & WORMFL_WRITTEN) == 0)
error = EINVAL;
else if (w->toc_type < WORM_TOC_TYPE_AUDIO ||
w->toc_type > WORM_TOC_TYPE_CDI)
error = EINVAL;
else if (w->onp != 0 && w->onp != 1)
error = EINVAL;
else {
worm->worm_flags = 0;
/* no fixation needed if dummy write */
if (worm->dummy == 0)
error = (worm->quirks->finalize_disk)
(sc_link, w->toc_type, w->onp);
}
{
struct wormio_fixation *w =
(struct wormio_fixation *)addr;
if ((worm->worm_flags & WORMFL_WRITTEN) == 0)
error = EINVAL;
else if (w->toc_type < WORM_TOC_TYPE_AUDIO ||
w->toc_type > WORM_TOC_TYPE_CDI)
error = EINVAL;
else if (w->onp != 0 && w->onp != 1)
error = EINVAL;
else {
worm->worm_flags = 0;
/* no fixation needed if dummy write */
if (worm->dummy == 0) {
worm->error = 0;
error = (worm->quirks->finalize_disk)
(sc_link, w->toc_type, w->onp);
}
break;
}
}
break;
case WORMIOERROR:
bcopy(&(worm->error), addr, sizeof (int));
break;
case CDIOREADTOCHEADER:
{
@ -877,33 +886,29 @@ worm_read_toc(struct scsi_link *sc_link, u_int32_t mode, u_int32_t start,
SCSI_DATA_IN));
}
static errval
worm_quirk_select(struct scsi_link *sc_link, u_int32_t unit,
struct wormio_quirk_select *qs)
static int
worm_sense_handler(struct scsi_xfer *xs)
{
struct worm_quirks *qp;
struct scsi_data *worm = sc_link->sd;
errval error = 0;
SC_DEBUG(sc_link, SDEV_DB2, ("worm_quirk_select"));
struct scsi_data *worm;
struct scsi_sense_data *sense;
struct scsi_sense_extended *ext;
int asc, devmode, i;
worm = xs->sc_link->sd;
sense = &(xs->sense);
ext = (struct scsi_sense_extended *) &(sense->ext.extended);
asc = ext->add_sense_code;
devmode = *(int *) xs->sc_link->devmodes;
for (qp = worm_quirks; qp->vendor; qp++)
if (strcmp(qp->vendor, qs->vendor) == 0 &&
strncmp(qp->model, qs->model, strlen(qp->model)) == 0)
break;
if (qp->vendor) {
SC_DEBUG(sc_link, SDEV_DB3,
("worm_quirk_select: %s %s",
qp->vendor, qp->model));
worm->quirks = qp;
for (i = 0; worm_error[i].asc; i++)
if ((asc == worm_error[i].asc) && (devmode & worm_error[i].devmode)) {
worm->error = worm_error[i].error;
return worm_error[i].ret;
}
else
error = EINVAL;
return error;
worm->error = -1;
return SCSIRET_CONTINUE;
}
static void
worm_drvinit(void *unused)
{
@ -920,6 +925,16 @@ SYSINIT(wormdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,worm_drvinit,NULL)
* Begin device-specific stuff. Subject to being moved out to LKMs.
*/
static u_char
ascii_to_6bit (char c)
{
if (c < '0' || c > 'Z' || (c > '9' && c < 'A'))
return 0;
if (c <= '9')
return c - '0';
else
return c - 'A' + 11;
}
/*
* PLASMON RF4100/4102
* Perhaps other Plasmon's, too.
@ -951,7 +966,7 @@ struct plasmon_rf4100_pages
#define RF4100_MIXED_MODE 0x08 /* mixed mode data enabled */
#define RF4100_AUDIO_MODE 0x04 /* audio mode data enabled */
#define RF4100_MODE_1 0x01 /* mode 1 blocks are enabled */
#define RF4110_MODE_2 0x02 /* mode 2 blocks are enabled */
#define RF4100_MODE_2 0x02 /* mode 2 blocks are enabled */
u_char track_number;
u_char isrc_i1; /* country code, ASCII */
u_char isrc_i2;
@ -1028,15 +1043,19 @@ rf4100_prepare_disk(struct scsi_link *sc_link, int dummy, int speed)
static errval
rf4100_prepare_track(struct scsi_link *sc_link, int audio, int preemp)
rf4100_prepare_track(struct scsi_link *sc_link, struct wormio_prepare_track *t)
{
struct scsi_mode_select scsi_cmd;
struct scsi_data *worm;
struct {
struct scsi_mode_header header;
struct blk_desc blk_desc;
struct plasmon_rf4100_pages page;
} dat;
u_int32_t pagelen, dat_len, blk_len;
int year;
worm = sc_link->sd;
pagelen = sizeof(dat.page.pages.page_0x21) + PAGE_HEADERLEN;
dat_len = sizeof(struct scsi_mode_header)
@ -1045,16 +1064,6 @@ rf4100_prepare_track(struct scsi_link *sc_link, int audio, int preemp)
SC_DEBUG(sc_link, SDEV_DB2, ("rf4100_prepare_track"));
if (!audio && preemp)
return EINVAL;
/*
* By now, make a simple decision about the block length to be
* used. It's just only Red Book (Audio) == 2352 bytes, or
* Yellow Book (CD-ROM) Mode 1 == 2048 bytes.
*/
blk_len = audio? 2352: 2048;
/*
* Set up a mode page 0x21. Note that the block descriptor is
* mandatory in at least one of the MODE SELECT commands, in
@ -1072,15 +1081,70 @@ rf4100_prepare_track(struct scsi_link *sc_link, int audio, int preemp)
scsi_cmd.op_code = MODE_SELECT;
scsi_cmd.length = dat_len;
dat.header.blk_desc_len = sizeof(struct blk_desc);
/* dat.header.dev_spec = host application code; (see spec) */
scsi_uto3b(blk_len, dat.blk_desc.blklen);
dat.page.page_code = RF4100_PAGE_CODE_21;
dat.page.param_len = sizeof(dat.page.pages.page_0x21);
dat.page.pages.page_0x21.mode =
(audio? RF4100_AUDIO_MODE: RF4100_MODE_1) +
(preemp? RF4100_MODE_1: 0);
/* dat.page.pages.page_0x21.track_number = 0; (current track) */
/* dat.header.dev_spec = host application code; (see spec) */
if (t->audio) {
blk_len = 2352;
dat.page.pages.page_0x21.mode = RF4100_AUDIO_MODE +
(t->preemp? RF4100_MODE_1 : 0);
} else
switch (t->track_type) {
case BLOCK_RAW:
blk_len = 2352;
dat.page.pages.page_0x21.mode = RF4100_RAW_MODE;
break;
case BLOCK_MODE_1:
blk_len = 2048;
dat.page.pages.page_0x21.mode = RF4100_MODE_1;
break;
case BLOCK_MODE_2:
blk_len = 2336;
dat.page.pages.page_0x21.mode = RF4100_MODE_2;
break;
case BLOCK_MODE_2_FORM_1:
blk_len = 2048;
dat.page.pages.page_0x21.mode = RF4100_MODE_2;
break;
case BLOCK_MODE_2_FORM_1b:
blk_len = 2056;
dat.page.pages.page_0x21.mode = RF4100_MODE_2;
break;
case BLOCK_MODE_2_FORM_2:
blk_len = 2324;
dat.page.pages.page_0x21.mode = RF4100_MODE_2;
break;
case BLOCK_MODE_2_FORM_2b:
blk_len = 2332;
dat.page.pages.page_0x21.mode = RF4100_MODE_2;
break;
default:
return EINVAL;
}
dat.page.pages.page_0x21.mode |= t->copy_bits << 5;
worm->blk_size = blk_len;
dat.page.pages.page_0x21.track_number = t->track_number;
dat.page.pages.page_0x21.isrc_i1 = ascii_to_6bit(t->ISRC_country[0]);
dat.page.pages.page_0x21.isrc_i2 = ascii_to_6bit(t->ISRC_country[1]);
dat.page.pages.page_0x21.isrc_i3 = ascii_to_6bit(t->ISRC_owner[0]);
dat.page.pages.page_0x21.isrc_i4 = ascii_to_6bit(t->ISRC_owner[1]);
dat.page.pages.page_0x21.isrc_i5 = ascii_to_6bit(t->ISRC_owner[2]);
year = t->ISRC_year > 1900 ? t->ISRC_year - 1900 : t->ISRC_year;
if (year > 99 || year < 0)
return EINVAL;
dat.page.pages.page_0x21.isrc_i6_7 = bin2bcd(year);
if (t->ISRC_serial[0]) {
dat.page.pages.page_0x21.isrc_i8_9 = ((t->ISRC_serial[0]-'0') << 8) ||
(t->ISRC_serial[1] - '0');
dat.page.pages.page_0x21.isrc_i10_11 = ((t->ISRC_serial[2]-'0') << 8) ||
(t->ISRC_serial[3] - '0');
dat.page.pages.page_0x21.isrc_i12_0 = (t->ISRC_serial[4] - '0' << 8);
}
scsi_uto3b(blk_len, dat.blk_desc.blklen);
/*
* Fire it off.
*/
@ -1100,7 +1164,8 @@ static errval
rf4100_finalize_track(struct scsi_link *sc_link)
{
struct scsi_synchronize_cache cmd;
int error;
SC_DEBUG(sc_link, SDEV_DB2, ("rf4100_finalize_track"));
/*
@ -1108,7 +1173,7 @@ rf4100_finalize_track(struct scsi_link *sc_link)
*/
bzero(&cmd, sizeof(cmd));
cmd.op_code = SYNCHRONIZE_CACHE;
return scsi_scsi_cmd(sc_link,
error = scsi_scsi_cmd(sc_link,
(struct scsi_generic *) &cmd,
sizeof(cmd),
0, /* no data transfer */
@ -1117,6 +1182,13 @@ rf4100_finalize_track(struct scsi_link *sc_link)
60000, /* this may take a while */
NULL,
0);
if (!error) {
struct wormio_prepare_track t;
bzero (&t, sizeof (t));
t.track_type = BLOCK_MODE_1;
error = rf4100_prepare_track(sc_link, &t);
}
return error;
}
@ -1190,7 +1262,7 @@ struct hp_4020i_pages
u_char isrc_i3; /* owner code, ASCII */
u_char isrc_i4;
u_char isrc_i5;
u_char isrc_i6_7; /* country code, BCD */
u_char isrc_i6_7; /* year code, BCD */
u_char isrc_i8_9; /* serial number, BCD */
u_char isrc_i10_11;
u_char isrc_i12_0;
@ -1261,15 +1333,19 @@ hp4020i_prepare_disk(struct scsi_link *sc_link, int dummy, int speed)
static errval
hp4020i_prepare_track(struct scsi_link *sc_link, int audio, int preemp)
hp4020i_prepare_track(struct scsi_link *sc_link, struct wormio_prepare_track *t)
{
struct scsi_mode_select scsi_cmd;
struct scsi_data *worm;
struct {
struct scsi_mode_header header;
struct blk_desc blk_desc;
struct hp_4020i_pages page;
} dat;
u_int32_t pagelen, dat_len, blk_len;
int year;
worm = sc_link->sd;
pagelen = sizeof(dat.page.pages.page_0x21) + PAGE_HEADERLEN;
dat_len = sizeof(struct scsi_mode_header)
@ -1278,15 +1354,6 @@ hp4020i_prepare_track(struct scsi_link *sc_link, int audio, int preemp)
SC_DEBUG(sc_link, SDEV_DB2, ("hp4020i_prepare_track"));
if (!audio && preemp)
return EINVAL;
/*
* By now, make a simple decision about the block length to be
* used. It's just only Red Book (Audio) == 2352 bytes, or
* Yellow Book (CD-ROM) Mode 1 == 2048 bytes.
*/
blk_len = audio? 2352: 2048;
/*
* Set up a mode page 0x21. Note that the block descriptor is
@ -1306,14 +1373,70 @@ hp4020i_prepare_track(struct scsi_link *sc_link, int audio, int preemp)
scsi_cmd.byte2 |= SMS_PF;
scsi_cmd.length = dat_len;
dat.header.blk_desc_len = sizeof(struct blk_desc);
/* dat.header.dev_spec = host application code; (see spec) */
scsi_uto3b(blk_len, dat.blk_desc.blklen);
dat.page.page_code = HP4020I_PAGE_CODE_21;
dat.page.param_len = sizeof(dat.page.pages.page_0x21);
dat.page.pages.page_0x21.mode =
(audio? HP4020I_AUDIO_MODE: HP4020I_MODE_1) +
(preemp? HP4020I_MODE_1: 0);
/* dat.page.pages.page_0x21.track_number = 0; (current track) */
/* dat.header.dev_spec = host application code; (see spec) */
if (t->audio) {
blk_len = 2352;
dat.page.pages.page_0x21.mode = HP4020I_AUDIO_MODE +
(t->preemp? HP4020I_MODE_1 : 0);
} else
switch (t->track_type) {
case BLOCK_RAW:
blk_len = 2352;
dat.page.pages.page_0x21.mode = HP4020I_RAW_MODE;
break;
case BLOCK_MODE_1:
blk_len = 2048;
dat.page.pages.page_0x21.mode = HP4020I_MODE_1;
break;
case BLOCK_MODE_2:
blk_len = 2336;
dat.page.pages.page_0x21.mode = HP4020I_MODE_2;
break;
case BLOCK_MODE_2_FORM_1:
blk_len = 2048;
dat.page.pages.page_0x21.mode = HP4020I_MODE_2;
break;
case BLOCK_MODE_2_FORM_1b:
blk_len = 2056;
dat.page.pages.page_0x21.mode = HP4020I_MODE_2;
break;
case BLOCK_MODE_2_FORM_2:
blk_len = 2324;
dat.page.pages.page_0x21.mode = HP4020I_MODE_2;
break;
case BLOCK_MODE_2_FORM_2b:
blk_len = 2332;
dat.page.pages.page_0x21.mode = HP4020I_MODE_2;
break;
default:
return EINVAL;
}
dat.page.pages.page_0x21.mode |= t->copy_bits << 5;
worm->blk_size = blk_len;
dat.page.pages.page_0x21.track_number = t->track_number;
dat.page.pages.page_0x21.isrc_i1 = ascii_to_6bit(t->ISRC_country[0]);
dat.page.pages.page_0x21.isrc_i2 = ascii_to_6bit(t->ISRC_country[1]);
dat.page.pages.page_0x21.isrc_i3 = ascii_to_6bit(t->ISRC_owner[0]);
dat.page.pages.page_0x21.isrc_i4 = ascii_to_6bit(t->ISRC_owner[1]);
dat.page.pages.page_0x21.isrc_i5 = ascii_to_6bit(t->ISRC_owner[2]);
year = t->ISRC_year > 1900 ? t->ISRC_year - 1900 : t->ISRC_year;
if (year > 99 || year < 0)
return EINVAL;
dat.page.pages.page_0x21.isrc_i6_7 = bin2bcd(year);
if (t->ISRC_serial[0]) {
dat.page.pages.page_0x21.isrc_i8_9 = ((t->ISRC_serial[0]-'0') << 8) ||
(t->ISRC_serial[1] - '0');
dat.page.pages.page_0x21.isrc_i10_11 = ((t->ISRC_serial[2]-'0') << 8) ||
(t->ISRC_serial[3] - '0');
dat.page.pages.page_0x21.isrc_i12_0 = (t->ISRC_serial[4] - '0' << 8);
}
scsi_uto3b(blk_len, dat.blk_desc.blklen);
/*
* Fire it off.
@ -1334,7 +1457,8 @@ static errval
hp4020i_finalize_track(struct scsi_link *sc_link)
{
struct scsi_synchronize_cache cmd;
int error;
SC_DEBUG(sc_link, SDEV_DB2, ("hp4020i_finalize_track"));
/*
@ -1342,7 +1466,7 @@ hp4020i_finalize_track(struct scsi_link *sc_link)
*/
bzero(&cmd, sizeof(cmd));
cmd.op_code = SYNCHRONIZE_CACHE;
return scsi_scsi_cmd(sc_link,
error = scsi_scsi_cmd(sc_link,
(struct scsi_generic *) &cmd,
sizeof(cmd),
0, /* no data transfer */
@ -1351,6 +1475,13 @@ hp4020i_finalize_track(struct scsi_link *sc_link)
60000, /* this may take a while */
NULL,
0);
if (!error) {
struct wormio_prepare_track t;
bzero (&t, sizeof (t));
t.track_type = BLOCK_MODE_1;
error = hp4020i_prepare_track(sc_link, &t);
}
return error;
}

View File

@ -9,18 +9,6 @@
* Ioctls for the WORM drive *
\***************************************************************/
/*
* Quirk select: chose the set of quirk functions to use for this
* device.
*/
struct wormio_quirk_select
{
const char *vendor; /* vendor name */
const char *model; /* model name */
};
#define WORMIOCQUIRKSELECT _IOW('W', 10, struct wormio_quirk_select)
/*
* Prepare disk-wide parameters.
@ -42,10 +30,29 @@ struct wormio_prepare_track
{
int audio; /* audio track (data track if 0) */
int preemp; /* audio with preemphasis */
#define BLOCK_RAW 0 /* 2352 bytes, raw data */
#define BLOCK_RAWPQ 1 /* 2368 bytes, raw data with P and Q subchannels */
#define BLOCK_RAWPW 2 /* 2448 bytes, raw data with P-W subchannel appended */
#define BLOCK_MODE_1 8 /* 2048 bytes, mode 1 (ISO/IEC 10149) */
#define BLOCK_MODE_2 9 /* 2336 bytes, mode 2 (ISO/IEC 10149) */
#define BLOCK_MODE_2_FORM_1 10 /* 2048 bytes, CD-ROM XA form 1 */
#define BLOCK_MODE_2_FORM_1b 11 /* 2056 bytes, CD-ROM XA form 1 */
#define BLOCK_MODE_2_FORM_2 12 /* 2324 bytes, CD-ROM XA form 2 */
#define BLOCK_MODE_2_FORM_2b 13 /* 2332 bytes, CD-ROM XA form 2 */
int track_type; /* defines the number of bytes in a block */
#define COPY_INHIBIT 0 /* no copy allowed */
#define COPY_PERMITTED 1 /* track can be copied */
#define COPY_SCMS 2 /* alternate copy */
int copy_bits; /* define the possibilities for copying */
int track_number;
char ISRC_country[2]; /* country code (2 chars) */
char ISRC_owner[3]; /* owner code (3 chars) */
int ISRC_year; /* year of recording */
char ISRC_serial[5]; /* serial number */
};
#define WORMIOCPREPTRACK _IOW('W', 21, struct wormio_prepare_track)
/*
* Fixation: write leadins and leadouts. Select table-of-contents
* type for this session. If onp is != 0, another session will be
@ -60,4 +67,21 @@ struct wormio_fixation
#define WORMIOCFIXATION _IOW('W', 22, struct wormio_fixation)
/*
* Finalize track
*/
#define WORMIOCFINISHTRACK _IO('W', 23)
/* Errors/warnings */
#define WORM_SEQUENCE_ERROR 1
#define WORM_DUMMY_BLOCKS_ADDED 2
#define WORM_CALIBRATION_AREA_ALMOST_FULL 3
#define WORM_CALIBRATION_AREA_FULL 4
#define WORM_BUFFER_UNDERRUN 5
#define WORM_ABSORPTION_CONTROL_ERROR 6
#define WORM_END_OF_MEDIUM 7
#define WORM_OPTIMUM_POWER_CALIBRATION_ERROR 8
#define WORMIOERROR _IOR('W', 24, int)
#endif /* !_SYS_WORMIO_H_ */