/** @(#)drv_jvc.c	1.88 07/04/07 Copyright 1997-2007 J. Schilling */
#ifndef lint
static	char sccsid[] =
	"@(#)drv_jvc.c	1.88 07/04/07 Copyright 1997-2007 J. Schilling";
#endif
/*
 *	CDR device implementation for
 *	JVC/TEAC
 *
 *	Copyright (c) 1997-2007 J. Schilling
 */
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file CDDL.Schily.txt in this distribution for details.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file CDDL.Schily.txt from this distribution.
 */
/*#define	XXDEBUG*/
/*#define	XXBUFFER*/

#include <schily/mconfig.h>

#include <stdio.h>
#include <schily/standard.h>
#include <schily/fcntl.h>
#include <schily/errno.h>
#include <schily/string.h>
#include <schily/unistd.h>
#ifdef	XXDEBUG
#include <schily/stdlib.h>
#endif

#include <schily/utypes.h>
#include <schily/btorder.h>
#include <schily/intcvt.h>
#include <schily/schily.h>

#include <scg/scgcmd.h>
#include <scg/scsidefs.h>
#include <scg/scsireg.h>
#include <scg/scsitransp.h>

#include "cdrecord.h"

/* just a hack */
long	lba_addr;
BOOL	last_done;

/*
 * macros for building MSF values from LBA
 */
#define	LBA_MIN(x)	((x)/(60*75))
#define	LBA_SEC(x)	(((x)%(60*75))/75)
#define	LBA_FRM(x)	((x)%75)
#define	MSF_CONV(a)	((((a)%(unsigned)100)/10)*16 + ((a)%(unsigned)10))

extern	int	lverbose;

#if defined(_BIT_FIELDS_LTOH)	/* Intel byteorder */
struct teac_mode_page_21 {		/* teac dummy selection */
		MP_P_CODE;		/* parsave & pagecode */
	Uchar	p_len;			/* 0x01 = 1 Byte */
	Ucbit	dummy		: 2;
	Ucbit	res		: 6;
};
#else
struct teac_mode_page_21 {		/* teac dummy selection */
		MP_P_CODE;		/* parsave & pagecode */
	Uchar	p_len;			/* 0x01 = 1 Byte */
	Ucbit	res		: 6;
	Ucbit	dummy		: 2;
};
#endif

struct teac_mode_page_31 {		/* teac speed selection */
		MP_P_CODE;		/* parsave & pagecode */
	Uchar	p_len;			/* 0x02 = 2 Byte */
	Uchar	speed;
	Uchar	res;
};

struct cdd_52x_mode_data {
	struct scsi_mode_header	header;
	union cdd_pagex	{
		struct teac_mode_page_21	teac_page21;
		struct teac_mode_page_31	teac_page31;
	} pagex;
};

#if defined(_BIT_FIELDS_LTOH)	/* Intel byteorder */

struct pgm_subcode {		/* subcode for progam area */
	Uchar	subcode;
	Ucbit	addr		: 4;
	Ucbit	control		: 4;
	Uchar	track;
	Uchar	index;
};

#else

struct pgm_subcode {		/* subcode for progam area */
	Uchar	subcode;
	Ucbit	control		: 4;
	Ucbit	addr		: 4;
	Uchar	track;
	Uchar	index;
};

#endif

#define	set_pgm_subcode(sp, t, c, a, tr, idx)		(\
			(sp)->subcode = (t),		 \
			(sp)->control = (c),		 \
			(sp)->addr = (a),		 \
			(sp)->track = MSF_CONV(tr),	 \
			(sp)->index = (idx))

#define	SC_P		1	/* Subcode defines pre-gap (Pause)	*/
#define	SC_TR		0	/* Subcode defines track data		*/

#if defined(_BIT_FIELDS_LTOH)	/* Intel byteorder */

typedef struct lin_subcode {	/* subcode for lead in area */
	Ucbit	addr		: 4;
	Ucbit	control		: 4;
	Uchar	track;
	Uchar	msf[3];
} lsc_t;

#else

typedef struct lin_subcode {	/* subcode for lead in area */
	Ucbit	control		: 4;
	Ucbit	addr		: 4;
	Uchar	track;
	Uchar	msf[3];
} lsc_t;

#endif

#define	set_toc_subcode(sp, c, a, tr, bno)				(\
			((lsc_t *)sp)->control = (c),			 \
			((lsc_t *)sp)->addr = (a),			 \
			((lsc_t *)sp)->track = MSF_CONV(tr),		 \
			((lsc_t *)sp)->msf[0] = MSF_CONV(LBA_MIN(bno)),	 \
			((lsc_t *)sp)->msf[1] = MSF_CONV(LBA_SEC(bno)),	 \
			((lsc_t *)sp)->msf[2] = MSF_CONV(LBA_FRM(bno)),	 \
			&((lsc_t *)sp)->msf[3])

#define	set_lin_subcode(sp, c, a, pt, min, sec, frm)			(\
			((lsc_t *)sp)->control = (c),			 \
			((lsc_t *)sp)->addr = (a),			 \
			((lsc_t *)sp)->track = (pt),			 \
			((lsc_t *)sp)->msf[0] = (min),			 \
			((lsc_t *)sp)->msf[1] = (sec),			 \
			((lsc_t *)sp)->msf[2] = (frm),			 \
			&((lsc_t *)sp)->msf[3])

#if defined(_BIT_FIELDS_LTOH)	/* Intel byteorder */

struct upc_subcode {		/* subcode for upc/bar code */
	Uchar	res;
	Ucbit	addr		: 4;
	Ucbit	control		: 4;
	Uchar	upc[13];
};

#else

struct upc_subcode {		/* subcode for upc/bar code */
	Uchar	res;
	Ucbit	control		: 4;
	Ucbit	addr		: 4;
	Uchar	upc[13];
};

#endif

#if defined(_BIT_FIELDS_LTOH)	/* Intel byteorder */

struct isrc_subcode {		/* subcode for ISRC code */
	Uchar	res;
	Ucbit	addr		: 4;
	Ucbit	control		: 4;
	Uchar	isrc[12];
	Uchar	res14;
};

