aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorJoerg Wunsch <joerg@FreeBSD.org>1996-01-27 17:48:12 +0000
committerJoerg Wunsch <joerg@FreeBSD.org>1996-01-27 17:48:12 +0000
commit984afdcfd2da34ad437c00d12b68eae613b3af0f (patch)
tree8f8f8222d051368c30144ddac9db24ef0b4bfbed /sys
parented46ab75b27dd6fc2d51fe39db230b003b80b046 (diff)
downloadsrc-984afdcfd2da34ad437c00d12b68eae613b3af0f.tar.gz
src-984afdcfd2da34ad437c00d12b68eae613b3af0f.zip
The Great Merge.
The worm driver is alpha-usable! I've stuffed everything that is needed into the kernel driver, including the logic to select between different vendor's quirks. Disclaimer: this has by now _only_ been tested on a heavily tweaked 2.0.5R system. I've done my best to retrofit it into -current, but i don't have a chance yet to test it in a -current environment.
Notes
Notes: svn path=/head/; revision=13650
Diffstat (limited to 'sys')
-rw-r--r--sys/scsi/scsi_worm.h93
-rw-r--r--sys/scsi/worm.c593
2 files changed, 643 insertions, 43 deletions
diff --git a/sys/scsi/scsi_worm.h b/sys/scsi/scsi_worm.h
new file mode 100644
index 000000000000..b2fa7740ac53
--- /dev/null
+++ b/sys/scsi/scsi_worm.h
@@ -0,0 +1,93 @@
+#ifndef _SCSI_SCSI_WORM_H
+#define _SCSI_SCSI_WORM_H
+
+#define PAGE_HEADERLEN 2
+
+/*
+ * Opcodes
+ */
+
+#define REZERO_UNIT 0x01 /* re-init; XXX belongs to scsi_all? */
+#define SYNCHRONIZE_CACHE 0x35 /* flush write buffer, close wr chn */
+#define FIRST_WRITEABLE_ADDR 0xe2 /* return first available LBA */
+#define RESERVE_TRACK 0xe4 /* reserve a track for later write */
+#define READ_TRACK_INFORMATION 0xe5 /* get info for a particular track */
+#define WRITE_TRACK 0xe6 /* open the write channel */
+#define LOAD_UNLOAD 0xe7 /* resembles part of START_STOP */
+#define FIXATION 0xe9 /* write leadin/leadout */
+
+struct scsi_rezero_unit
+{
+ u_char op_code;
+ u_char byte2;
+ u_char reserved[3];
+ u_char control;
+};
+
+struct scsi_synchronize_cache
+{
+ u_char op_code;
+ u_char byte2;
+ u_char reserved[7];
+ u_char control;
+};
+
+/* struct scsi_first_writeable_address; */
+
+struct scsi_reserve_track
+{
+ u_char op_code;
+ u_char byte2;
+ u_char reserved[3];
+ u_char reserve_length_3; /* MSB */
+ u_char reserve_length_2;
+ u_char reserve_length_1;
+ u_char reserve_length_0; /* LSB */
+ u_char control;
+};
+
+/* struct scsi_read_track_information; */
+
+struct scsi_write_track
+{
+ u_char op_code;
+ u_char byte2;
+ u_char reserved[3];
+ u_char track_number; /* 0 means: use next available */
+ u_char mode;
+#define WORM_TRACK_MODE_RAW 0x08
+#define WORM_TRACK_MODE_AUDIO 0x04
+#define WORM_TRACK_MODE_MODE1 0x01 /* also audio with preemphasis */
+#define WORM_TRACK_MODE_MODE2 0x02
+ u_char transfer_length_1; /* number of blocks to transfer, MSB */
+ u_char transfer_length_0; /* LSB */
+ u_char control;
+#define WORM_TRACK_CONTROL_MIX 0x40 /* mixed mode blocks */
+};
+
+struct scsi_load_unload
+{
+ u_char op_code;
+ u_char byte2;
+ u_char reserved[6];
+ u_char load;
+#define WORM_LOAD_MEDIUM 0x01
+ u_char control;
+};
+
+struct scsi_fixation
+{
+ u_char op_code;
+ u_char byte2;
+ u_char reserved[6];
+ u_char action;
+#define WORM_FIXATION_ONP 0x08 /* open next program area (new session) */
+#define WORM_TOC_TYPE_AUDIO 0x00
+#define WORM_TOC_TYPE_CDROM 0x01
+#define WORM_TOC_TYPE_CDROM_1 0x02 /* CD-ROM, first track mode 1 (?) */
+#define WORM_TOC_TYPE_CDROM_2 0x03 /* CD-ROM, first track mode 2 */
+#define WORM_TOC_TYPE_CDI 0x04
+ u_char control;
+};
+
+#endif /* _SCSI_SCSI_WORM_H */
diff --git a/sys/scsi/worm.c b/sys/scsi/worm.c
index 169e373baf48..4eb8932e6a04 100644
--- a/sys/scsi/worm.c
+++ b/sys/scsi/worm.c
@@ -7,6 +7,12 @@
* 508 433 5266
* dufault@hda.com
*
+ * Copyright (C) 1996, interface business GmbH
+ * Tolkewitzer Str. 49
+ * D-01277 Dresden
+ * F.R. Germany
+ * <joerg_wunsch@interface-business.de>
+ *
* This code is contributed to the University of California at Berkeley:
*
* Redistribution and use in source and binary forms, with or without
@@ -37,7 +43,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: worm.c,v 1.18 1996/01/05 20:12:53 wollman Exp $
+ * $Id: worm.c,v 1.19 1996/01/20 15:27:36 joerg Exp $
*/
/* XXX This is PRELIMINARY.
@@ -57,6 +63,9 @@
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/proc.h>
+#include <sys/ioctl.h>
+#include <sys/wormio.h>
+#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#ifdef DEVFS
@@ -65,27 +74,72 @@
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#include <scsi/scsi_disk.h>
+#include <scsi/scsi_worm.h>
+/* #include <scsi/scsi_cd.h> */ /* XXX a CD-R includes all CD functionality */
+
+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 (*finalize_track)(struct scsi_link *);
+ errval (*finalize_disk)(struct scsi_link *, int toc_type, int onp);
+};
-struct scsi_data {
+struct scsi_data
+{
struct buf_queue_head buf_queue;
- u_int32 n_blks; /* Number of blocks (0 for bogus) */
- u_int32 blk_size; /* Size of each blocks */
+ u_int32 n_blks; /* Number of blocks (0 for bogus) */
+ u_int32 blk_size; /* Size of each blocks */
#ifdef DEVFS
- void *devfs_token; /* more elaborate later */
+ void *devfs_token; /* more elaborate later */
#endif
+
+ struct worm_quirks *quirks; /* model-specific functions */
+
+ u_int8 dummy; /* use dummy writes */
+ u_int8 speed; /* select drive speed */
+ u_int8 audio; /* write audio data */
+ u_int8 preemp; /* audio only: use preemphasis */
+
+ u_int32 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 */
};
static void wormstart(u_int32 unit, u_int32 flags);
static errval worm_open(dev_t dev, int flags, int fmt, struct proc *p,
- struct scsi_link *sc_link);
+ struct scsi_link *sc_link);
static errval worm_ioctl(dev_t dev, int cmd, caddr_t addr, int flag,
- struct proc *p, struct scsi_link *sc_link);
+ struct proc *p, struct scsi_link *sc_link);
static errval worm_close(dev_t dev, int flag, int fmt, struct proc *p,
- struct scsi_link *sc_link);
+ 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 unit,
+ struct wormio_quirk_select *);
+static errval worm_rezero_unit(struct scsi_link *sc_link);
+
+/* 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_finalize_track(struct scsi_link *);
+static errval rf4100_finalize_disk(struct scsi_link *, int toc_type, int onp);
+
+static worm_devsw_installed = 0;
+
static d_open_t wormopen;
static d_close_t wormclose;
static d_ioctl_t wormioctl;
@@ -123,16 +177,29 @@ static struct scsi_device worm_switch =
wormunit,
0,
worm_open,
- 0,
+ worm_ioctl,
worm_close,
worm_strategy,
};
-static int worm_size(struct scsi_link *sc_link, int flags)
+/* 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
+ },
+ {0}
+};
+
+static errval
+worm_size(struct scsi_link *sc_link, int flags)
{
- int ret;
+ errval ret;
struct scsi_data *worm = sc_link->sd;
+ SC_DEBUG(sc_link, SDEV_DB2, ("worm_size"));
+
worm->n_blks = scsi_read_capacity(sc_link, &worm->blk_size,
flags);
@@ -142,12 +209,12 @@ static int worm_size(struct scsi_link *sc_link, int flags)
if (worm->n_blks)
{
sc_link->flags |= SDEV_MEDIA_LOADED;
- ret = 1;
+ ret = 0;
}
else
{
sc_link->flags &= ~SDEV_MEDIA_LOADED;
- ret = 0;
+ ret = ENXIO;
}
return ret;
@@ -156,21 +223,31 @@ static int worm_size(struct scsi_link *sc_link, int flags)
static errval
wormattach(struct scsi_link *sc_link)
{
+#ifdef DEVFS
+ char name[20];
+ int mynor;
+#endif
struct scsi_data *worm = sc_link->sd;
TAILQ_INIT(&worm->buf_queue);
- printf("- UNTESTED ");
+ printf("- see worm(4) for usage warnings");
- if (worm_size(sc_link, SCSI_NOSLEEP | SCSI_NOMASK) == 0)
+ if (worm_size(sc_link, SCSI_NOSLEEP | SCSI_NOMASK) != 0)
printf("- can't get capacity.");
else
- printf("with %ld %ld byte blocks.",
- worm->n_blks, worm->blk_size);
+ printf("with %ld blocks.", worm->n_blks);
#ifdef DEVFS
-
- worm->devfs_token = devfs_add_devsw( "/", "rworm", &worm_cdevsw, 0,
- DV_CHR, 0, 0, 0600);
+ mynor = wormunit(sc_link->dev);
+ sprintf(name, "rworm%d", mynor);
+ worm->devfs_token =
+ devfs_add_devsw("/", name, &worm_cdevsw, mynor,
+ DV_CHR, 0, 0, 0600);
+ sprintf(name, "rworm%d.ctl", mynor);
+ worm->devfs_token =
+ devfs_add_devsw("/", name, &worm_cdevsw,
+ mynor | SCSI_CONTROL_MASK,
+ DV_CHR, 0, 0, 0600);
#endif
return 0;
}
@@ -261,10 +338,12 @@ wormstart(unit, flags)
sizeof(cmd),
(u_char *) bp->b_un.b_addr,
bp->b_bcount,
- 0, /* can't retry a read on a tape really */
+ 0,
100000,
bp,
flags | SCSI_NOSLEEP) == SUCCESSFULLY_QUEUED) {
+ if ((bp->b_flags & B_READ) == B_WRITE)
+ worm->worm_flags |= WORMFL_WRITTEN;
} else {
printf("worm%ld: oops not queued\n", unit);
if (bp) {
@@ -284,7 +363,7 @@ worm_strategy(struct buf *bp, struct scsi_link *sc_link)
u_int32 opri;
struct scsi_data *worm;
- unit = minor((bp->b_dev));
+ unit = wormunit(bp->b_dev);
worm = sc_link->sd;
/*
@@ -294,7 +373,7 @@ worm_strategy(struct buf *bp, struct scsi_link *sc_link)
if (!(sc_link->flags & SDEV_MEDIA_LOADED) ||
bp->b_blkno * DEV_BSIZE > worm->n_blks * worm->blk_size||
(bp->b_bcount % worm->blk_size) != 0) {
- SC_DEBUG(sc_link, SDEV_DB2,
+ SC_DEBUG(sc_link, SDEV_DB3,
("worm block size / capacity error") );
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
@@ -326,12 +405,21 @@ worm_strategy(struct buf *bp, struct scsi_link *sc_link)
}
/*
- * Open the device. XXX: I'm completely guessing at this sequence.
+ * 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,
-struct scsi_link *sc_link)
+ struct scsi_link *sc_link)
{
+ struct scsi_data *worm;
+ errval error;
+
+ error = 0;
+ worm = sc_link->sd;
+
if (sc_link->flags & SDEV_OPEN)
return EBUSY;
@@ -350,62 +438,481 @@ struct scsi_link *sc_link)
scsi_test_unit_ready(sc_link, SCSI_SILENT);
/*
+ * The semantics of the "flags" is as follows:
+ *
+ * If the device has been opened O_RDONLY, no write 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_RDONLY 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
*/
sc_link->flags |= SDEV_OPEN; /* unit attn errors are now errors */
if (scsi_test_unit_ready(sc_link, SCSI_SILENT) != 0) {
SC_DEBUG(sc_link, SDEV_DB3, ("not ready\n"));
+ if ((flags & FWRITE) != 0)
+ worm->worm_flags &= ~WORMFL_TRACK_PREPED;
sc_link->flags &= ~SDEV_OPEN;
return ENXIO;
}
-#if 0
- scsi_start_unit(sc_link, SCSI_SILENT);
-
- scsi_prevent(sc_link, PR_PREVENT, SCSI_SILENT);
/*
- * XXX The worm_size() is required, alas, putting it here will
- * result in an ICORRECT COMMAND SEQUENCE error. Without it,
- * the driver will only work if the CD-R was present at boot-time.
+ * XXX The check might go away if we wanna support CDROM, too.
*/
- if (worm_size(sc_link, 0) == 0) {
+ if ((flags & FWRITE) != 0) {
+ scsi_start_unit(sc_link, SCSI_SILENT);
+ scsi_prevent(sc_link, PR_PREVENT, SCSI_SILENT);
+ }
+
+ if((flags & FWRITE) != 0 &&
+ ((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)) {
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("rezero, get size, or prepare_track 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;
- return ENXIO;
}
-#endif
- return 0;
+
+ return error;
}
static int
worm_close(dev_t dev, int flag, int fmt, struct proc *p,
- struct scsi_link *sc_link)
+ struct scsi_link *sc_link)
{
-#if 0
+ errval error;
+ struct scsi_data *worm = sc_link->sd;
+
scsi_stop_unit(sc_link, 0, SCSI_SILENT);
scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
-#endif
+
sc_link->flags &= ~SDEV_OPEN;
+ if((flags & FWRITE) != 0) {
+ worm->worm_flags &= ~WORMFL_TRACK_PREPED;
+ (worm->quirks->finalize_track)(sc_link);
+ }
+
return 0;
}
-static worm_devsw_installed = 0;
+/*
+ * Perform special action on behalf of the user.
+ * Knows about the internals of this device
+ */
+errval
+worm_ioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p,
+ struct scsi_link *sc_link)
+{
+ errval error = 0;
+ u_int8 unit;
+ register struct scsi_data *worm;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ unit = wormunit(dev);
+ worm = sc_link->sd;
+ 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;
+ }
+ }
+ }
+ 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 ||
+ (worm->worm_flags & WORMFL_WRITTEN) != 0)
+ error = EINVAL;
+ else {
+ worm->audio = w->audio;
+ worm->preemp = w->preemp;
+ 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);
+ }
+ }
+ break;
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+
+static errval
+worm_rezero_unit(struct scsi_link *sc_link)
+{
+ struct scsi_rezero_unit cmd;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("worm_rezero_unit"));
+
+ /*
+ * Re-initialize the unit, just to be sure.
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.op_code = REZERO_UNIT;
+ return scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &cmd,
+ sizeof(cmd),
+ 0, /* no data transfer */
+ 0,
+ /*WORMRETRY*/ 4,
+ 5000,
+ NULL,
+ 0);
+}
+
+static errval
+worm_quirk_select(struct scsi_link *sc_link, u_int32 unit,
+ struct wormio_quirk_select *qs)
+{
+ struct worm_quirks *qp;
+ struct scsi_data *worm = sc_link->sd;
+ errval error = 0;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("worm_quirk_select"));
+
+ 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;
+ }
+ else
+ error = EINVAL;
+
+ return error;
+}
+
static void
worm_drvinit(void *unused)
{
dev_t dev;
- if( ! worm_devsw_installed ) {
- dev = makedev(CDEV_MAJOR,0);
- cdevsw_add(&dev,&worm_cdevsw,NULL);
+ if (! worm_devsw_installed) {
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev, &worm_cdevsw, NULL);
worm_devsw_installed = 1;
}
}
SYSINIT(wormdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,worm_drvinit,NULL)
+/*
+ * Begin device-specific stuff. Subject to being moved out to LKMs.
+ */
+
+/*
+ * PLASMON RF4100/4102
+ * Perhaps other Plasmon's, too.
+ *
+ * NB: By now, you'll certainly have to compare the SCSI reference
+ * manual in order to understand the following.
+ */
+
+/* The following mode pages might apply to other drives as well. */
+
+struct plasmon_rf4100_pages
+{
+ u_char page_code;
+#define RF4100_PAGE_CODE_20 0x20
+#define RF4100_PAGE_CODE_21 0x21
+#define RF4100_PAGE_CODE_22 0x22
+#define RF4100_PAGE_CODE_23 0x23
+#define RF4100_PAGE_CODE_24 0x24
+#define RF4100_PAGE_CODE_25 0x25
+ u_char param_len;
+ union
+ {
+ /* page 0x20 omitted by now */
+ struct
+ {
+ u_char reserved1;
+ u_char mode;
+#define RF4100_RAW_MODE 0x10 /* raw mode enabled */
+#define RF4100_MIXED_MODE 0x08 /* midex 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 */
+ u_char track_number;
+ u_char isrc_i1; /* country code, ASCII */
+ u_char isrc_i2;
+ 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_i8_9; /* serial number, BCD */
+ u_char isrc_i10_11;
+ u_char isrc_i12_0;
+ u_char reserved2[2];
+ }
+ page_0x21;
+ /* mode page 0x22 omitted by now */
+ struct
+ {
+ u_char speed_select;
+#define RF4100_SPEED_AUDIO 0x01
+#define RF4100_SPEED_DOUBLE 0x02
+ u_char dummy_write;
+#define RF4100_DUMMY_WRITE 0x01
+ u_char reserved[4];
+ }
+ page_0x23;
+ /* pages 0x24 and 0x25 omitted by now */
+ }
+ pages;
+};
+
+
+static errval
+rf4100_prepare_disk(struct scsi_link *sc_link, int dummy, int speed)
+{
+ struct scsi_mode_select scsi_cmd;
+ struct {
+ struct scsi_mode_header header;
+ struct plasmon_rf4100_pages page;
+ } dat;
+ u_int32 pagelen, dat_len;
+ struct scsi_data *worm = sc_link->sd;
+
+ pagelen = sizeof(dat.page.pages.page_0x23) + PAGE_HEADERLEN;
+ dat_len = sizeof(struct scsi_mode_header) + pagelen;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("rf4100_prepare_disk"));
+ if (speed != RF4100_SPEED_AUDIO && speed != RF4100_SPEED_DOUBLE)
+ return EINVAL;
+
+ /*
+ * Set up a mode page 0x23
+ */
+ bzero(&dat, sizeof(dat));
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MODE_SELECT;
+ scsi_cmd.length = dat_len;
+ /* dat.header.dev_spec = host application code; (see spec) */
+ dat.page.page_code = RF4100_PAGE_CODE_23;
+ dat.page.param_len = sizeof(dat.page.pages.page_0x23);
+ dat.page.pages.page_0x23.speed_select = speed;
+ dat.page.pages.page_0x23.dummy_write = dummy? RF4100_DUMMY_WRITE: 0;
+ /*
+ * Fire it off.
+ */
+ return scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) &dat,
+ dat_len,
+ /*WORM_RETRIES*/ 4,
+ 5000,
+ NULL,
+ SCSI_DATA_OUT);
+}
+
+
+static errval
+rf4100_prepare_track(struct scsi_link *sc_link, int audio, int preemp)
+{
+ struct scsi_mode_select scsi_cmd;
+ struct {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ struct plasmon_rf4100_pages page;
+ } dat;
+ u_int32 pagelen, dat_len, blk_len;
+ struct scsi_data *worm = sc_link->sd;
+
+ pagelen = sizeof(dat.page.pages.page_0x21) + PAGE_HEADERLEN;
+ dat_len = sizeof(struct scsi_mode_header)
+ + sizeof(struct blk_desc)
+ + pagelen;
+
+ 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
+ * order to select the block length in question. We do this
+ * here, just prior to opening the write channel. (Spec:
+ * ``All information for the write is included in the MODE
+ * SELECT, MODE PAGE 21h, and the write channel can be
+ * considered open on receipt of the first WRITE command.'' I
+ * didn't have luck with an explicit WRITE TRACK command
+ * anyway, this might be different for other CD-R drives. -
+ * Jörg)
+ */
+ bzero(&dat, sizeof(dat));
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ 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) */
+
+ /*
+ * Fire it off.
+ */
+ return scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) &dat,
+ dat_len,
+ /*WORM_RETRIES*/ 4,
+ 5000,
+ NULL,
+ SCSI_DATA_OUT);
+}
+
+
+static errval
+rf4100_finalize_track(struct scsi_link *sc_link)
+{
+ struct scsi_data *worm = sc_link->sd;
+ struct scsi_synchronize_cache cmd;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("rf4100_finalize_track"));
+
+ /*
+ * Only a "synchronize cache" is needed.
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.op_code = SYNCHRONIZE_CACHE;
+ return scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &cmd,
+ sizeof(cmd),
+ 0, /* no data transfer */
+ 0,
+ 1,
+ 60000, /* this may take a while */
+ NULL,
+ 0);
+}
+
+
+static errval
+rf4100_finalize_disk(struct scsi_link *sc_link, int toc_type, int onp)
+{
+ struct scsi_data *worm = sc_link->sd;
+ struct scsi_fixation cmd;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("rf4100_finalize_disk"));
+
+ if (toc_type < 0 || toc_type > WORM_TOC_TYPE_CDI)
+ return EINVAL;
+
+ /*
+ * Fixate this session. Mark the next one as opened if onp
+ * is true. Otherwise, the disk will be finalized once and
+ * for all. ONP stands for "open next program area".
+ */
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.op_code = FIXATION;
+ cmd.action = (onp? WORM_FIXATION_ONP: 0) + toc_type;
+ return scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &cmd,
+ sizeof(cmd),
+ 0, /* no data transfer */
+ 0,
+ 1,
+ 20*60*1000, /* takes a huge amount of time */
+ NULL,
+ 0);
+}
+
+/*
+ * End Plasmon RF4100/4102 section.
+ */