freebsd-skq/sys/dev/ata/atapi-cd.c
Søren Schmidt 775986fc09 Sixth update to the new ATA/ATAPI driver:
Fixed problems:

    Promise controllers was not always set up correctly.
        Parantheses are a good thing, fixed.

    Some older CDROM's could hang the probe.
        Proberly wait for the drive to catch its breath after IDENTIFY.

    Some CD writers fails because they dont support rezero.
        Rearranged the code to not use rezero.

    Warnings now that we use EGCS.
        Fixed.
1999-04-10 18:53:35 +00:00

1435 lines
43 KiB
C

/*-
* Copyright (c) 1998,1999 Søren Schmidt
* 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,
* without modification, immediately at the beginning of the file.
* 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. 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.
*
* $Id: atapi-cd.c,v 1.4 1999/03/28 18:57:19 sos Exp $
*/
#include "ata.h"
#include "atapicd.h"
#include "opt_devfs.h"
#if NATA > 0 && NATAPICD > 0
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/disklabel.h>
#include <sys/devicestat.h>
#include <sys/cdio.h>
#include <sys/wormio.h>
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/stat.h>
#ifdef DEVFS
#include <sys/devfsext.h>
#endif
#include <pci/pcivar.h>
#include <dev/ata/ata-all.h>
#include <dev/ata/atapi-all.h>
#include <dev/ata/atapi-cd.h>
static d_open_t acdopen;
static d_close_t acdclose;
static d_read_t acdread;
static d_write_t acdwrite;
static d_ioctl_t acdioctl;
static d_strategy_t acdstrategy;
#define BDEV_MAJOR 31
#define CDEV_MAJOR 117
static struct cdevsw acd_cdevsw = {
acdopen, acdclose, acdread, acdwrite,
acdioctl, nostop, nullreset, nodevtotty,
seltrue, nommap, acdstrategy, "acd",
NULL, -1, nodump, nopsize,
D_DISK, 0, -1
};
#define NUNIT 16 /* Max # of devices */
#define F_BOPEN 0x0001 /* The block device is opened */
#define F_MEDIA_CHANGED 0x0002 /* The media have changed since open */
#define F_LOCKED 0x0004 /* This unit is locked (or should be) */
#define F_TRACK_PREP 0x0008 /* Track should be prep'ed */
#define F_TRACK_PREPED 0x0010 /* Track has been prep'ed */
#define F_DISK_PREPED 0x0020 /* Disk has been prep'ed */
#define F_WRITTEN 0x0040 /* The medium has been written to */
static struct acd_softc *acdtab[NUNIT];
static int32_t acdnlun = 0; /* Number of configured drives */
int32_t acdattach(struct atapi_softc *);
static struct acd_softc *acd_init_lun(struct atapi_softc *, int, struct devstat *);
static void acd_start(struct acd_softc *);
static void acd_done(struct atapi_request *);
static int32_t acd_test_unit_ready (struct acd_softc *);
static int32_t acd_lock_device (struct acd_softc *, int32_t);
static int32_t acd_start_device (struct acd_softc *, int32_t);
static int32_t acd_pause_device (struct acd_softc *, int32_t);
static int32_t acd_mode_sense (struct acd_softc *, u_int8_t, void *, int32_t);
static int32_t acd_mode_select (struct acd_softc *, void *, int32_t);
static int32_t acd_read_toc(struct acd_softc *);
static void acd_describe(struct acd_softc *);
static int32_t acd_setchan(struct acd_softc *, u_int8_t, u_int8_t, u_int8_t, u_int8_t);
static int32_t acd_eject(struct acd_softc *, int);
static void acd_select_slot(struct acd_softc *);
static int32_t acd_open_disk(struct acd_softc *, int);
static int32_t acd_open_track(struct acd_softc *, struct wormio_prepare_track *);
static int32_t acd_close_track(struct acd_softc *);
static int32_t acd_close_disk(struct acd_softc *);
static int32_t acd_read_track_info(struct acd_softc *, int, struct acd_track_info*);
static int32_t acd_blank_disk(struct acd_softc *);
static void lba2msf(int32_t, u_int8_t *, u_int8_t *, u_int8_t *);
static int32_t msf2lba(u_int8_t, u_int8_t, u_int8_t);
static void acd_drvinit(void *);
int
acdattach(struct atapi_softc *atp)
{
struct acd_softc *cdp;
struct changer *chp;
int32_t error, count;
if (acdnlun >= NUNIT) {
printf("acd: too many units\n");
return -1;
}
if ((cdp = acd_init_lun(atp, acdnlun, NULL)) == NULL) {
printf("acd: out of memory\n");
return -1;
}
/* Get drive capabilities, some drives needs this repeated */
for (count = 0 ; count < 5 ; count++) {
if (!(error = acd_mode_sense(cdp, ATAPI_CDROM_CAP_PAGE,
&cdp->cap, sizeof(cdp->cap))))
break;
}
if (error) {
free(cdp, M_TEMP);
return -1;
}
cdp->cap.max_speed = ntohs(cdp->cap.max_speed);
cdp->cap.max_vol_levels = ntohs(cdp->cap.max_vol_levels);
cdp->cap.buf_size = ntohs(cdp->cap.buf_size);
cdp->cap.cur_speed = ntohs(cdp->cap.cur_speed);
acd_describe(cdp);
/* If this is a changer device, allocate the neeeded lun's */
if (cdp->cap.mech == MST_MECH_CHANGER) {
int8_t ccb[16] = { ATAPI_MECH_STATUS,
0, 0, 0, 0, 0, 0, 0,
sizeof(struct changer)>>8, sizeof(struct changer),
0, 0, 0, 0, 0, 0 };
chp = malloc(sizeof(struct changer), M_TEMP, M_NOWAIT);
if (chp == NULL) {
printf("acd: out of memory\n");
return 0;
}
bzero(chp, sizeof(struct changer));
error = atapi_queue_cmd(cdp->atp, ccb, chp, sizeof(struct changer),
A_READ, NULL, NULL, NULL);
#ifdef ACD_DEBUG
printf("error=%02x curr=%02x slots=%d len=%d\n",
error, chp->current_slot, chp->slots, htons(chp->table_length));
#endif
if (!error) {
struct acd_softc *tmpcdp = cdp;
int32_t count;
int8_t string[16];
chp->table_length = htons(chp->table_length);
for (count = 0; count < chp->slots && acdnlun < NUNIT; count++) {
if (count > 0) {
tmpcdp = acd_init_lun(atp, acdnlun, cdp->stats);
if (!tmpcdp) {
printf("acd: out of memory\n");
return -1;
}
}
tmpcdp->slot = count;
tmpcdp->changer_info = chp;
printf("acd%d: changer slot %d %s\n", acdnlun, count,
(chp->slot[count].present ? "CD present" : "empty"));
acdtab[acdnlun++] = tmpcdp;
}
if (acdnlun >= NUNIT) {
printf("acd: too many units\n");
return 0;
}
sprintf(string, "acd%d-", cdp->lun);
devstat_add_entry(cdp->stats, string, tmpcdp->lun, DEV_BSIZE,
DEVSTAT_NO_ORDERED_TAGS,
DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_IDE,
0x178);
}
}
else {
acdtab[acdnlun++] = cdp;
devstat_add_entry(cdp->stats, "acd", cdp->lun, DEV_BSIZE,
DEVSTAT_NO_ORDERED_TAGS,
DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_IDE,
0x178);
}
return 0;
}
static struct acd_softc *
acd_init_lun(struct atapi_softc *atp, int32_t lun, struct devstat *stats)
{
struct acd_softc *acd;
if (!(acd = malloc(sizeof(struct acd_softc), M_TEMP, M_NOWAIT)))
return NULL;
bzero(acd, sizeof(struct acd_softc));
bufq_init(&acd->buf_queue);
acd->atp = atp;
acd->lun = lun;
acd->flags = F_MEDIA_CHANGED;
acd->flags &= ~(F_WRITTEN|F_TRACK_PREP|F_TRACK_PREPED);
acd->block_size = 2048;
acd->refcnt = 0;
acd->slot = -1;
acd->changer_info = NULL;
if (stats == NULL) {
if (!(acd->stats = malloc(sizeof(struct devstat),
M_TEMP, M_NOWAIT)))
return NULL;
bzero(acd->stats, sizeof(struct devstat));
}
else
acd->stats = stats;
#ifdef DEVFS
acd->a_cdevfs_token = devfs_add_devswf(&acd_cdevsw, dkmakeminor(lun, 0, 0),
DV_CHR, UID_ROOT, GID_OPERATOR, 0644,
"racd%da", lun);
acd->c_cdevfs_token = devfs_add_devswf(&acd_cdevsw,
dkmakeminor(lun, 0, RAW_PART),
DV_CHR, UID_ROOT, GID_OPERATOR, 0644,
"racd%dc", lun);
acd->a_bdevfs_token = devfs_add_devswf(&acd_cdevsw, dkmakeminor(lun, 0, 0),
DV_BLK, UID_ROOT, GID_OPERATOR, 0644,
"acd%da", lun);
acd->c_bdevfs_token = devfs_add_devswf(&acd_cdevsw,
dkmakeminor(lun, 0, RAW_PART),
DV_BLK, UID_ROOT, GID_OPERATOR, 0644,
"acd%dc", lun);
#endif
return acd;
}
static void
acd_describe(struct acd_softc *cdp)
{
int32_t comma;
int8_t *mechanism;
int8_t model_buf[40+1];
int8_t revision_buf[8+1];
bpack(cdp->atp->atapi_parm->model, model_buf, sizeof(model_buf));
bpack(cdp->atp->atapi_parm->revision, revision_buf, sizeof(revision_buf));
printf("acd%d: <%s/%s> CDROM drive at ata%d as %s\n",
cdp->lun, model_buf, revision_buf,
cdp->atp->controller->lun,
(cdp->atp->unit == ATA_MASTER) ? "master" : "slave ");
printf("acd%d: drive speed ", cdp->lun);
if (cdp->cap.cur_speed != cdp->cap.max_speed)
printf("%d - ", cdp->cap.cur_speed * 1000 / 1024);
printf("%dKB/sec", cdp->cap.max_speed * 1000 / 1024);
if (cdp->cap.buf_size)
printf(", %dKB cache", cdp->cap.buf_size);
if (cdp->atp->flags & ATAPI_F_DMA_ENABLED)
printf(", DMA");
printf("\n");
printf("acd%d: supported read types:", cdp->lun);
comma = 0;
if (cdp->cap.read_cdr) {
printf(" CD-R"); comma = 1;
}
if (cdp->cap.read_cdrw) {
printf("%s CD-RW", comma ? "," : ""); comma = 1;
}
if (cdp->cap.cd_da) {
printf("%s CD-DA", comma ? "," : ""); comma = 1;
}
if (cdp->cap.method2)
printf("%s packet track", comma ? "," : "");
if (cdp->cap.write_cdr || cdp->cap.write_cdrw) {
printf("\nacd%d: supported write types:", cdp->lun);
comma = 0;
if (cdp->cap.write_cdr) {
printf(" CD-R" ); comma = 1;
}
if (cdp->cap.write_cdrw) {
printf("%s CD-RW", comma ? "," : ""); comma = 1;
}
if (cdp->cap.test_write) {
printf("%s test write", comma ? "," : ""); comma = 1;
}
}
if (cdp->cap.audio_play) {
printf("\nacd%d: Audio: ", cdp->lun);
if (cdp->cap.audio_play)
printf("play");
if (cdp->cap.max_vol_levels)
printf(", %d volume levels", cdp->cap.max_vol_levels);
}
printf("\nacd%d: Mechanism: ", cdp->lun);
switch (cdp->cap.mech) {
case MST_MECH_CADDY:
mechanism = "caddy"; break;
case MST_MECH_TRAY:
mechanism = "tray"; break;
case MST_MECH_POPUP:
mechanism = "popup"; break;
case MST_MECH_CHANGER:
mechanism = "changer"; break;
case MST_MECH_CARTRIDGE:
mechanism = "cartridge"; break;
default:
mechanism = 0; break;
}
if (mechanism)
printf("%s%s", cdp->cap.eject ? "ejectable " : "", mechanism);
else if (cdp->cap.eject)
printf("ejectable");
if (cdp->cap.mech != MST_MECH_CHANGER) {
printf("\nacd%d: Medium: ", cdp->lun);
switch (cdp->cap.medium_type & MST_TYPE_MASK_HIGH) {
case MST_CDROM:
printf("CD-ROM "); break;
case MST_CDR:
printf("CD-R "); break;
case MST_CDRW:
printf("CD-RW "); break;
case MST_DOOR_OPEN:
printf("door open"); break;
case MST_NO_DISC:
printf("no/blank disc inside"); break;
case MST_FMT_ERROR:
printf("medium format error"); break;
}
if ((cdp->cap.medium_type & MST_TYPE_MASK_HIGH) < MST_TYPE_MASK_HIGH) {
switch (cdp->cap.medium_type & MST_TYPE_MASK_LOW) {
case MST_DATA_120:
printf("120mm data disc loaded"); break;
case MST_AUDIO_120:
printf("120mm audio disc loaded"); break;
case MST_COMB_120:
printf("120mm data/audio disc loaded"); break;
case MST_PHOTO_120:
printf("120mm photo disc loaded"); break;
case MST_DATA_80:
printf("80mm data disc loaded"); break;
case MST_AUDIO_80:
printf("80mm audio disc loaded"); break;
case MST_COMB_80:
printf("80mm data/audio disc loaded"); break;
case MST_PHOTO_80:
printf("80mm photo disc loaded"); break;
case MST_FMT_NONE:
switch (cdp->cap.medium_type & MST_TYPE_MASK_HIGH) {
case MST_CDROM:
printf("unknown medium"); break;
case MST_CDR:
case MST_CDRW:
printf("blank medium"); break;
}
break;
default:
printf("unknown type=0x%x", cdp->cap.medium_type); break;
}
}
}
if (cdp->cap.lock)
printf(cdp->cap.locked ? ", locked" : ", unlocked");
if (cdp->cap.prevent)
printf(", lock protected");
printf("\n");
}
static __inline void
lba2msf(int32_t lba, u_int8_t *m, u_int8_t *s, u_int8_t *f)
{
lba += 150;
lba &= 0xffffff;
*m = lba / (60 * 75);
lba %= (60 * 75);
*s = lba / 75;
*f = lba % 75;
}
static __inline int32_t
msf2lba(u_int8_t m, u_int8_t s, u_int8_t f)
{
return (m * 60 + s) * 75 + f - 150;
}
static int
acdopen(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
{
int32_t lun = dkunit(dev);
struct acd_softc *cdp;
if (lun >= acdnlun || !(cdp = acdtab[lun]))
return ENXIO;
if (!(cdp->flags & F_BOPEN) && !cdp->refcnt) {
acd_lock_device(cdp, 1); /* Prevent user eject */
cdp->flags |= F_LOCKED;
}
if (fmt == S_IFBLK)
cdp->flags |= F_BOPEN;
else
cdp->refcnt++;
if (!(flags & O_NONBLOCK)) {
if (acd_read_toc(cdp)) {
if (!(flags & FWRITE)) {
printf("acd%d: read_toc failed\n", lun);
return EIO;
}
}
}
return 0;
}
static int
acdclose(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
{
int32_t lun = dkunit(dev);
struct acd_softc *cdp;
if (lun >= acdnlun || !(cdp = acdtab[lun]))
return ENXIO;
if (fmt == S_IFBLK)
cdp->flags &= ~F_BOPEN;
else
cdp->refcnt--;
/* Are we the last open ?? */
if (!(cdp->flags & F_BOPEN) && !cdp->refcnt) {
/* Yup, do we need to close any written tracks */
if ((flags & FWRITE) != 0) {
if ((cdp->flags & F_TRACK_PREPED) != 0) {
acd_close_track(cdp);
cdp->flags &= ~(F_TRACK_PREPED | F_TRACK_PREP);
}
}
acd_lock_device(cdp, 0); /* Allow the user eject */
}
cdp->flags &= ~F_LOCKED;
return 0;
}
static int
acdread(dev_t dev, struct uio *uio, int32_t ioflag)
{
return physio(acdstrategy, NULL, dev, 1, minphys, uio);
}
static int
acdwrite(dev_t dev, struct uio *uio, int32_t ioflag)
{
return physio(acdstrategy, NULL, dev, 0, minphys, uio);
}
static int
acdioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
{
int32_t lun = dkunit(dev);
struct acd_softc *cdp = acdtab[lun];
int32_t error = 0;
if (cdp->flags & F_MEDIA_CHANGED)
switch (cmd) {
case CDIOCRESET:
break;
default:
acd_read_toc(cdp);
acd_lock_device(cdp, 1);
cdp->flags |= F_LOCKED;
break;
}
switch (cmd) {
case CDIOCRESUME:
return acd_pause_device(cdp, 1);
case CDIOCPAUSE:
return acd_pause_device(cdp, 0);
case CDIOCSTART:
return acd_start_device(cdp, 1);
case CDIOCSTOP:
return acd_start_device(cdp, 0);
case CDIOCALLOW:
acd_select_slot(cdp);
cdp->flags &= ~F_LOCKED;
return acd_lock_device(cdp, 0);
case CDIOCPREVENT:
acd_select_slot(cdp);
cdp->flags |= F_LOCKED;
return acd_lock_device(cdp, 1);
case CDIOCRESET:
error = suser(p->p_ucred, &p->p_acflag);
if (error)
return error;
return acd_test_unit_ready(cdp);
case CDIOCEJECT:
if ((cdp->flags & F_BOPEN) && cdp->refcnt)
return EBUSY;
return acd_eject(cdp, 0);
case CDIOCCLOSE:
if ((cdp->flags & F_BOPEN) && cdp->refcnt)
return 0;
return acd_eject(cdp, 1);
case CDIOREADTOCHEADER:
if (!cdp->toc.hdr.ending_track)
return EIO;
bcopy(&cdp->toc.hdr, addr, sizeof(cdp->toc.hdr));
break;
case CDIOREADTOCENTRYS:
{
struct ioc_read_toc_entry *te = (struct ioc_read_toc_entry *)addr;
struct toc *toc = &cdp->toc;
struct toc buf;
u_int32_t len;
u_int8_t starting_track = te->starting_track;
if (!cdp->toc.hdr.ending_track)
return EIO;
if (te->data_len < sizeof(toc->tab[0]) ||
(te->data_len % sizeof(toc->tab[0])) != 0 ||
(te->address_format != CD_MSF_FORMAT &&
te->address_format != CD_LBA_FORMAT))
return EINVAL;
if (!starting_track)
starting_track = toc->hdr.starting_track;
else if (starting_track == 170)
starting_track = toc->hdr.ending_track + 1;
else if (starting_track < toc->hdr.starting_track ||
starting_track > toc->hdr.ending_track + 1)
return EINVAL;
len = ((toc->hdr.ending_track + 1 - starting_track) + 1) *
sizeof(toc->tab[0]);
if (te->data_len < len)
len = te->data_len;
if (len > sizeof(toc->tab))
return EINVAL;
if (te->address_format == CD_MSF_FORMAT) {
struct cd_toc_entry *entry;
buf = cdp->toc;
toc = &buf;
entry = toc->tab + (toc->hdr.ending_track + 1 -
toc->hdr.starting_track) + 1;
while (--entry >= toc->tab)
lba2msf(ntohl(entry->addr.lba), &entry->addr.msf.minute,
&entry->addr.msf.second, &entry->addr.msf.frame);
}
return copyout(toc->tab + starting_track - toc->hdr.starting_track,
te->data, len);
}
case CDIOREADTOCENTRY:
{
struct ioc_read_toc_single_entry *te =
(struct ioc_read_toc_single_entry *)addr;
struct toc *toc = &cdp->toc;
struct toc buf;
u_int8_t track = te->track;
if (!cdp->toc.hdr.ending_track)
return EIO;
if (te->address_format != CD_MSF_FORMAT &&
te->address_format != CD_LBA_FORMAT)
return EINVAL;
if (!track)
track = toc->hdr.starting_track;
else if (track == 170)
track = toc->hdr.ending_track + 1;
else if (track < toc->hdr.starting_track ||
track > toc->hdr.ending_track + 1)
return EINVAL;
if (te->address_format == CD_MSF_FORMAT) {
struct cd_toc_entry *entry;
buf = cdp->toc;
toc = &buf;
entry = toc->tab + (track - toc->hdr.starting_track);
lba2msf(ntohl(entry->addr.lba), &entry->addr.msf.minute,
&entry->addr.msf.second, &entry->addr.msf.frame);
}
bcopy(toc->tab + track - toc->hdr.starting_track,
&te->entry, sizeof(struct cd_toc_entry));
}
break;
case CDIOCREADSUBCHANNEL:
{
struct ioc_read_subchannel *args =
(struct ioc_read_subchannel *)addr;
struct cd_sub_channel_info data;
u_int32_t len = args->data_len;
int32_t abslba, rellba;
int8_t ccb[16] = { ATAPI_READ_SUBCHANNEL, 0, 0x40, 1, 0, 0, 0,
sizeof(cdp->subchan)>>8, sizeof(cdp->subchan),
0, 0, 0, 0, 0, 0, 0 };
if (len > sizeof(data) ||
len < sizeof(struct cd_sub_channel_header))
return EINVAL;
if (atapi_queue_cmd(cdp->atp, ccb, &cdp->subchan,
sizeof(cdp->subchan), A_READ, NULL, NULL, NULL))
return EIO;
#ifdef ACD_DEBUG
atapi_dump("acd: subchan", &cdp->subchan, sizeof(cdp->subchan));
#endif
abslba = cdp->subchan.abslba;
rellba = cdp->subchan.rellba;
if (args->address_format == CD_MSF_FORMAT) {
lba2msf(ntohl(abslba),
&data.what.position.absaddr.msf.minute,
&data.what.position.absaddr.msf.second,
&data.what.position.absaddr.msf.frame);
lba2msf(ntohl(rellba),
&data.what.position.reladdr.msf.minute,
&data.what.position.reladdr.msf.second,
&data.what.position.reladdr.msf.frame);
} else {
data.what.position.absaddr.lba = abslba;
data.what.position.reladdr.lba = rellba;
}
data.header.audio_status = cdp->subchan.audio_status;
data.what.position.control = cdp->subchan.control & 0xf;
data.what.position.addr_type = cdp->subchan.control >> 4;
data.what.position.track_number = cdp->subchan.track;
data.what.position.index_number = cdp->subchan.indx;
return copyout(&data, args->data, len);
}
case CDIOCPLAYMSF:
{
struct ioc_play_msf *args = (struct ioc_play_msf *)addr;
int8_t ccb[16] = { ATAPI_PLAY_MSF, 0, 0,
args->start_m, args->start_s, args->start_f,
args->end_m, args->end_s, args->end_f,
0, 0, 0, 0, 0, 0, 0 };
return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
}
case CDIOCPLAYBLOCKS:
{
struct ioc_play_blocks *args = (struct ioc_play_blocks *)addr;
int8_t ccb[16] = { ATAPI_PLAY_BIG, 0,
args->blk>>24, args->blk>>16, args->blk>>8,
args->blk, args->len>>24, args->len>>16,
args->len>>8, args->len,
0, 0, 0, 0, 0, 0 };
return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
}
case CDIOCPLAYTRACKS:
{
struct ioc_play_track *args = (struct ioc_play_track *)addr;
u_int32_t start, len;
int32_t t1, t2;
int8_t ccb[16];
if (!cdp->toc.hdr.ending_track)
return EIO;
if (args->end_track < cdp->toc.hdr.ending_track + 1)
++args->end_track;
if (args->end_track > cdp->toc.hdr.ending_track + 1)
args->end_track = cdp->toc.hdr.ending_track + 1;
t1 = args->start_track - cdp->toc.hdr.starting_track;
t2 = args->end_track - cdp->toc.hdr.starting_track;
if (t1 < 0 || t2 < 0)
return EINVAL;
start = ntohl(cdp->toc.tab[t1].addr.lba);
len = ntohl(cdp->toc.tab[t2].addr.lba) - start;
bzero(ccb, sizeof(ccb));
ccb[0] = ATAPI_PLAY_BIG;
ccb[2] = start>>24;
ccb[3] = start>>16;
ccb[4] = start>>8;
ccb[5] = start;
ccb[6] = len>>24;
ccb[7] = len>>16;
ccb[8] = len>>8;
ccb[9] = len;
return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
}
case CDIOCREADAUDIO:
{
struct ioc_read_audio *args = (struct ioc_read_audio *)addr;
int32_t lba, frames, error = 0;
u_int8_t *buffer, *ubuf = args->buffer;
int8_t ccb[16];
if (!cdp->toc.hdr.ending_track)
return EIO;
if ((frames = args->nframes) < 0)
return EINVAL;
if (args->address_format == CD_LBA_FORMAT)
lba = args->address.lba;
else if (args->address_format == CD_MSF_FORMAT)
lba = msf2lba(args->address.msf.minute,
args->address.msf.second,
args->address.msf.frame);
else
return EINVAL;
#ifndef CD_BUFFER_BLOCKS
#define CD_BUFFER_BLOCKS 13
#endif
if (!(buffer = malloc(CD_BUFFER_BLOCKS * 2352,
M_TEMP,M_NOWAIT)))
return ENOMEM;
bzero(ccb, sizeof(ccb));
while (frames > 0) {
int32_t size;
u_int8_t blocks;
blocks = (frames>CD_BUFFER_BLOCKS) ? CD_BUFFER_BLOCKS : frames;
size = blocks * 2352;
ccb[0] = ATAPI_READ_CD;
ccb[1] = 4;
ccb[2] = lba>>24;
ccb[3] = lba>>16;
ccb[4] = lba>>8;
ccb[5] = lba;
ccb[8] = blocks;
ccb[9] = 0xf0;
if ((error = atapi_queue_cmd(cdp->atp, ccb, buffer, size,
A_READ, NULL, NULL, NULL)))
break;
if ((error = copyout(buffer, ubuf, size)))
break;
ubuf += size;
frames -= blocks;
lba += blocks;
}
free(buffer, M_TEMP);
if (args->address_format == CD_LBA_FORMAT)
args->address.lba = lba;
else if (args->address_format == CD_MSF_FORMAT)
lba2msf(lba, &args->address.msf.minute,
&args->address.msf.second,
&args->address.msf.frame);
return error;
}
case CDIOCGETVOL:
{
struct ioc_vol *arg = (struct ioc_vol *)addr;
if ((error = acd_mode_sense(cdp, ATAPI_CDROM_AUDIO_PAGE,
&cdp->au, sizeof(cdp->au))))
return error;
if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE)
return EIO;
arg->vol[0] = cdp->au.port[0].volume;
arg->vol[1] = cdp->au.port[1].volume;
arg->vol[2] = cdp->au.port[2].volume;
arg->vol[3] = cdp->au.port[3].volume;
}
break;
case CDIOCSETVOL:
{
struct ioc_vol *arg = (struct ioc_vol *)addr;
if ((error = acd_mode_sense(cdp, ATAPI_CDROM_AUDIO_PAGE,
&cdp->au, sizeof(cdp->au))))
return error;
if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE)
return EIO;
if ((error = acd_mode_sense(cdp, ATAPI_CDROM_AUDIO_PAGE_MASK,
&cdp->aumask, sizeof(cdp->aumask))))
return error;
cdp->au.data_length = 0;
cdp->au.port[0].channels = CHANNEL_0;
cdp->au.port[1].channels = CHANNEL_1;
cdp->au.port[0].volume = arg->vol[0] & cdp->aumask.port[0].volume;
cdp->au.port[1].volume = arg->vol[1] & cdp->aumask.port[1].volume;
cdp->au.port[2].volume = arg->vol[2] & cdp->aumask.port[2].volume;
cdp->au.port[3].volume = arg->vol[3] & cdp->aumask.port[3].volume;
return acd_mode_select(cdp, &cdp->au, sizeof(cdp->au));
}
case CDIOCSETPATCH:
{
struct ioc_patch *arg = (struct ioc_patch *)addr;
return acd_setchan(cdp, arg->patch[0], arg->patch[1],
arg->patch[2], arg->patch[3]);
}
case CDIOCSETMONO:
return acd_setchan(cdp, CHANNEL_0|CHANNEL_1, CHANNEL_0|CHANNEL_1, 0, 0);
case CDIOCSETSTEREO:
return acd_setchan(cdp, CHANNEL_0, CHANNEL_1, 0, 0);
case CDIOCSETMUTE:
return acd_setchan(cdp, 0, 0, 0, 0);
case CDIOCSETLEFT:
return acd_setchan(cdp, CHANNEL_0, CHANNEL_0, 0, 0);
case CDIOCSETRIGHT:
return acd_setchan(cdp, CHANNEL_1, CHANNEL_1, 0, 0);
case CDRIOCNEXTWRITEABLEADDR:
{
struct acd_track_info track_info;
if ((error = acd_read_track_info(cdp, 0xff, &track_info)))
break;
if (!track_info.nwa_valid)
return EINVAL;
cdp->next_writeable_lba = track_info.next_writeable_addr;
*(int*)addr = track_info.next_writeable_addr;
}
break;
case WORMIOCPREPDISK:
{
struct wormio_prepare_disk *w = (struct wormio_prepare_disk *)addr;
if (w->dummy != 0 && w->dummy != 1)
error = EINVAL;
else {
error = acd_open_disk(cdp, w->dummy);
if (error == 0) {
cdp->flags |= F_DISK_PREPED;
cdp->dummy = w->dummy;
cdp->speed = w->speed;
}
}
}
break;
case WORMIOCPREPTRACK:
{
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 ((cdp->flags & F_DISK_PREPED) == 0) {
error = EINVAL;
printf("acd%d: sequence error (PREP_TRACK)\n", cdp->lun);
} else {
cdp->flags |= F_TRACK_PREP;
cdp->preptrack = *w;
}
}
break;
case WORMIOCFINISHTRACK:
if ((cdp->flags & F_TRACK_PREPED) != 0)
error = acd_close_track(cdp);
cdp->flags &= ~(F_TRACK_PREPED | F_TRACK_PREP);
break;
case WORMIOCFIXATION:
{
struct wormio_fixation *w =
(struct wormio_fixation *)addr;
if ((cdp->flags & F_WRITTEN) == 0)
error = EINVAL;
else if (w->toc_type < 0 /* WORM_TOC_TYPE_AUDIO */ ||
w->toc_type > 4 /* WORM_TOC_TYPE_CDI */ )
error = EINVAL;
else if (w->onp != 0 && w->onp != 1)
error = EINVAL;
else {
/* no fixation needed if dummy write */
if (cdp->dummy == 0)
error = acd_close_disk(cdp);
cdp->flags &=
~(F_WRITTEN|F_DISK_PREPED|F_TRACK_PREP|F_TRACK_PREPED);
}
}
break;
case CDRIOCBLANK:
return acd_blank_disk(cdp);
default:
return ENOTTY;
}
return error;
}
static void
acdstrategy(struct buf *bp)
{
int32_t lun = dkunit(bp->b_dev);
struct acd_softc *cdp = acdtab[lun];
int32_t x;
#ifdef NOTYET
/* allow write only on CD-R/RW media */ /* all for now SOS */
if (!(bp->b_flags & B_READ) && !(writeable_media)) {
bp->b_error = EROFS;
bp->b_flags |= B_ERROR;
biodone(bp);
return;
}
#endif
if (bp->b_bcount == 0) {
bp->b_resid = 0;
biodone(bp);
return;
}
/* check for valid blocksize SOS */
bp->b_pblkno = bp->b_blkno;
bp->b_resid = bp->b_bcount;
x = splbio();
bufqdisksort(&cdp->buf_queue, bp);
acd_start(cdp);
splx(x);
}
static void
acd_start(struct acd_softc *cdp)
{
struct buf *bp = bufq_first(&cdp->buf_queue);
u_int32_t lba, count;
int8_t ccb[16];
if (!bp)
return;
bufq_remove(&cdp->buf_queue, bp);
/* Should reject all queued entries if media have changed. */
if (cdp->flags & F_MEDIA_CHANGED) {
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
biodone(bp);
return;
}
acd_select_slot(cdp);
if ((bp->b_flags & B_READ) == B_WRITE) {
if ((cdp->flags & F_TRACK_PREPED) == 0) {
if ((cdp->flags & F_TRACK_PREP) == 0) {
printf("acd%d: sequence error\n", cdp->lun);
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
biodone(bp);
return;
} else {
if (acd_open_track(cdp, &cdp->preptrack) != 0) {
biodone(bp);
return;
}
cdp->flags |= F_TRACK_PREPED;
}
}
}
bzero(ccb, sizeof(ccb));
if (bp->b_flags & B_READ) {
lba = bp->b_blkno / (cdp->block_size / DEV_BSIZE);
ccb[0] = ATAPI_READ_BIG;
}
else {
lba = cdp->next_writeable_lba + (bp->b_offset / cdp->block_size);
ccb[0] = ATAPI_WRITE_BIG;
}
count = (bp->b_bcount + (cdp->block_size - 1)) / cdp->block_size;
#ifdef ACD_DEBUG
printf("acd%d: lba=%d, count=%d\n", cdp->lun, lba, count);
#endif
ccb[1] = 0;
ccb[2] = lba>>24;
ccb[3] = lba>>16;
ccb[4] = lba>>8;
ccb[5] = lba;
ccb[7] = count>>8;
ccb[8] = count;
devstat_start_transaction(cdp->stats);
atapi_queue_cmd(cdp->atp, ccb, bp->b_data, bp->b_bcount,
(bp->b_flags&B_READ)?A_READ : 0, acd_done, cdp, (void *)bp);
}
static void
acd_done(struct atapi_request *request)
{
struct buf *bp = request->bp;
struct acd_softc *cdp = request->driver;
devstat_end_transaction(cdp->stats, bp->b_bcount-request->bytecount,
DEVSTAT_TAG_NONE,
(bp->b_flags&B_READ) ? DEVSTAT_READ:DEVSTAT_WRITE);
if (request->result) {
atapi_error(request->device, request->result);
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
}
else {
bp->b_resid = request->bytecount;
if ((bp->b_flags & B_READ) == B_WRITE)
cdp->flags |= F_WRITTEN;
}
biodone(bp);
acd_start(cdp);
}
static int32_t
acd_test_unit_ready(struct acd_softc *cdp)
{
int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
}
static int32_t
acd_lock_device(struct acd_softc *cdp, int32_t lock)
{
int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
}
static int32_t
acd_start_device(struct acd_softc *cdp, int32_t start)
{
int8_t ccb[16] = { ATAPI_START_STOP, 0, 0, 0, start,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
}
static int32_t
acd_pause_device(struct acd_softc *cdp, int32_t pause)
{
int8_t ccb[16] = { ATAPI_START_STOP, 0, 0, 0, 0, 0, 0, 0, pause,
0, 0, 0, 0, 0, 0, 0 };
return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
}
static int32_t
acd_mode_sense(struct acd_softc *cdp, u_int8_t page,
void *pagebuf, int32_t pagesize)
{
int32_t error;
int8_t ccb[16] = { ATAPI_MODE_SENSE, 0, page, 0, 0, 0, 0,
pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 };
error = atapi_queue_cmd(cdp->atp, ccb, pagebuf, pagesize, A_READ,
NULL, NULL, NULL);
#ifdef ACD_DEBUG
atapi_dump("acd: mode sense ", pagebuf, pagesize);
#endif
return error;
}
static int32_t
acd_mode_select(struct acd_softc *cdp, void *pagebuf, int32_t pagesize)
{
int8_t ccb[16] = { ATAPI_MODE_SELECT, 0x10, 0, 0, 0, 0, 0,
pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 };
#ifdef ACD_DEBUG
printf("acd: modeselect pagesize=%d\n", pagesize);
atapi_dump("acd: mode select ", pagebuf, pagesize);
#endif
return atapi_queue_cmd(cdp->atp, ccb, pagebuf, pagesize, 0,
NULL, NULL, NULL);
}
static int32_t
acd_read_toc(struct acd_softc *cdp)
{
int32_t error, ntracks, len;
int8_t ccb[16];
bzero(&cdp->toc, sizeof(cdp->toc));
bzero(&cdp->info, sizeof(cdp->info));
bzero(ccb, sizeof(ccb));
acd_select_slot(cdp);
error = acd_test_unit_ready(cdp);
if ((error & ATAPI_SK_MASK) == ATAPI_SK_UNIT_ATTENTION) {
cdp->flags |= F_MEDIA_CHANGED;
cdp->flags &= ~(F_WRITTEN | F_TRACK_PREP | F_TRACK_PREPED);
error = acd_test_unit_ready(cdp);
}
if (error) {
atapi_error(cdp->atp, error);
return EIO;
}
cdp->flags &= ~F_MEDIA_CHANGED;
len = sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry);
ccb[0] = ATAPI_READ_TOC;
ccb[7] = len>>8;
ccb[8] = len;
if (atapi_queue_cmd(cdp->atp, ccb, &cdp->toc, len, A_READ, NULL,NULL,NULL)){
bzero(&cdp->toc, sizeof(cdp->toc));
return 0;
}
ntracks = cdp->toc.hdr.ending_track - cdp->toc.hdr.starting_track + 1;
if (ntracks <= 0 || ntracks > MAXTRK) {
bzero(&cdp->toc, sizeof(cdp->toc));
return 0;
}
len = sizeof(struct ioc_toc_header) + ntracks * sizeof(struct cd_toc_entry);
bzero(ccb, sizeof(ccb));
ccb[0] = ATAPI_READ_TOC;
ccb[7] = len>>8;
ccb[8] = len;
if (atapi_queue_cmd(cdp->atp, ccb, &cdp->toc, len, A_READ, NULL,NULL,NULL)){
bzero(&cdp->toc, sizeof(cdp->toc));
return 0;
}
cdp->toc.hdr.len = ntohs(cdp->toc.hdr.len);
bzero(ccb, sizeof(ccb));
ccb[0] = ATAPI_READ_CAPACITY;
if (atapi_queue_cmd(cdp->atp, ccb, &cdp->info, sizeof(cdp->info),
A_READ, NULL, NULL, NULL))
bzero(&cdp->info, sizeof(cdp->info));
cdp->toc.tab[ntracks].control = cdp->toc.tab[ntracks - 1].control;
cdp->toc.tab[ntracks].addr_type = cdp->toc.tab[ntracks - 1].addr_type;
cdp->toc.tab[ntracks].track = 170;
cdp->toc.tab[ntracks].addr.lba = cdp->info.volsize;
cdp->info.volsize = ntohl(cdp->info.volsize);
cdp->info.blksize = ntohl(cdp->info.blksize);
#ifdef ACD_DEBUG
if (cdp->info.volsize && cdp->toc.hdr.ending_track) {
printf("acd%d: ", cdp->lun);
if (cdp->toc.tab[0].control & 4)
printf("%dMB ", cdp->info.volsize / 512);
else
printf("%d:%d audio ", cdp->info.volsize / 75 / 60,
cdp->info.volsize / 75 % 60);
printf("(%d sectors (%d bytes)), %d tracks\n",
cdp->info.volsize, cdp->info.blksize,
cdp->toc.hdr.ending_track - cdp->toc.hdr.starting_track + 1);
}
#endif
return 0;
}
static int32_t
acd_setchan(struct acd_softc *cdp,
u_int8_t c0, u_int8_t c1, u_int8_t c2, u_int8_t c3)
{
int32_t error;
if ((error = acd_mode_sense(cdp, ATAPI_CDROM_AUDIO_PAGE, &cdp->au,
sizeof(cdp->au))))
return error;
if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE)
return EIO;
cdp->au.data_length = 0;
cdp->au.port[0].channels = c0;
cdp->au.port[1].channels = c1;
cdp->au.port[2].channels = c2;
cdp->au.port[3].channels = c3;
return acd_mode_select(cdp, &cdp->au, sizeof(cdp->au));
}
static int32_t
acd_eject(struct acd_softc *cdp, int32_t close)
{
int32_t error;
acd_select_slot(cdp);
error = acd_start_device(cdp, 0);
if ((error & ATAPI_SK_MASK) &&
((error & ATAPI_SK_MASK) == ATAPI_SK_NOT_READY ||
(error & ATAPI_SK_MASK) == ATAPI_SK_UNIT_ATTENTION)) {
if (!close)
return 0;
if ((error = acd_start_device(cdp, 3)))
return error;
acd_read_toc(cdp);
acd_lock_device(cdp, 1);
cdp->flags |= F_LOCKED;
return 0;
}
if (error) {
atapi_error(cdp->atp, error);
return EIO;
}
if (close)
return 0;
tsleep((caddr_t) &lbolt, PRIBIO, "acdej1", 0);
tsleep((caddr_t) &lbolt, PRIBIO, "acdej2", 0);
acd_lock_device(cdp, 0);
cdp->flags &= ~F_LOCKED;
cdp->flags |= F_MEDIA_CHANGED;
cdp->flags &= ~(F_WRITTEN|F_TRACK_PREP|F_TRACK_PREPED);
return acd_start_device(cdp, 2);
}
static void
acd_select_slot(struct acd_softc *cdp)
{
int8_t ccb[16];
if (cdp->slot < 0 || cdp->changer_info->current_slot == cdp->slot)
return;
/* Unlock (might not be needed but its cheaper than asking) */
acd_lock_device(cdp, 0);
bzero(ccb, sizeof(ccb));
/* Unload the current media from player */
ccb[0] = ATAPI_LOAD_UNLOAD;
ccb[4] = 2;
ccb[8] = cdp->changer_info->current_slot;
atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
/* load the wanted slot */
ccb[0] = ATAPI_LOAD_UNLOAD;
ccb[4] = 3;
ccb[8] = cdp->slot;
atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
cdp->changer_info->current_slot = cdp->slot;
/* Lock the media if needed */
if (cdp->flags & F_LOCKED)
acd_lock_device(cdp, 1);
}
static int32_t
acd_open_disk(struct acd_softc *cdp, int32_t test)
{
cdp->next_writeable_lba = 0;
return 0;
}
static int32_t
acd_close_disk(struct acd_softc *cdp)
{
int8_t ccb[16];
bzero(ccb, sizeof(ccb));
ccb[0] = ATAPI_CLOSE_TRACK;
ccb[2] = 2;
ccb[5] = 0; /* track to close (0 = last open) */
return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
}
static int32_t
acd_open_track(struct acd_softc *cdp, struct wormio_prepare_track *ptp)
{
struct write_param param;
int32_t error;
if ((error = acd_mode_sense(cdp, ATAPI_CDROM_WRITE_PARAMETERS_PAGE,
&param, sizeof(param))))
return error;
param.page_code = 0x05;
param.page_length = 0x32;
param.test_write = cdp->dummy ? 1 : 0;
param.write_type = CDR_WTYPE_TRACK;
switch (ptp->audio) {
/* switch (data_type) { */
case 0:
/* case CDR_DATA: */
cdp->block_size = 2048;
param.track_mode = CDR_TMODE_DATA;
param.data_block_type = CDR_DB_ROM_MODE1;
param.session_format = CDR_SESS_CDROM;
break;
default:
/* case CDR_AUDIO: */
cdp->block_size = 2352;
if (ptp->preemp)
param.track_mode = CDR_TMODE_AUDIO;
else
param.track_mode = 0;
param.data_block_type = CDR_DB_RAW;
param.session_format = CDR_SESS_CDROM;
break;
/*
case CDR_MODE2:
param.track_mode = CDR_TMODE_DATA;
param.data_block_type = CDR_DB_ROM_MODE2;
param.session_format = CDR_SESS_CDROM;
break;
case CDR_XA1:
param.track_mode = CDR_TMODE_DATA;
param.data_block_type = CDR_DB_XA_MODE1;
param.session_format = CDR_SESS_CDROM_XA;
break;
case CDR_XA2:
param.track_mode = CDR_TMODE_DATA;
param.data_block_type = CDR_DB_XA_MODE2_F1;
param.session_format = CDR_SESS_CDROM_XA;
break;
case CDR_CDI:
param.track_mode = CDR_TMODE_DATA;
param.data_block_type = CDR_DB_XA_MODE2_F1;
param.session_format = CDR_SESS_CDI;
break;
}
*/
}
param.multi_session = CDR_MSES_NONE;
param.fp = 0;
param.packet_size = 0;
return acd_mode_select(cdp, &param, sizeof(param));
}
static int32_t
acd_close_track(struct acd_softc *cdp)
{
int8_t ccb[16];
bzero(ccb, sizeof(ccb));
ccb[0] = ATAPI_SYNCHRONIZE_CACHE;
return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
}
static int32_t
acd_read_track_info(struct acd_softc *cdp,
int32_t lba, struct acd_track_info *info)
{
int32_t error;
int8_t ccb[16] = { ATAPI_READ_TRACK_INFO, 1,
lba>>24, lba>>16, lba>>8, lba,
0,
sizeof(*info)>>8, sizeof(*info),
0, 0, 0, 0, 0, 0, 0 };
if ((error = atapi_queue_cmd(cdp->atp, ccb, info, sizeof(*info),
A_READ, NULL, NULL, NULL)))
return error;
info->track_start_addr = ntohl(info->track_start_addr);
info->next_writeable_addr = ntohl(info->next_writeable_addr);
info->free_blocks = ntohl(info->free_blocks);
info->fixed_packet_size = ntohl(info->fixed_packet_size);
info->track_length = ntohl(info->track_length);
return 0;
}
static int32_t
acd_blank_disk(struct acd_softc *cdp)
{
int32_t error;
int8_t ccb[16];
bzero(ccb, sizeof(ccb));
ccb[0] = ATAPI_BLANK;
ccb[1] = 1;
error = atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
cdp->flags |= F_MEDIA_CHANGED;
cdp->flags &= ~(F_WRITTEN|F_TRACK_PREP|F_TRACK_PREPED);
return error;
}
static void
acd_drvinit(void *unused)
{
static int32_t acd_devsw_installed = 0;
if (!acd_devsw_installed) {
cdevsw_add_generic(BDEV_MAJOR, CDEV_MAJOR, &acd_cdevsw);
acd_devsw_installed = 1;
}
}
SYSINIT(acddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, acd_drvinit, NULL)
#endif /* NATA && NATAPICD */