#else

struct isrc_subcode {		/* subcode for ISRC code */
	Uchar	res;
	Ucbit	control		: 4;
	Ucbit	addr		: 4;
	Uchar	isrc[12];
	Uchar	res14;
};

#endif


LOCAL	int	teac_attach		__PR((SCSI *scgp, cdr_t *dp));
LOCAL	int	teac_init		__PR((SCSI *scgp, cdr_t *dp));
LOCAL	int	teac_getdisktype	__PR((SCSI *scgp, cdr_t *dp));
LOCAL	int	speed_select_teac	__PR((SCSI *scgp, cdr_t *dp, int *speedp));
LOCAL	int	select_secsize_teac	__PR((SCSI *scgp, track_t *trackp));
LOCAL	int	next_wr_addr_jvc	__PR((SCSI *scgp, track_t *, long *ap));
LOCAL	int	write_teac_xg1		__PR((SCSI *scgp, caddr_t, long, long, int, BOOL));
LOCAL	int	cdr_write_teac		__PR((SCSI *scgp, caddr_t bp, long sectaddr, long size, int blocks, BOOL islast));
LOCAL	int	open_track_jvc		__PR((SCSI *scgp, cdr_t *dp, track_t *trackp));
LOCAL	int	teac_fixation		__PR((SCSI *scgp, cdr_t *dp, track_t *trackp));
LOCAL	int	close_track_teac	__PR((SCSI *scgp, cdr_t *dp, track_t *trackp));
LOCAL	int	teac_open_session	__PR((SCSI *scgp, cdr_t *dp, track_t *trackp));
LOCAL	int	initsub_teac		__PR((SCSI *scgp, int toctype, int multi));
LOCAL	int	teac_doopc		__PR((SCSI *scgp));
LOCAL	int	teac_opc		__PR((SCSI *scgp, caddr_t, int cnt, int doopc));
LOCAL	int	opt_power_judge		__PR((SCSI *scgp, int judge));
LOCAL	int	clear_subcode		__PR((SCSI *scgp));
LOCAL	int	set_limits		__PR((SCSI *scgp, long lba, long length));
LOCAL	int	set_subcode		__PR((SCSI *scgp, Uchar *subcode_data, int length));
LOCAL	int	read_disk_info_teac	__PR((SCSI *scgp, Uchar *data, int length, int type));
LOCAL	int	teac_freeze		__PR((SCSI *scgp, int bp_flag));
LOCAL	int	teac_wr_pma		__PR((SCSI *scgp));
LOCAL	int	teac_rd_pma		__PR((SCSI *scgp));
LOCAL	int	next_wr_addr_teac	__PR((SCSI *scgp, long start_lba, long last_lba));
LOCAL	int	blank_jvc		__PR((SCSI *scgp, cdr_t *dp, long addr, int blanktype));
LOCAL	int	buf_cap_teac		__PR((SCSI *scgp, long *sp, long *fp));
LOCAL	long	read_peak_buffer_cap_teac __PR((SCSI *scgp));
LOCAL	int	buffer_inquiry_teac	__PR((SCSI *scgp, int fmt));
#ifdef	XXBUFFER
LOCAL	void	check_buffer_teac	__PR((SCSI *scgp));
#endif
#ifdef	XXDEBUG
LOCAL	void	xxtest_teac		__PR((SCSI *scgp));
#endif


cdr_t	cdr_teac_cdr50 = {
	0, 0,
/*	CDR_TAO|CDR_SAO|CDR_SWABAUDIO|CDR_NO_LOLIMIT,*/
	CDR_TAO|CDR_SWABAUDIO|CDR_NO_LOLIMIT,
	CDR_CDRW_ALL,
	WM_TAO,
	2, 4,
	"teac_cdr50",
	"driver for Teac CD-R50S, Teac CD-R55S, JVC XR-W2010, Pinnacle RCD-5020",
	0,
	(dstat_t *)0,
	drive_identify,
	teac_attach,
	teac_init,
	teac_getdisktype,
	no_diskstatus,
	scsi_load,
	scsi_unload,
	buf_cap_teac,
	cmd_dummy,					/* recovery_needed */
	(int(*)__PR((SCSI *, cdr_t *, int)))cmd_dummy,	/* recover	*/
	speed_select_teac,
	select_secsize,
	next_wr_addr_jvc,
	(int(*)__PR((SCSI *, Ulong)))cmd_ill,	/* reserve_track	*/
	cdr_write_teac,
	(int(*)__PR((track_t *, void *, BOOL)))cmd_dummy,	/* gen_cue */
	no_sendcue,
	(int(*)__PR((SCSI *, cdr_t *, track_t *)))cmd_dummy, /* leadin */
	open_track_jvc,
	close_track_teac,
	teac_open_session,
	cmd_dummy,
	cmd_dummy,					/* abort	*/
	read_session_offset_philips,
	teac_fixation,
	cmd_dummy,					/* stats	*/
/*	blank_dummy,*/
	blank_jvc,
	format_dummy,
	teac_opc,
	cmd_dummy,					/* opt1		*/
	cmd_dummy,					/* opt2		*/
};

LOCAL int
teac_init(scgp, dp)
	SCSI	*scgp;
	cdr_t	*dp;
{
	return (speed_select_teac(scgp, dp, NULL));
}

LOCAL int
teac_getdisktype(scgp, dp)
	SCSI	*scgp;
	cdr_t	*dp;
{
	dstat_t	*dsp = dp->cdr_dstat;
	struct scsi_mode_data md;
	int	count = sizeof (struct scsi_mode_header) +
			sizeof (struct scsi_mode_blockdesc);
	int	len;
	int	page = 0;
	long	l;

	fillbytes((caddr_t)&md, sizeof (md), '\0');

	(void) test_unit_ready(scgp);
	if (mode_sense(scgp, (Uchar *)&md, count, page, 0) < 0) {	/* Page n current */
		return (-1);
	} else {
		len = ((struct scsi_mode_header *)&md)->sense_data_len + 1;
	}
	if (((struct scsi_mode_header *)&md)->blockdesc_len < 8)
		return (-1);

	l = a_to_u_3_byte(md.blockdesc.nlblock);
	dsp->ds_maxblocks = l;
	return (drive_getdisktype(scgp, dp));
}

