diff options
author | Joerg Wunsch <joerg@FreeBSD.org> | 1996-01-27 17:48:12 +0000 |
---|---|---|
committer | Joerg Wunsch <joerg@FreeBSD.org> | 1996-01-27 17:48:12 +0000 |
commit | 984afdcfd2da34ad437c00d12b68eae613b3af0f (patch) | |
tree | 8f8f8222d051368c30144ddac9db24ef0b4bfbed /sys | |
parent | ed46ab75b27dd6fc2d51fe39db230b003b80b046 (diff) | |
download | src-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.h | 93 | ||||
-rw-r--r-- | sys/scsi/worm.c | 593 |
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. + */ |