LOCAL int
speed_select_teac(scgp, dp, speedp)
	SCSI	*scgp;
	cdr_t	*dp;
	int	*speedp;
{
	struct cdd_52x_mode_data md;
	int	count;
	int	status;
	int	speed = 1;
	BOOL	dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;

	if (speedp)
		speed = *speedp;

	fillbytes((caddr_t)&md, sizeof (md), '\0');

	count  = sizeof (struct scsi_mode_header) +
		sizeof (struct teac_mode_page_21);

	md.pagex.teac_page21.p_code = 0x21;
	md.pagex.teac_page21.p_len =  0x01;
	md.pagex.teac_page21.dummy = dummy?3:0;

	status = mode_select(scgp, (Uchar *)&md, count, 0, scgp->inq->data_format >= 2);
	if (status < 0)
		return (status);

	if (speedp == 0)
		return (0);

	fillbytes((caddr_t)&md, sizeof (md), '\0');

	count  = sizeof (struct scsi_mode_header) +
		sizeof (struct teac_mode_page_31);

	speed >>= 1;
	md.pagex.teac_page31.p_code = 0x31;
	md.pagex.teac_page31.p_len =  0x02;
	md.pagex.teac_page31.speed = speed;

	return (mode_select(scgp, (Uchar *)&md, count, 0, scgp->inq->data_format >= 2));
}

LOCAL int
select_secsize_teac(scgp, trackp)
	SCSI	*scgp;
	track_t	*trackp;
{
	struct scsi_mode_data md;
	int	count = sizeof (struct scsi_mode_header) +
			sizeof (struct scsi_mode_blockdesc);
	int	len;
	int	page = 0;

	fillbytes((caddr_t)&md, sizeof (md), '\0');

	(void) test_unit_ready(scgp);
	if (mode_sense(scgp, (Uchar *)&md, count, page, 0) < 0) {	/* Page n current */
		return (-1);
	} else {
		len = ((struct scsi_mode_header *)&md)->sense_data_len + 1;
	}
	if (((struct scsi_mode_header *)&md)->blockdesc_len < 8)
		return (-1);

	md.header.sense_data_len = 0;
	md.header.blockdesc_len = 8;

	md.blockdesc.density = 1;
	if (trackp->secsize == 2352)
		md.blockdesc.density = 4;
	i_to_3_byte(md.blockdesc.lblen, trackp->secsize);

	return (mode_select(scgp, (Uchar *)&md, count, 0, scgp->inq->data_format >= 2));
}

LOCAL int
next_wr_addr_jvc(scgp, trackp, ap)
	SCSI	*scgp;
	track_t	*trackp;
	long	*ap;
{
	if (trackp != 0 && trackp->track > 0) {
		*ap = lba_addr;
	} else {
		long	nwa;

		if (read_B0(scgp, TRUE, &nwa, NULL) < 0)
			return (-1);

		*ap = nwa + 150;
	}
	return (0);
}

LOCAL int
write_teac_xg1(scgp, bp, sectaddr, size, blocks, extwr)
	SCSI	*scgp;
	caddr_t	bp;		/* address of buffer */
	long	sectaddr;	/* disk address (sector) to put */
	long	size;		/* number of bytes to transfer */
	int	blocks;		/* sector count */
	BOOL	extwr;		/* is an extended write */
{
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = bp;
	scmd->size = size;
	scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
/*	scmd->flags = SCG_DISRE_ENA;*/
	scmd->cdb_len = SC_G1_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;
	scmd->cdb.g1_cdb.cmd = SC_EWRITE;
	scmd->cdb.g1_cdb.lun = scg_lun(scgp);
	g1_cdbaddr(&scmd->cdb.g1_cdb, sectaddr);
	g1_cdblen(&scmd->cdb.g1_cdb, blocks);
	scmd->cdb.g1_cdb.vu_97 = extwr;

	scgp->cmdname = "write_teac_g1";

	if (scg_cmd(scgp) < 0)
		return (-1);
	return (size - scg_getresid(scgp));
}

LOCAL int
cdr_write_teac(scgp, bp, sectaddr, size, blocks, islast)
	SCSI	*scgp;
	caddr_t	bp;		/* address of buffer */
	long	sectaddr;	/* disk address (sector) to put */
	long	size;		/* number of bytes to transfer */
	int	blocks;		/* sector count */
	BOOL	islast;		/* last write for track */
{
	int	ret;

	if (islast)
		last_done = TRUE;

	ret = write_teac_xg1(scgp, bp, sectaddr, size, blocks, !islast);
	if (ret < 0)
		return (ret);

	lba_addr = sectaddr + blocks;
#ifdef	XXBUFFER
	check_buffer_teac(scgp);
#endif
	return (ret);
}

LOCAL int
open_track_jvc(scgp, dp, trackp)
	SCSI	*scgp;
	cdr_t	*dp;
	track_t	*trackp;
{
	int	status;
	long	blocks;
	long	pregapsize;
	struct	pgm_subcode	sc;

	last_done = FALSE;

	if (select_secsize_teac(scgp, trackp) < 0)
		return (-1);

	status = clear_subcode(scgp);
/*next_wr_addr_teac(scgp);*/
	if (status < 0)
		return (status);

if (trackp->pregapsize != 0) {
	if (lverbose > 1) {
		printf("set_limits(%ld, %ld)-> %ld\n",
		lba_addr, trackp->pregapsize, lba_addr + trackp->pregapsize);
	}

	status = set_limits(scgp, lba_addr, trackp->pregapsize);
	if (status < 0)
		return (status);

	/*
	 * Set pre-gap (pause - index 0)
	 */
	set_pgm_subcode(&sc, SC_P,
			st2mode[trackp->sectype&ST_MASK], ADR_POS, trackp->trackno, 0);

	if (lverbose > 1)
		scg_prbytes("Subcode:", (Uchar *)&sc, sizeof (sc));

	status = set_subcode(scgp, (Uchar *)&sc, sizeof (sc));
	if (status < 0)
		return (status);

	pregapsize = trackp->pregapsize;
	if (!is_audio(trackp)) {
		lba_addr += 5;	/* link & run in blocks */
		pregapsize -= 5;
	}
	if (lverbose > 1) {
		printf("pad_track(%ld, %ld)-> %ld\n",
			lba_addr, pregapsize, lba_addr + pregapsize);
	}
	/*
	 * XXX Do we need to check isecsize too?
	 */
	if (pad_track(scgp, dp, trackp,
			lba_addr, (Llong)pregapsize*trackp->secsize,
			FALSE, (Llong *)0) < 0)
		return (-1);
}

	blocks = trackp->tracksize/trackp->secsize +
		    (trackp->tracksize%trackp->secsize?1:0);
	blocks += trackp->padsecs;
	if (blocks < 300)
		blocks = 300;
	if (!is_audio(trackp))
		blocks += 2;
if (!is_last(trackp) && trackp[1].pregapsize == 0)
		blocks -= 150;

	/*
	 * set the limits for the new subcode - seems to apply to all
	 * of the data track.
	 * Unknown tracksize is handled in open_session.
	 * We definitely need to know the tracksize in this driver.
	 */
	if (lverbose > 1) {
		printf("set_limits(%ld, %ld)-> %ld\n",
			lba_addr, blocks, lba_addr + blocks);
	}
	status = set_limits(scgp, lba_addr, blocks);
	if (status < 0)
		return (status);

	/*
	 * Set track start (index 1)
	 */
	set_pgm_subcode(&sc, SC_TR,
			st2mode[trackp->sectype&ST_MASK], ADR_POS, trackp->trackno, 1);

	if (lverbose > 1)
		scg_prbytes("Subcode:", (Uchar *)&sc, sizeof (sc));

	status = set_subcode(scgp, (Uchar *)&sc, sizeof (sc));
	if (status < 0)
		return (status);

if (!is_last(trackp) && trackp[1].pregapsize == 0) {
	blocks += lba_addr;
	pregapsize = 150;

	if (lverbose > 1) {
		printf("set_limits(%ld, %ld)-> %ld\n",
		blocks, pregapsize, blocks + pregapsize);
	}

	status = set_limits(scgp, blocks, pregapsize);
	if (status < 0)
		return (status);

	/*
	 * Set pre-gap (pause - index 0)
	 */
	trackp++;
	set_pgm_subcode(&sc, SC_P,
			st2mode[trackp->sectype&ST_MASK], ADR_POS, trackp->trackno, 0);

	if (lverbose > 1)
		scg_prbytes("Subcode:", (Uchar *)&sc, sizeof (sc));

	status = set_subcode(scgp, (Uchar *)&sc, sizeof (sc));
	if (status < 0)
		return (status);
}
	return (status);
}

LOCAL	char	sector[3000];

LOCAL int
close_track_teac(scgp, dp, trackp)
	SCSI	*scgp;
	cdr_t	*dp;
	track_t	*trackp;
{
	int	ret = 0;

	if (!last_done) {
		printf("WARNING: adding dummy block to close track.\n");
		/*
		 * need read sector size
		 * XXX do we really need this ?
		 * XXX if we need this can we set blocks to 0 ?
		 */
		ret =  write_teac_xg1(scgp, sector, lba_addr, 2352, 1, FALSE);
		lba_addr++;
	}
	if (!is_audio(trackp))
		lba_addr += 2;
	teac_wr_pma(scgp);
	return (ret);
}



static const char *sd_teac50_error_str[] = {
	"\100\200diagnostic failure on component parts",	/* 40 80 */
	"\100\201diagnostic failure on memories",		/* 40 81 */
	"\100\202diagnostic failure on cd-rom ecc circuit",	/* 40 82 */
	"\100\203diagnostic failure on gate array",		/* 40 83 */
	"\100\204diagnostic failure on internal SCSI controller",	/* 40 84 */
	"\100\205diagnostic failure on servo processor",	/* 40 85 */
	"\100\206diagnostic failure on program rom",		/* 40 86 */
	"\100\220thermal sensor failure",			/* 40 90 */
	"\200\000controller prom error",			/* 80 00 */	/* JVC */
	"\201\000no disk present - couldn't get focus",		/* 81 00 */	/* JVC */
	"\202\000no cartridge present",				/* 82 00 */	/* JVC */
	"\203\000unable to spin up",				/* 83 00 */	/* JVC */
	"\204\000addr exceeded the last valid block addr",	/* 84 00 */	/* JVC */
	"\205\000sync error",					/* 85 00 */	/* JVC */
	"\206\000address can't find or not data track",		/* 86 00 */	/* JVC */
	"\207\000missing track",				/* 87 00 */	/* JVC */
	"\213\000cartridge could not be ejected",		/* 8B 00 */	/* JVC */
	"\215\000audio not playing",				/* 8D 00 */	/* JVC */
	"\216\000read toc error",				/* 8E 00 */	/* JVC */
	"\217\000a blank disk is detected by read toc",		/* 8F 00 */
	"\220\000pma less disk - not a recordable disk",	/* 90 00 */
	"\223\000mount error",					/* 93 00 */	/* JVC */
	"\224\000toc less disk",				/* 94 00 */
	"\225\000disc information less disk",			/* 95 00 */	/* JVC */
	"\226\000disc information read error",			/* 96 00 */	/* JVC */
	"\227\000linear velocity measurement error",		/* 97 00 */	/* JVC */
	"\230\000drive sequence stop",				/* 98 00 */	/* JVC */
	"\231\000actuator velocity control error",		/* 99 00 */	/* JVC */
	"\232\000slider velocity control error",		/* 9A 00 */	/* JVC */
	"\233\000opc initialize error",				/* 9B 00 */
	"\233\001power calibration not executed",		/* 9B 01 */
	"\234\000opc execution eror",				/* 9C 00 */
	"\234\001alpc error - opc execution",			/* 9C 01 */
	"\234\002opc execution timeout",			/* 9C 02 */
	"\245\000disk application code does not match host application code",	/* A5 00 */
	"\255\000completed preview write",			/* AD 00 */
	"\256\000invalid B0 value",				/* AE 00 */	/* JVC */
	"\257\000pca area full",				/* AF 00 */
	"\260\000efm isn't detected",				/* B0 00 */	/* JVC */
	"\263\000no logical sector",				/* B3 00 */	/* JVC */
	"\264\000full pma area",				/* B4 00 */
	"\265\000read address is atip area - blank",		/* B5 00 */
	"\266\000write address is efm area - aleady written",	/* B6 00 */
	"\271\000abnormal spinning - servo irq",		/* B9 00 */	/* JVC */
	"\272\000no write data - buffer empty",			/* BA 00 */
	"\273\000write emergency occurred",			/* BB 00 */
	"\274\000read timeout",					/* BC 00 */	/* JVC */
	"\277\000abnormal spin - nmi",				/* BF 00 */	/* JVC */
	"\301\0004th run-in block detected",			/* C1 00 */
	"\302\0003rd run-in block detected",			/* C2 00 */
	"\303\0002nd run-in block detected",			/* C3 00 */
	"\304\0001st run-in block detected",			/* C4 00 */
	"\305\000link block detected",				/* C5 00 */
	"\306\0001st run-out block detected",			/* C6 00 */
	"\307\0002nd run-out block detected",			/* C7 00 */
	"\314\000write request means mixed data mode",		/* CC 00 */
	"\315\000unable to ensure reliable writing with the inserted disk - unsupported disk",	 /* CD 00 */
	"\316\000unable to ensure reliable writing as the inserted disk does not support speed", /* CE 00 */
	"\317\000unable to ensure reliable writing as the inserted disk has no char id code",	 /* CF 00 */
	NULL
};

LOCAL int
teac_attach(scgp, dp)
	SCSI	*scgp;
	cdr_t	*dp;
{
	scg_setnonstderrs(scgp, sd_teac50_error_str);
#ifdef	XXDEBUG
	xxtest_teac(scgp);
	exit(0);
#endif
	return (0);
}

LOCAL int
teac_fixation(scgp, dp, trackp)
	SCSI	*scgp;
	cdr_t	*dp;
	track_t	*trackp;
{
	long	lba;
	int	status;
	Uchar	*sp;
	Uint	i;
extern	char	*buf;

	if (trackp->tracks < 1) {
		/*
		 * We come here if cdrecord isonly called with the -fix option.
		 * As long as we cannot read and interpret the PMA, we must
		 * abort here.
		 */
		teac_rd_pma(scgp);
/*		errmsgno(EX_BAD, "Cannot fixate zero track disk.\n");*/
		errmsgno(EX_BAD, "Cannot fixate without track list (not yet implemented).\n");
		return (-1);
	}
	sp = (Uchar *)buf;

	sleep(1);

	status = clear_subcode(scgp);
	sleep(1);
	if (status < 0)
		return (status);

	sp[0] = 0;		/* reserved */
	sp[1] = 0;		/* reserved */
	sp[2] = 0;		/* Q TNO */

	sp = &sp[3];		/* point past header */

	/*
	 * Set up TOC entries for all tracks
	 */
	for (i = 1; i <= trackp->tracks; i++) {
		lba = trackp[i].trackstart+150;	/* MSF=00:02:00 is LBA=0 */

		sp = set_toc_subcode(sp,
				/* ctrl/adr for this track */
				st2mode[trackp[i].sectype&ST_MASK], ADR_POS,
					trackp[i].trackno, lba);
	}

	/*
	 * Set first track on disk
	 *
	 * XXX We set the track type for the lead-in to the track type
	 * XXX of the first track. The TEAC manual states that we should use
	 * XXX audio if the disk contains both, audio and data tracks.
	 */
	sp = set_lin_subcode(sp,
		/* ctrl/adr for first track */
		st2mode[trackp[1].sectype&ST_MASK], ADR_POS,
		0xA0,				/* Point A0 */
		trackp[1].trackno,		/* first track # */
		toc2sess[track_base(trackp)->tracktype & TOC_MASK],	/* disk type */
		0);				/* reserved */

	/*
	 * Set last track on disk
	 */
	sp = set_lin_subcode(sp,
		/* ctrl/adr for first track */
		st2mode[trackp[1].sectype&ST_MASK], ADR_POS,
		0xA1,				/* Point A1 */
		MSF_CONV(trackp[trackp->tracks].trackno), /* last track # */
		0,				/* reserved */
		0);				/* reserved */

	/*
	 * Set start of lead out area in MSF
	 * MSF=00:02:00 is LBA=0
	 */
	lba = lba_addr + 150;
	if (lverbose > 1)
	printf("lba: %ld lba_addr: %ld\n", lba, lba_addr);

	if (lverbose > 1)
	printf("Lead out start: (%02d:%02d/%02d)\n",
			minutes(lba*2352),
			seconds(lba*2352),
			frames(lba*2352));

	sp = set_lin_subcode(sp,
		/* ctrl/adr for first track */
		st2mode[trackp[1].sectype&ST_MASK], ADR_POS,
		0xA2,				/* Point A2 */
		MSF_CONV(LBA_MIN(lba)),
		MSF_CONV(LBA_SEC(lba)),
		MSF_CONV(LBA_FRM(lba)));

	status = sp - ((Uchar *)buf);
	if (lverbose > 1) {
		printf("Subcode len: %d\n", status);
		scg_prbytes("Subcode:", (Uchar *)buf, status);
	}
	status = set_subcode(scgp, (Uchar *)buf, status);
	sleep(1);
	if (status < 0)
		return (status);

	/*
	 * now write the toc
	 */
	status = teac_freeze(scgp, (track_base(trackp)->tracktype & TOCF_MULTI) == 0);
	return (status);

}

LOCAL int
teac_open_session(scgp, dp, trackp)
	SCSI	*scgp;
	cdr_t	*dp;
	track_t	*trackp;
{
	Uint	i;

	for (i = 1; i <= trackp->tracks; i++) {
		if (trackp[i].tracksize < (tsize_t)0) {
			/*
			 * XXX How about setting the subcode range to infinity.
			 * XXX and correct it in clode track before writing
			 * XXX the PMA?
			 */
			errmsgno(EX_BAD, "Track %d has unknown length.\n", i);
			return (-1);
		}
	}
	return (initsub_teac(scgp, track_base(trackp)->tracktype & TOC_MASK,
				track_base(trackp)->tracktype & TOCF_MULTI));
}

LOCAL int
initsub_teac(scgp, toctype, multi)
	SCSI	*scgp;
	int	toctype;
	int	multi;
{
	int	status;

	scgp->silent++;
	if (read_B0(scgp, TRUE, &lba_addr, NULL) < 0)
		lba_addr = -150;
	scgp->silent--;

	status = clear_subcode(scgp);
	if (status < 0)
		return (status);

	return (0);
}

LOCAL int
teac_doopc(scgp)
	SCSI	*scgp;
{
	int	status;

	if (lverbose) {
		fprintf(stdout, "Judging disk...");
		flush();
	}
	status = opt_power_judge(scgp, 1);
	if (status < 0) {
		printf("\n");
		return (status);
	}
	if (lverbose) {
		fprintf(stdout, "done.\nCalibrating laser...");
		flush();
	}

	status = opt_power_judge(scgp, 0);
	if (lverbose) {
		fprintf(stdout, "done.\n");
	}
	/*
	 * Check for error codes 0xCD ... 0xCF
	 */
	scgp->silent++;
	if (next_wr_addr_teac(scgp, -1, -1) < 0) {
		if (scgp->verbose == 0 && scg_sense_key(scgp) != SC_ILLEGAL_REQUEST)
			scg_printerr(scgp);
	}
	scgp->silent--;
	return (status);
}

LOCAL int
teac_opc(scgp, bp, cnt, doopc)
	SCSI	*scgp;
	caddr_t	bp;
	int	cnt;
	int	doopc;
{
	int	status;
	int	count = 0;

	do {
		status = teac_doopc(scgp);
	} while (++count <= 1 && status < 0);

	return (status);
}

/*--------------------------------------------------------------------------*/
#define	SC_SET_LIMITS		0xb3		/* teac 12 byte command */
#define	SC_SET_SUBCODE		0xc2		/* teac 10 byte command */
#define	SC_READ_PMA		0xc4		/* teac 10 byte command */
#define	SC_READ_DISK_INFO	0xc7		/* teac 10 byte command */
#define	SC_BUFFER_INQUIRY	0xe0		/* teac 12 byte command */

#define	SC_WRITE_PMA		0xe1		/* teac 12 byte command */
#define	SC_FREEZE		0xe3		/* teac 12 byte command */
#define	SC_OPC_EXECUTE		0xec		/* teac 12 byte command */
#define	SC_CLEAR_SUBCODE	0xe4		/* teac 12 byte command */
#define	SC_NEXT_WR_ADDRESS	0xe6		/* teac 12 byte command */

#define	SC_READ_PEAK_BUF_CAP	0xef		/* teac 12 byte command */

/*
 * Optimum power calibration for Teac Drives.
 */
LOCAL int
opt_power_judge(scgp, judge)
	SCSI	*scgp;
	int	judge;
{
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)0;
	scmd->size = 0;
	scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
	scmd->cdb_len = SC_G5_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;
	scmd->timeout = 60;

	scmd->cdb.g5_cdb.cmd = SC_OPC_EXECUTE;
	scmd->cdb.g5_cdb.lun = scg_lun(scgp);
	scmd->cdb.g5_cdb.reladr = judge; /* Judge the Disc */

	scgp->cmdname = "opt_power_judge";

	return (scg_cmd(scgp));
}

/*
 * Clear subcodes for Teac Drives.
 */
LOCAL int
clear_subcode(scgp)
	SCSI	*scgp;
{
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)0;
	scmd->size = 0;
	scmd->flags = SCG_DISRE_ENA;
	scmd->cdb_len = SC_G5_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g5_cdb.cmd = SC_CLEAR_SUBCODE;
	scmd->cdb.g5_cdb.lun = scg_lun(scgp);
	scmd->cdb.g5_cdb.addr[3] = 0x80;

	scgp->cmdname = "clear subcode";

	return (scg_cmd(scgp));
}

/*
 * Set limits for command linking for Teac Drives.
 */
LOCAL int
set_limits(scgp, lba, length)
	SCSI	*scgp;
	long	lba;
	long	length;
{
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)0;
	scmd->size = 0;
	scmd->flags = SCG_DISRE_ENA;
	scmd->cdb_len = SC_G5_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g5_cdb.cmd = SC_SET_LIMITS;
	scmd->cdb.g5_cdb.lun = scg_lun(scgp);
	i_to_4_byte(&scmd->cdb.g5_cdb.addr[0], lba);
	i_to_4_byte(&scmd->cdb.g5_cdb.count[0], length);

	scgp->cmdname = "set limits";

	return (scg_cmd(scgp));
}

/*
 * Set subcode for Teac Drives.
 */
LOCAL int
set_subcode(scgp, subcode_data, length)
	SCSI	*scgp;
	Uchar	*subcode_data;
	int	length;
{
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)subcode_data;
	scmd->size = length;
	scmd->flags = SCG_DISRE_ENA;
	scmd->cdb_len = SC_G1_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g1_cdb.cmd = SC_SET_SUBCODE;
	scmd->cdb.g1_cdb.lun = scg_lun(scgp);
	g1_cdblen(&scmd->cdb.g1_cdb, length);

	scgp->cmdname = "set subcode";

	return (scg_cmd(scgp));
}

LOCAL int
read_disk_info_teac(scgp, data, length, type)
	SCSI	*scgp;
	Uchar	*data;
	int	length;
	int	type;
{
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)data;
	scmd->size = length;
	scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
	scmd->cdb_len = SC_G1_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g1_cdb.cmd = SC_READ_DISK_INFO;
	scmd->cdb.g1_cdb.lun = scg_lun(scgp);

	scmd->cdb.g1_cdb.reladr = type & 1;
	scmd->cdb.g1_cdb.res    = (type & 2) >> 1;

	scgp->cmdname = "read disk info teac";

	return (scg_cmd(scgp));
}

/*
 * Perform the freeze command for Teac Drives.
 */
LOCAL int
teac_freeze(scgp, bp_flag)
	SCSI	*scgp;
	int	bp_flag;
{
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)0;
	scmd->size = 0;
	scmd->flags = SCG_DISRE_ENA;
	scmd->cdb_len = SC_G5_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;
	scmd->timeout = 8 * 60;		/* Needs up to 4 minutes */

	scmd->cdb.g5_cdb.cmd = SC_FREEZE;
	scmd->cdb.g5_cdb.lun = scg_lun(scgp);
	scmd->cdb.g5_cdb.addr[3] = bp_flag ? 0x80 : 0;

	scgp->cmdname = "teac_freeze";

	return (scg_cmd(scgp));
}

LOCAL int
teac_wr_pma(scgp)
	SCSI	*scgp;
{
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)0;
	scmd->size = 0;
	scmd->flags = SCG_DISRE_ENA;
	scmd->cdb_len = SC_G5_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g5_cdb.cmd = SC_WRITE_PMA;
	scmd->cdb.g5_cdb.lun = scg_lun(scgp);

	scgp->cmdname = "teac_write_pma";

	return (scg_cmd(scgp));
}

/*
 * Read PMA for Teac Drives.
 */
LOCAL int
teac_rd_pma(scgp)
	SCSI	*scgp;
{
	unsigned char	xx[256];
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)xx, sizeof (xx), '\0');
	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)xx;
	scmd->size = sizeof (xx);
	scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
	scmd->cdb_len = SC_G1_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g1_cdb.cmd = SC_READ_PMA;
	scmd->cdb.g1_cdb.lun = scg_lun(scgp);

	g1_cdblen(&scmd->cdb.g1_cdb, sizeof (xx));

	scgp->cmdname = "teac_read_pma";

/*	return (scg_cmd(scgp));*/
	if (scg_cmd(scgp) < 0)
		return (-1);

	if (scgp->verbose) {
		scg_prbytes("PMA Data", xx, sizeof (xx) - scg_getresid(scgp));
	}
	if (lverbose) {
		unsigned i;
		Uchar	*p;

		scg_prbytes("PMA Header: ", xx, 4);
		i = xx[2];
		p = &xx[4];
		for (; i <= xx[3]; i++) {
			scg_prbytes("PMA: ", p, 10);
			p += 10;
		}
	}
	return (0);
}

/*
 * Next writable address for Teac Drives.
 */
LOCAL int
next_wr_addr_teac(scgp, start_lba, last_lba)
	SCSI	*scgp;
	long	start_lba;
	long	last_lba;
{
	unsigned char	xx[256];
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)xx, sizeof (xx), '\0');
	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)xx;
	scmd->size = sizeof (xx);
	scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
	scmd->cdb_len = SC_G5_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g5_cdb.cmd = SC_NEXT_WR_ADDRESS;
	scmd->cdb.g5_cdb.lun = scg_lun(scgp);

	i_to_4_byte(&scmd->cdb.g5_cdb.addr[0], start_lba);
	i_to_4_byte(&scmd->cdb.g5_cdb.count[0], last_lba);

	if (scgp->verbose)
		printf("start lba: %ld last lba: %ld\n",
					start_lba, last_lba);

	scgp->cmdname = "next writable address";

/*	return (scg_cmd(scgp));*/
	if (scg_cmd(scgp) < 0)
		return (-1);

	if (scgp->verbose) {
		scg_prbytes("WRa Data", xx, sizeof (xx) - scg_getresid(scgp));
		printf("NWA: %ld\n", a_to_4_byte(xx));
	}
	return (0);
}

LOCAL int
blank_jvc(scgp, dp, addr, blanktype)
	SCSI	*scgp;
	cdr_t	*dp;
	long	addr;
	int	blanktype;
{
	extern	char	*blank_types[];

	if (lverbose) {
		printf("Blanking %s\n", blank_types[blanktype & 0x07]);
		flush();
	}

	return (scsi_blank(scgp, addr, blanktype, FALSE));
}

LOCAL int
buf_cap_teac(scgp, sp, fp)
	SCSI	*scgp;
	long	*sp;	/* Size pointer */
	long	*fp;	/* Free pointer */
{
	Ulong	freespace;
	Ulong	bufsize;
	long	ret;
	int	per;

	ret = read_peak_buffer_cap_teac(scgp);
	if (ret < 0)
		return (-1);
	bufsize = ret;
	freespace = 0;
	if (sp)
		*sp = bufsize;
	if (fp)
		*fp = freespace;

	if (scgp->verbose || (sp == 0 && fp == 0))
		printf("BFree: %ld K BSize: %ld K\n", freespace >> 10, bufsize >> 10);

	if (bufsize == 0)
		return (0);
	per = (100 * (bufsize - freespace)) / bufsize;
	if (per < 0)
		return (0);
	if (per > 100)
		return (100);
	return (per);
}

LOCAL long
read_peak_buffer_cap_teac(scgp)
	SCSI	*scgp;
{
	Uchar	xx[4];
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)xx, sizeof (xx), '\0');
	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)xx;
	scmd->size = sizeof (xx);
	scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
	scmd->cdb_len = SC_G5_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g5_cdb.cmd = SC_READ_PEAK_BUF_CAP;
	scmd->cdb.g5_cdb.lun = scg_lun(scgp);

	scgp->cmdname = "read peak buffer capacity";

#define	BDEBUG
#ifndef	BDEBUG
	return (scg_cmd(scgp));
#else
	if (scg_cmd(scgp) < 0)
		return (-1);

	if (scgp->verbose) {
		scg_prbytes("WRa Data", xx, sizeof (xx) - scg_getresid(scgp));
		printf("Buffer cap: %ld\n", a_to_u_3_byte(&xx[1]));
	}
	return (a_to_u_3_byte(&xx[1]));
/*	return (0);*/
#endif
}

#define	BI_ONE_BYTE	0xC0
#define	BI_448_BYTE	0x40
#define	BI_APP_CODE	0x10

LOCAL int
buffer_inquiry_teac(scgp, fmt)
	SCSI	*scgp;
	int	fmt;
{
	Uchar	xx[448];
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)xx, sizeof (xx), '\0');
	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)xx;
	scmd->size = sizeof (xx);
	scmd->size = 448;
	scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
	scmd->cdb_len = SC_G5_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g5_cdb.cmd = SC_BUFFER_INQUIRY;
	scmd->cdb.g5_cdb.lun = scg_lun(scgp);

	if (fmt > 0) {
		scmd->cdb.g5_cdb.addr[3] = fmt;
		if (fmt == BI_ONE_BYTE)
			scmd->size = 1;
	} else {
		scmd->cdb.g5_cdb.addr[3] = BI_448_BYTE;
/*		scmd->cdb.g5_cdb.addr[3] = BI_APP_CODE;*/
	}

	scgp->cmdname = "buffer inquiry";

#define	BDEBUG
#ifndef	BDEBUG
	return (scg_cmd(scgp));
#else
	if (scg_cmd(scgp) < 0)
		return (-1);

	if (scgp->verbose) {
/*		scg_prbytes("WRa Data", xx, sizeof (xx) - scg_getresid(scgp));*/
/*		scg_prbytes("WRa Data", xx, 1);*/

		if (fmt > 0) printf("fmt: %X ", fmt);
		scg_prbytes("WRa Data", xx, 9);
		printf("%d\n", xx[8] - xx[1]);
/*		printf("Buffer cap: %ld\n", a_to_u_3_byte(&xx[1]));*/
	}
	return (0);
#endif
}

#ifdef	XXBUFFER
LOCAL void
check_buffer_teac(scgp)
	SCSI	*scgp;
{
	printf("-------\n");
	buffer_inquiry_teac(scgp, 0);
#ifdef	SL
	usleep(40000);
	buffer_inquiry_teac(scgp, 0);
#endif
	read_peak_buffer_cap_teac(scgp);
}
#endif
/*--------------------------------------------------------------------------*/
#ifdef	XXDEBUG
#include "scsimmc.h"

LOCAL	int	g7_teac			__PR((SCSI *scgp));
LOCAL	int	g6_teac			__PR((SCSI *scgp));

LOCAL int
g7_teac(scgp)
	SCSI	*scgp;
{
	Uchar	xx[2048];
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)xx, sizeof (xx), '\0');
	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)xx;
	scmd->size = sizeof (xx);
	scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
	scmd->cdb_len = SC_G5_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g5_cdb.cmd = 0xDF;
/*	scmd->cdb.g5_cdb.cmd = 0xE5;*/
	scmd->cdb.g5_cdb.lun = scg_lun(scgp);

/*	scmd->cdb.g5_cdb.addr[3] = BI_ONE_BYTE;*/
/*	scmd->size = 1;*/

/*	scmd->cdb.g5_cdb.addr[3] = BI_448_BYTE;*/
/*	scmd->cdb.g5_cdb.addr[3] = BI_APP_CODE;*/

	scgp->cmdname = "g7 teac";

/*	return (scg_cmd(scgp));*/
	if (scg_cmd(scgp) < 0)
		return (-1);

/*	if (scgp->verbose) {*/
		scg_prbytes("WRa Data", xx, sizeof (xx) - scg_getresid(scgp));
/*		scg_prbytes("WRa Data", xx, 1);*/
/*		scg_prbytes("WRa Data", xx, 9);*/
/*printf("%d\n", xx[8] - xx[1]);*/
/*		printf("Buffer cap: %ld\n", a_to_u_3_byte(&xx[1]));*/
/*	}*/
	return (0);
}

LOCAL int
g6_teac(scgp)
	SCSI	*scgp;
{
	Uchar	xx[2048];
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)xx, sizeof (xx), '\0');
	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)xx;
	scmd->size = sizeof (xx);
	scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
	scmd->cdb_len = SC_G1_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;

	scmd->cdb.g1_cdb.cmd = 0xC1;
	scmd->cdb.g1_cdb.cmd = 0xC3;
	scmd->cdb.g1_cdb.cmd = 0xC6;
	scmd->cdb.g1_cdb.cmd = 0xC7;	/* Read TOC */
	scmd->cdb.g1_cdb.cmd = 0xCE;
	scmd->cdb.g1_cdb.cmd = 0xCF;
	scmd->cdb.g1_cdb.cmd = 0xC7;	/* Read TOC */
	scmd->cdb.g1_cdb.lun = scg_lun(scgp);

	scgp->cmdname = "g6 teac";

/*	return (scg_cmd(scgp));*/
	if (scg_cmd(scgp) < 0)
		return (-1);

/*	if (scgp->verbose) {*/
		scg_prbytes("WRa Data", xx, sizeof (xx) - scg_getresid(scgp));
/*		scg_prbytes("WRa Data", xx, 1);*/
/*		scg_prbytes("WRa Data", xx, 9);*/
/*printf("%d\n", xx[8] - xx[1]);*/
/*		printf("Buffer cap: %ld\n", a_to_u_3_byte(&xx[1]));*/
/*	}*/
	return (0);
}

LOCAL void
xxtest_teac(scgp)
	SCSI	*scgp;
{
	read_peak_buffer_cap_teac(scgp);

/*#define	XDI*/
#ifdef	XDI
	{
		Uchar cbuf[512];

/*		read_disk_info_teac(scgp, data, length, type)*/
/*		read_disk_info_teac(scgp, cbuf, 512, 2);*/
/*		read_disk_info_teac(scgp, cbuf, 512, 2);*/
		read_disk_info_teac(scgp, cbuf, 512, 3);
		scg_prbytes("DI Data", cbuf, sizeof (cbuf) - scg_getresid(scgp));
	}
#endif	/* XDI */

	buffer_inquiry_teac(scgp, -1);

/*#define	XBU*/
#ifdef	XBU
	{
		int i;

		for (i = 0; i < 63; i++) {
			scgp->silent++;
			buffer_inquiry_teac(scgp, i<<2);
			scgp->silent--;
		}
	}
#endif	/* XBU */

/*	printf("LLLL\n");*/
/*	g7_teac(scgp);*/
/*	g6_teac(scgp);*/
}
#endif	/* XXDEBUG */
