/* @(#)drv_bd.c	1.21 13/12/10 Copyright 2007-2013 J. Schilling */
#include <schily/mconfig.h>
#ifndef lint
static	UConst char sccsid[] =
	"@(#)drv_bd.c	1.21 13/12/10 Copyright 2007-2013 J. Schilling";
#endif
/*
 *	Copyright (c) 2007-2013 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.
 * A copy of the CDDL is also available via the Internet at
 * http://www.opensource.org/licenses/cddl1.txt
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file CDDL.Schily.txt from this distribution.
 */

/*
 *	BD-R/RE device implementation for
 *	SCSI-3/mmc-3 conforming drives
 *
 * XXX Aktualisieren:
 *		Check recovery		- DUMMY
 *		Load Media		- OK
 *		Get Disktype		- Disk Status & Size (read ATIP -> BD structure)
 *		Check Session ??	- Nicht vorhanden
 *		Check Disk size		- Nach Status & size + Next wr. Addr.
 *		Set Speed/Dummy		- Speed auf BD -> dummy
 *		Open Session		- Set Write Parameter & ??? -> DAO
 *		LOOP
 *			Open Track	- Set Write Parameter ???
 *			Get Next wr. Addr.	-> DUMMY "0" ???
 *			Write Track Data	OK
 *			Close Track	- Flush Cache -> DUMMY
 *		END
 *		Fixate			- Close Track/Session -> Flush Cache
 *		Unload Media		- OK
 *
 *	Verbose levels:
 *			0		silent
 *			1		print laser log & track sizes
 *			2		print disk info & write parameters
 *			3		print log pages & dvd structure
 *
 *	Copyright (c) 2007-2008 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.
 * A copy of the CDDL is also available via the Internet at
 * http://www.opensource.org/licenses/cddl1.txt
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file CDDL.Schily.txt from this distribution.
 */

/*#define	BD_DEBUG*/
#ifndef	DEBUG
#define	DEBUG
#endif
#include <schily/mconfig.h>

#include <schily/stdio.h>
#include <schily/stdlib.h>
#include <schily/unistd.h>	/* Include sys/types.h to make off_t available */
#include <schily/standard.h>
#include <schily/string.h>
#include <schily/time.h>

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

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

#include "scsimmc.h"
#include "scsilog.h"
#include "mmcvendor.h"
#include "cdrecord.h"


extern	char	*driveropts;

extern	int	lverbose;
extern	int	xdebug;

#define	strbeg(s1, s2)	(strstr((s2), (s1)) == (s2))

LOCAL	cdr_t	*identify_bd	__PR((SCSI *scgp, cdr_t *, struct scsi_inquiry *));
LOCAL	int	attach_bd		__PR((SCSI *scgp, cdr_t *));
LOCAL	void	di_to_dstat		__PR((struct disk_info *dip, dstat_t *dsp));
LOCAL	int	init_bd		__PR((SCSI *scgp, cdr_t *dp));
LOCAL	int	getdisktype_bd	__PR((SCSI *scgp, cdr_t *dp));
LOCAL	int	prdiskstatus_bd	__PR((SCSI *scgp, cdr_t *dp));
LOCAL	int	speed_select_bd	__PR((SCSI *scgp, cdr_t *dp, int *speedp));
LOCAL	int	next_wr_addr_bdre	__PR((SCSI *scgp, track_t *trackp, long *ap));
LOCAL	int	next_wr_addr_bdr	__PR((SCSI *scgp, track_t *trackp, long *ap));
LOCAL	int	open_track_bd	__PR((SCSI *scgp, cdr_t *dp, track_t *trackp));
LOCAL	long	rzone_size		__PR((track_t *trackp));
LOCAL	int	close_track_bd	__PR((SCSI *scgp, cdr_t *dp, track_t *trackp));
/*LOCAL	int	open_session_bd	__PR((SCSI *scgp, cdr_t *dp, track_t *trackp));*/
LOCAL	int	fixate_bdre		__PR((SCSI *scgp, cdr_t *dp, track_t *trackp));
LOCAL	int	fixate_bdr		__PR((SCSI *scgp, cdr_t *dp, track_t *trackp));
LOCAL	int	blank_bd		__PR((SCSI *scgp, cdr_t *dp, long addr, int blanktype));
LOCAL	int	format_bd		__PR((SCSI *scgp, cdr_t *dp, int fmtflags));
LOCAL	int	waitformat		__PR((SCSI *scgp, int secs));
LOCAL	int	stats_bd		__PR((SCSI *scgp, cdr_t *dp));

EXPORT	int	format_unit		__PR((SCSI *scgp,
						void *fmt, int length,
						int list_format, int dgdl,
						int interlv, int pattern, int timeout));
LOCAL	void	print_bd_info		__PR((SCSI *_scgp));
LOCAL	void	print_bd00		__PR((Uchar *dp));
LOCAL	void	print_bd09		__PR((Uchar *dp));
LOCAL	void	print_bd0A		__PR((Uchar *dp));

/*
 * SCSI-3/mmc-3 conformant BD-R or BD-RE writer
 * Checks the current medium and then returns either
 * cdr_bdr or cdr_bdre
 */
cdr_t	cdr_bd = {
	0, 0, 0,
	0,
	CDR2_NOCD|CDR2_BD,
	CDR_CDRW_ALL,
	WM_NONE,
	1000, 1000,
	"mmc_bd",
	"generic SCSI-3/mmc-3 BD-R/BD-RE driver (checks media)",
	0,
	(dstat_t *)0,
	identify_bd,
	attach_bd,
	(int(*)__PR((SCSI *scgp, cdr_t *)))cmd_ill,	/* init */
	drive_getdisktype,
	no_diskstatus,
	scsi_load,
	scsi_unload,
	read_buff_cap,
	cmd_dummy,					/* recovery_needed */
	(int(*)__PR((SCSI *, cdr_t *, int)))cmd_dummy,	/* recover	*/
	(int(*)__PR((SCSI *scgp, cdr_t *, int *)))cmd_dummy,		/* Speed */
	select_secsize,
	(int(*)__PR((SCSI *scgp, track_t *, long *)))cmd_ill,	/* next_wr_addr	 */
	(int(*)__PR((SCSI *, Ulong)))cmd_ill,			/* reserve_track */
	scsi_cdr_write,
	(int(*)__PR((track_t *, void *, BOOL)))cmd_dummy,	/* gen_cue */
	no_sendcue,
	(int(*)__PR((SCSI *, cdr_t *, track_t *)))cmd_dummy,		/* leadin */
	(int(*)__PR((SCSI *scgp, cdr_t *, track_t *)))cmd_dummy,	/* open track */
	(int(*)__PR((SCSI *scgp, cdr_t *, track_t *)))cmd_dummy,	/* close track */
	(int(*)__PR((SCSI *scgp, cdr_t *, track_t *)))cmd_dummy,	/* open session */
	cmd_dummy,					/* close session */
	cmd_dummy,					/* abort */
	read_session_offset,
	(int(*)__PR((SCSI *scgp, cdr_t *, track_t *)))cmd_dummy,	/* fixation */
	cmd_dummy,					/* stats	*/
	blank_dummy,
	format_dummy,
	(int(*)__PR((SCSI *, caddr_t, int, int)))NULL,	/* no OPC	*/
	cmd_dummy,					/* opt1		*/
	cmd_dummy,					/* opt2		*/
};

/*
 * SCSI-3/mmc-3 conformant BD-ROM writer
 */
cdr_t	cdr_bdrom = {
	0, 0, 0,
	0,
	CDR2_NOCD|CDR2_BD,
	CDR_CDRW_ALL,
	WM_SAO,
	1000, 1000,
	"mmc_bdrom",
	"generic SCSI-3/mmc-3 BD-ROM driver",
	0,
	(dstat_t *)0,
	identify_bd,
	attach_bd,
	init_bd,
	getdisktype_bd,
	prdiskstatus_bd,
	scsi_load,
	scsi_unload,
	read_buff_cap,
	cmd_dummy,					/* recovery_needed */
	(int(*)__PR((SCSI *, cdr_t *, int)))cmd_dummy,	/* recover	*/
	speed_select_bd,
	select_secsize,
	next_wr_addr_bdr,
	(int(*)__PR((SCSI *, Ulong)))cmd_ill,		/* reserve_track */
	scsi_cdr_write,
	(int(*)__PR((track_t *, void *, BOOL)))cmd_dummy,	/* gen_cue */
	no_sendcue,
	(int(*)__PR((SCSI *, cdr_t *, track_t *)))cmd_ill,	/* leadin */
	(int(*)__PR((SCSI *scgp, cdr_t *, track_t *)))cmd_ill,	/* open track */
	(int(*)__PR((SCSI *scgp, cdr_t *, track_t *)))cmd_ill,	/* close track */
	(int(*)__PR((SCSI *scgp, cdr_t *, track_t *)))cmd_ill,	/* open session */
	cmd_dummy,					/* close session */
	cmd_dummy,					/* abort	*/
	read_session_offset,
	fixate_bdr,
	stats_bd,
	blank_bd,
	format_dummy,
	(int(*)__PR((SCSI *, caddr_t, int, int)))NULL,	/* no OPC	*/
	cmd_dummy,					/* opt1		*/
	cmd_dummy,					/* opt2		*/
};

/*
 * SCSI-3/mmc-3 conformant BD-R writer
 */
cdr_t	cdr_bdr = {
	0, 0, 0,
	0,
	CDR2_NOCD|CDR2_BD,
	CDR_CDRW_ALL,
	WM_SAO,
	1000, 1000,
	"mmc_bdr",
	"generic SCSI-3/mmc-3 BD-R driver",
	0,
	(dstat_t *)0,
	identify_bd,
	attach_bd,
	init_bd,
	getdisktype_bd,
	prdiskstatus_bd,
	scsi_load,
	scsi_unload,
	read_buff_cap,
	cmd_dummy,					/* recovery_needed */
	(int(*)__PR((SCSI *, cdr_t *, int)))cmd_dummy,	/* recover	*/
	speed_select_bd,
	select_secsize,
	next_wr_addr_bdr,
	(int(*)__PR((SCSI *, Ulong)))cmd_ill,		/* reserve_track */
	scsi_cdr_write,
	(int(*)__PR((track_t *, void *, BOOL)))cmd_dummy,	/* gen_cue */
	no_sendcue,
	(int(*)__PR((SCSI *, cdr_t *, track_t *)))cmd_dummy, /* leadin */
	open_track_bd,
	close_track_bd,
	(int(*)__PR((SCSI *scgp, cdr_t *, track_t *)))cmd_dummy,	/* open session */
	cmd_dummy,					/* close session */
	cmd_dummy,					/* abort	*/
	read_session_offset,
	fixate_bdr,
	stats_bd,
	blank_bd,
	format_dummy,
	(int(*)__PR((SCSI *, caddr_t, int, int)))NULL,	/* no OPC	*/
	cmd_dummy,					/* opt1		*/
	cmd_dummy,					/* opt2		*/
};

/*
 * SCSI-3/mmc-3 conformant BD-RE writer
 */
cdr_t	cdr_bdre = {
	0, 0, 0,
	0,
	CDR2_NOCD|CDR2_BD,
	CDR_CDRW_ALL,
	WM_SAO,
	1000, 1000,
	"mmc_bdre",
	"generic SCSI-3/mmc-3 BD-RE driver",
	0,
	(dstat_t *)0,
	identify_bd,
	attach_bd,
	init_bd,
	getdisktype_bd,
	prdiskstatus_bd,
	scsi_load,
	scsi_unload,
	read_buff_cap,
	cmd_dummy,					/* recovery_needed */
	(int(*)__PR((SCSI *, cdr_t *, int)))cmd_dummy,	/* recover	*/
	speed_select_bd,
	select_secsize,
	next_wr_addr_bdre,
	(int(*)__PR((SCSI *, Ulong)))cmd_ill,		/* reserve_track */
	scsi_cdr_write,
	(int(*)__PR((track_t *, void *, BOOL)))cmd_dummy,	/* gen_cue */
	no_sendcue,
	(int(*)__PR((SCSI *, cdr_t *, track_t *)))cmd_dummy, /* leadin */
	open_track_bd,
	close_track_bd,
	(int(*)__PR((SCSI *scgp, cdr_t *, track_t *)))cmd_dummy,	/* open session */
	cmd_dummy,					/* close session */
	cmd_dummy,					/* abort	*/
	read_session_offset,
	fixate_bdre,
	stats_bd,
	blank_bd,
	format_bd,
	(int(*)__PR((SCSI *, caddr_t, int, int)))NULL,	/* no OPC	*/
	cmd_dummy,					/* opt1		*/
	cmd_dummy,					/* opt2		*/
};

LOCAL cdr_t *
identify_bd(scgp, dp, ip)
	SCSI			*scgp;
	cdr_t			*dp;
	struct scsi_inquiry	*ip;
{
	Uchar	mode[0x100];
	struct	cd_mode_page_2A *mp;
	int	profile;

	if (ip->type != INQ_WORM && ip->type != INQ_ROMD)
		return ((cdr_t *)0);

	allow_atapi(scgp, TRUE); /* Try to switch to 10 byte mode cmds */

	scgp->silent++;
	mp = mmc_cap(scgp, mode); /* Get MMC capabilities */
	scgp->silent--;
	if (mp == NULL)
		return (NULL);	/* Pre SCSI-3/mmc drive		*/

	/*
	 * At this point we know that we have a SCSI-3/mmc compliant drive.
	 * Unfortunately ATAPI drives violate the SCSI spec in returning
	 * a response data format of '1' which from the SCSI spec would
	 * tell us not to use the "PF" bit in mode select. As ATAPI drives
	 * require the "PF" bit to be set, we 'correct' the inquiry data.
	 *
	 * XXX xxx_identify() should not have any side_effects ??
	 */
	if (ip->data_format < 2)
		ip->data_format = 2;

	profile = get_curprofile(scgp);
	if (xdebug)
		printf(_("Current profile: 0x%04X\n"), profile);

	if (profile == 0x0043) {
		dp = &cdr_bdre;
	} else if ((profile == 0x0041) || (profile == 0x0042)) {
		dp = &cdr_bdr;
	} else if (profile == 0x0040) {
		dp = &cdr_bdrom;
	} else {
		errmsgno(EX_BAD, _("Found unsupported 0x%X profile.\n"), profile);
		return ((cdr_t *)0);
	}

	return (dp);
}

LOCAL int
attach_bd(scgp, dp)
	SCSI	*scgp;
	cdr_t	*dp;
{
	Uchar	mode[0x100];
	struct	cd_mode_page_2A *mp;
	struct	ricoh_mode_page_30 *rp = NULL;
	Ulong	xspeed;
	Ulong	mp2Aspeed;


	allow_atapi(scgp, TRUE); /* Try to switch to 10 byte mode cmds */

	scgp->silent++;
	mp = mmc_cap(scgp, NULL); /* Get MMC capabilities in allocated mp */
	scgp->silent--;
	if (mp == NULL)
		return (-1);	/* Pre SCSI-3/mmc drive		*/

	dp->cdr_cdcap = mp;	/* Store MMC cap pointer	*/

	/*
	 * XXX hier sollte drive max write speed & drive cur write speed
	 * XXX gesetzt werden.
	 */
	dp->cdr_dstat->ds_dr_max_rspeed = a_to_u_2_byte(mp->max_read_speed)/4495;
	if (dp->cdr_dstat->ds_dr_max_rspeed == 0)
		dp->cdr_dstat->ds_dr_max_rspeed = 14;
	dp->cdr_dstat->ds_dr_cur_rspeed = a_to_u_2_byte(mp->cur_read_speed)/4495;
	if (dp->cdr_dstat->ds_dr_cur_rspeed == 0)
		dp->cdr_dstat->ds_dr_cur_rspeed = 14;

	dp->cdr_dstat->ds_dr_max_wspeed = a_to_u_2_byte(mp->max_write_speed)/4495;
	if (mp->p_len >= 28)
		dp->cdr_dstat->ds_dr_cur_wspeed = a_to_u_2_byte(mp->v3_cur_write_speed)/4495;
	else
		dp->cdr_dstat->ds_dr_cur_wspeed = a_to_u_2_byte(mp->cur_write_speed)/4495;

	/*
	 * NEC drives incorrectly return CD speed values in mode page 2A.
	 * Try MMC3 get performance in hope that values closer to DVD speeds
	 * are always more correct than what is found in mode page 2A.
	 */
	xspeed = 0;
	scsi_get_perf_maxspeed(scgp, NULL, &xspeed, NULL);

	mp2Aspeed = a_to_u_2_byte(mp->max_write_speed);

	if (lverbose > 2) {
		printf(_("max page 2A speed %lu (%lux), max perf speed %lu (%lux)\n"),
			mp2Aspeed, mp2Aspeed/4495,
			xspeed, xspeed/4495);
	}

	if ((is_cdspeed(mp2Aspeed) && !is_cdspeed(xspeed)) ||
	    (mp2Aspeed < 10000 && xspeed > 10000)) {
		dp->cdr_dstat->ds_dr_max_wspeed = xspeed/4495;
		xspeed = 0;
		scsi_get_perf_curspeed(scgp, NULL, &xspeed, NULL);
		if (xspeed > 0)
			dp->cdr_dstat->ds_dr_cur_wspeed = xspeed / 4495;
	}

	if (dp->cdr_speedmax > dp->cdr_dstat->ds_dr_max_wspeed)
		dp->cdr_speedmax = dp->cdr_dstat->ds_dr_max_wspeed;

	if (dp->cdr_speeddef > dp->cdr_speedmax)
		dp->cdr_speeddef = dp->cdr_speedmax;

	rp = get_justlink_ricoh(scgp, mode);

	if (mp->p_len >= 28)
		dp->cdr_flags |= CDR_MMC3;
	if (get_curprofile(scgp) >= 0)
		dp->cdr_flags |= CDR_MMC3;
	if (mp->p_len >= 24)
		dp->cdr_flags |= CDR_MMC2;
	dp->cdr_flags |= CDR_MMC;

	if (mp->loading_type == LT_TRAY)
		dp->cdr_flags |= CDR_TRAYLOAD;
	else if (mp->loading_type == LT_CADDY)
		dp->cdr_flags |= CDR_CADDYLOAD;

	if (mp->BUF != 0)
		dp->cdr_flags |= CDR_BURNFREE;

	check_writemodes_mmc(scgp, dp);
	/*
	 * To avoid that silly people try to call cdrecord will write modes
	 * that are illegal for DVDs, we clear anything that does now work.
	 */
	dp->cdr_flags &= ~(CDR_RAW|CDR_RAW16|CDR_RAW96P|CDR_RAW96R|CDR_SRAW96P|CDR_SRAW96R);
	dp->cdr_flags &= ~(CDR_TAO);

	if (mp->BUF != 0) {
		dp->cdr_flags |= CDR_BURNFREE;
	} else if (rp) {
		if ((dp->cdr_cmdflags & F_DUMMY) && rp->TWBFS && rp->BUEFS)
			dp->cdr_flags |= CDR_BURNFREE;

		if (rp->BUEFS)
			dp->cdr_flags |= CDR_BURNFREE;
	}

	if (rp && rp->AWSCS)
		dp->cdr_flags |= CDR_FORCESPEED;


	if ((dp->cdr_flags & (CDR_SAO)) != (CDR_SAO)) {
		/*
		 * XXX Ist dies ueberhaupt noch notwendig seit wir nicht
		 * XXX mehr CDR_TAO vorgaukeln muessen?
		 *
		 * Das Panasonic DVD-R mag check_writemodes_mmc() nicht
		 * hilft das vielleicht?
		 */
		dp->cdr_flags |= CDR_SAO;
	}

	if (driveropts != NULL) {
		char	*p;

		if (strcmp(driveropts, "help") == 0) {
			mmc_opthelp(scgp, dp, 0);
		}

		p = hasdrvopt(driveropts, "burnfree");
		if (p == NULL)
			p = hasdrvopt(driveropts, "burnproof");
		if (p != NULL && (dp->cdr_flags & CDR_BURNFREE) != 0) {
			if (*p == '1') {
				dp->cdr_dstat->ds_cdrflags |= RF_BURNFREE;
			} else if (*p == '0') {
				dp->cdr_dstat->ds_cdrflags &= ~RF_BURNFREE;
			}
		}

		p = hasdrvopt(driveropts, "forcespeed");
		if (p != NULL && *p == '1' && (dp->cdr_flags & CDR_FORCESPEED) != 0) {
			dp->cdr_dstat->ds_cdrflags |= RF_FORCESPEED;
		}
	}

	/*
	 * Raise the default timeout.
	 * The first write takes a long time as it writes the lead in.
	 */
	if (scgp->deftimeout < 100)
		scg_settimeout(scgp, 100);	/* 1:40			*/

	return (0);
}

LOCAL void
di_to_dstat(dip, dsp)
	struct disk_info	*dip;
	dstat_t	*dsp;
{
	dsp->ds_diskid = a_to_u_4_byte(dip->disk_id);

	dsp->ds_flags |= DSF_NOCD|DSF_BD;	/* This is a BD */

	if (dip->did_v)
		dsp->ds_flags |= DSF_DID_V;
	dsp->ds_disktype = dip->disk_type;
	dsp->ds_diskstat = dip->disk_status;
	dsp->ds_sessstat = dip->sess_status;
	if (dip->erasable)
		dsp->ds_flags |= DSF_ERA;

	dsp->ds_trfirst	   = dip->first_track;
	dsp->ds_trlast	   = dip->last_track_ls;
	dsp->ds_trfirst_ls = dip->first_track_ls;

#ifdef	nono
	/*
	 * On BD systems, there is no lead out start time
	 * in the disk info because there is no time based data.
	 */
	dsp->ds_maxblocks = msf_to_lba(dip->last_lead_out[1],
					dip->last_lead_out[2],
					dip->last_lead_out[3], TRUE);
#endif
}

LOCAL int
init_bd(scgp, dp)
	SCSI	*scgp;
	cdr_t	*dp;
{
	return (speed_select_bd(scgp, dp, NULL));
}

LOCAL int
getdisktype_bd(scgp, dp)
	SCSI	*scgp;
	cdr_t	*dp;
{
extern	char	*buf;
	dstat_t	*dsp = dp->cdr_dstat;
	struct disk_info *dip;
	Uchar	mode[0x100];
	struct rzone_info *rp;
	struct dvd_structure_00 *sp;
	int	len;
	BOOL	did_dummy = FALSE;
	BOOL	did_reload = FALSE;
	int	profile;
	Int32_t	maxblocks;
	Ulong	end_lba;

	if ((dp->cdr_dstat->ds_cdrflags & RF_PRATIP) != 0) {
		if (((dsp->ds_cdrflags & (RF_WRITE|RF_BLANK)) == 0) ||
				lverbose > 1) {
			/*
			 * Das BD Medieninfo ist so lang, daß wir es
			 * beim Schreiben mit -v noch nicht ausgeben.
			 */
			print_bd_info(scgp);
		}
	}

again:
	dip = (struct disk_info *)buf;
	if (get_diskinfo(scgp, dip, sizeof (*dip)) < 0)
		return (-1);

	/*
	 * Check for non writable disk first.
	 */
#ifdef	BD_DEBUG
error(_("DISK STATUS %X\n"), dip->disk_status);
#endif
	if (dip->disk_status == DS_COMPLETE &&
			(dsp->ds_cdrflags & (RF_WRITE|RF_BLANK)) == RF_WRITE) {

		profile = get_curprofile(scgp);
		if (profile == 0x0043) {	/* This is a BD-RE */
			if (dp->cdr_cmdflags & F_FIX)
				return (0);
		}


		if (!did_dummy) {
			int	xspeed = 0xFFFF;
			int	oflags = dp->cdr_cmdflags;

			/*
			 * Try to clear the dummy bit to reset the virtual
			 * drive status. Not all drives support it even though
			 * it is mentioned in the MMC standard.
			 */
			if (lverbose)
				printf(_("Trying to clear drive status.\n"));

			dp->cdr_cmdflags &= ~F_DUMMY;
			speed_select_bd(scgp, dp, &xspeed);
			dp->cdr_cmdflags = oflags;
			scgp->silent++;
			rezero_unit(scgp);
			scgp->silent--;
			did_dummy = TRUE;
			goto again;
		}
		if (!did_reload && profile == 0x0043) {	/* This is a BD-RE */
			scgp->silent++;
			len = read_scsi(scgp, buf, 0, 1);
			scgp->silent--;
			if (len < 0) {
				/*
				 * DVD+RW "complete" but unreadable
				 * Trying to clear drive status did not work...
				 * This happens if the DVD+RW media was erased
				 * by the Ricoh Vendor Unique command
				 */
				reload_media(scgp, dp);
				did_reload = TRUE;
				goto again;
			}
		}
		/*
		 * Trying to clear drive status did not work...
		 * XXX This most likely makes no sense with DVD+R
		 */
/*		reload_media(scgp, dp);*/
	}
	if (get_diskinfo(scgp, dip, sizeof (*dip)) < 0)
		return (-1);
	di_to_dstat(dip, dsp);

#ifdef	BD_DEBUG
error("dtype %d era %d sess stat %d disk stat %d\n",
dip->dtype,
dip->erasable,
dip->sess_status,
dip->disk_status);
error("Dirty %d Format status %d\n", dip->dbit, dip->bg_format_stat);
error("disk type %X\n",
dip->disk_type);
{
	long nwa = -1;

	next_wr_addr_bdr(scgp, (track_t *)NULL, &nwa);
	error("NWA: %ld\n", nwa);
}
#endif


	profile = get_curprofile(scgp);
	if (profile == 0x0043) {
		dsp->ds_flags |= DSF_BD_RE;		/* This is a BD-RE  */
		if (dip->disk_status == DS_EMPTY) {	/* Unformatted	    */
			dsp->ds_flags |= DSF_NEED_FORMAT;
			if ((dp->cdr_dstat->ds_cdrflags & RF_PRATIP) != 0)
				print_format_capacities(scgp);
			return (0);
		}
	} else if (profile == 0x0041 || profile == 0x0042) {
		dsp->ds_flags |= DSF_BD;		/* This is a BD-R */
	}
#ifdef	BD_DEBUG
error("profile %X dsp->ds_flags 0x%X\n", profile, dsp->ds_flags);
#endif

	/*
	 * This information is based on a logical recording zone
	 * and may not always be correct.
	 * XXX Check this if we want to support anything else but
	 * XXX one data track on DAO mode. The current firmware
	 * XXX of the recorder supports only this method but future
	 * XXX releases may support more.
	 */
	fillbytes((caddr_t)mode, sizeof (mode), '\0');
	rp = (struct rzone_info *)mode;
	read_rzone_info(scgp, (caddr_t)rp, sizeof (struct rzone_info));

	if ((dp->cdr_dstat->ds_cdrflags & RF_PRATIP) != 0) {
		if (((dsp->ds_cdrflags & (RF_WRITE|RF_BLANK)) == 0) ||
				lverbose > 1) {
			przone(rp);
			print_format_capacities(scgp);
		}
	}
#define	needed_for_pioneer_a06
#ifdef	needed_for_pioneer_a06
	/*
	 * Old code used with the S101 seems to be needed for DVD+RW
	 * and the Pioneer A06 if the medium is not yet fully formatted.
	 */
	dsp->ds_maxblocks = a_to_u_4_byte(rp->rzone_size);
#ifdef	BD_DEBUG
error("MAXBLO %d from rzone size\n", dsp->ds_maxblocks);
#endif
	if (dsp->ds_maxblocks == 0)
#endif
	dsp->ds_maxblocks = a_to_u_4_byte(rp->free_blocks);
#ifdef	BD_DEBUG
error("MAXBLO %d\n", dsp->ds_maxblocks);
error("MAXBLO %d from free_blocks\n", (int)a_to_u_4_byte(rp->free_blocks));
#endif
	if (rp->nwa_v)
		dsp->ds_maxblocks += a_to_u_4_byte(rp->next_recordable_addr);
#ifdef	BD_DEBUG
error("NWAv %d Next rec addr %d\n", rp->nwa_v, (int)a_to_u_4_byte(rp->next_recordable_addr));
#endif
	maxblocks = dsp->ds_maxblocks;

	/*
	 * XXX this was: if (dip->disk_status == DS_EMPTY)
	 */
	if (dip->disk_status == DS_COMPLETE && profile != 0x0043)
		return (drive_getdisktype(scgp, dp));

	/*
	 * This information is based on the physical pre recorded information.
	 * First try to find the len supported by the actual drive.
	 */
	fillbytes((caddr_t)mode, sizeof (mode), '\0');
	if (read_dvd_structure(scgp, (caddr_t)mode, 2, 1, 0, 0, 0) < 0) {
		errmsgno(EX_BAD, _("Cannot read BD structure.\n"));
		return (drive_getdisktype(scgp, dp));
	}
	len = a_to_u_2_byte(mode);
	len += 2;			/* Data len is not included */

	if (len > sizeof (struct dvd_structure_00)) {
		len = sizeof (struct dvd_structure_00);
		/*
		 * The ACARD TECH AEC-7720 ATAPI<->SCSI adaptor
		 * chokes if we try to transfer odd byte counts (rounds up to
		 * even byte counts and thus causes a DMA overflow and a
		 * bus reset), so make the byte count even.
		 */
		len += 1;
		len &= ~1;
	}
	fillbytes((caddr_t)mode, sizeof (mode), '\0');
	sp = (struct dvd_structure_00 *)mode;
	read_dvd_structure(scgp, (caddr_t)sp, len, 1, 0, 0, 0);
/*	if (lverbose > 1)*/
/*		print_dvd00(sp);*/
	/*
	 * Bei Pioneer ist Phys End ist nur bei dem S101 != 0.
	 * Bei Panasonic ist Phys End == Phys Start.
	 */
	if ((profile != 0x0043) &&
	    (a_to_u_3_byte(sp->phys_end) != 0) &&
			(dsp->ds_maxblocks !=
			(long)(a_to_u_3_byte(sp->phys_end) - a_to_u_3_byte(sp->phys_start) + 1))) {
		printf(_("WARNING: Phys disk size %ld differs from rzone size %ld! Prerecorded disk?\n"),
			(long)(a_to_u_3_byte(sp->phys_end) - a_to_u_3_byte(sp->phys_start) + 1),
			(long)dsp->ds_maxblocks);
		printf(_("WARNING: Phys start: %ld Phys end %ld\n"),
			(long)a_to_u_3_byte(sp->phys_start),
			(long)a_to_u_3_byte(sp->phys_end));
	}
#ifdef	BD_DEBUG
error("MAXBLOx %d\n", dsp->ds_maxblocks);
#endif
	if ((long)dsp->ds_maxblocks == 0) {
		dsp->ds_maxblocks = a_to_u_3_byte(sp->phys_end) - a_to_u_3_byte(sp->phys_start) + 1;
	}
#ifdef	BD_DEBUG
error("MAXBLOx %d\n", dsp->ds_maxblocks);
error("phys start %d phys end %d\n", a_to_u_3_byte(sp->phys_start), a_to_u_3_byte(sp->phys_end));
error("MAXBLO %d from phys end - phys start\n", (int)(a_to_u_3_byte(sp->phys_end) - a_to_u_3_byte(sp->phys_start) + 1));
#endif

	/*
	 * Workaround for some drive media combinations.
	 * At least the drive	'HL-DT-ST' 'BD-RE  BH10LS30 ' '1.00'
	 * does not report maxblocks correctly with any BD media.
	 */
	end_lba = 0L;
	scsi_get_perf_maxspeed(scgp, (Ulong *)NULL, (Ulong *)NULL, &end_lba);
#ifdef	BD_DEBUG
error("end_lba; %lu\n", end_lba);
#endif
	if ((Int32_t)end_lba > dsp->ds_maxblocks) {
		if (maxblocks == 0)
			printf(_("WARNING: Drive returns zero media size, correcting.\n"));
		dsp->ds_maxblocks = end_lba + 1;
	}

#ifdef	BD_DEBUG
error("FINAL MAXBLO %d\n", dsp->ds_maxblocks);
#endif
	return (drive_getdisktype(scgp, dp));
}

LOCAL int
prdiskstatus_bd(scgp, dp)
	SCSI	*scgp;
	cdr_t	*dp;
{
	return (prdiskstatus(scgp, dp, FALSE));
}


LOCAL int
speed_select_bd(scgp, dp, speedp)
	SCSI	*scgp;
	cdr_t	*dp;
	int	*speedp;
{
	Uchar	mode[0x100];
	Uchar	moder[0x100];
	int	len;
	struct	cd_mode_page_05 *mp;
	struct	ricoh_mode_page_30 *rp = NULL;
	int	val;
	Ulong	ul;
	BOOL	forcespeed = FALSE;
	BOOL	dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
	int	curspeed = 1;

	if (speedp)
		curspeed = *speedp;

	fillbytes((caddr_t)mode, sizeof (mode), '\0');

	if (!get_mode_params(scgp, 0x05, _("CD write parameter"),
			mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len))
		return (-1);
	if (len == 0)
		return (-1);

	mp = (struct cd_mode_page_05 *)
		(mode + sizeof (struct scsi_mode_header) +
		((struct scsi_mode_header *)mode)->blockdesc_len);
#ifdef	DEBUG
	if (lverbose > 1)
		scg_prbytes(_("CD write parameter:"), (Uchar *)mode, len);
#endif


	if (get_curprofile(scgp) == 0x0043) {	/* This is a BD-RE */
		if (dummy != 0) {
			errmsgno(EX_BAD, _("BD-RE has no -dummy mode.\n"));
			return (-1);
		}
		if (dp->cdr_cmdflags & F_FIX)
			return (0);
	}
	mp->test_write = dummy != 0;
	/*
	 * Set default values:
	 * Write type = 02 (disk at once)
	 * Track mode = 00 Reserved on Pioneer DVR-S101
	 * Data block type = 00 Reserved on Pioneer DVR-S101
	 * Session format = 00 Reserved on Pioneer DVR-S101
	 * XXX DVR-S101 uses ls_v and link size violating
	 * XXX the current MMC2 spec.
	 */
	mp->write_type = WT_SAO;

#ifdef	DEBUG
	if (lverbose > 1)
		scg_prbytes(_("CD write parameter:"), (Uchar *)mode, len);
#endif
	if (!set_mode_params(scgp, _("CD write parameter"), mode, len, 0, -1)) {
		return (-1);
	}

	/*
	 * Neither set nor get speed.
	 */
	if (speedp == 0)
		return (0);


	rp = get_justlink_ricoh(scgp, moder);
	if ((dp->cdr_flags & CDR_FORCESPEED) != 0) {
		forcespeed = rp && rp->AWSCD != 0;
	}

	if (lverbose && (dp->cdr_flags & CDR_FORCESPEED) != 0)
		printf(_("Forcespeed is %s.\n"), forcespeed?_("ON"):_("OFF"));

	if (!forcespeed && (dp->cdr_dstat->ds_cdrflags & RF_FORCESPEED) != 0) {
		printf(_("Turning forcespeed on\n"));
		forcespeed = TRUE;
	}
	if (forcespeed && (dp->cdr_dstat->ds_cdrflags & RF_FORCESPEED) == 0) {
		printf(_("Turning forcespeed off\n"));
		forcespeed = FALSE;
	}
	if ((dp->cdr_flags & CDR_FORCESPEED) != 0) {

		if (rp) {
			rp->AWSCD = forcespeed?1:0;
			set_mode_params(scgp, _("Ricoh Vendor Page"), moder, moder[0]+1, 0, -1);
			rp = get_justlink_ricoh(scgp, moder);
		}
	}

	/*
	 * BD single speed is 4495.5 kB/s
	 * Rounding down is guaranteed.
	 */
	val = curspeed*4496;
	if (val > 0x7FFFFFFF)
		val = 0x7FFFFFFF;
	if (dp->cdr_flags & CDR_MMC3) {
		if (speed_select_mdvd(scgp, -1, val) < 0)
			errmsgno(EX_BAD, _("MMC-3 speed select did not work.\n"));
	} else {
		if (val > 0xFFFF)
			val = 0xFFFF;
		scgp->silent++;
		if (scsi_set_speed(scgp, -1, val, ROTCTL_CLV) < 0) {
			/*
			 * Don't complain if it does not work,
			 * BD drives may not have speed setting.
			 */
		}
		scgp->silent--;
	}

	scgp->silent++;
	if (scsi_get_speed(scgp, 0, &val) >= 0) {
		if (val > 0) {
			curspeed = val / 4495;
			*speedp = curspeed;
		}
	}
	/*
	 * NEC drives incorrectly return CD speed values in mode page 2A.
	 * Try MMC3 get performance in hope that values closer to BD speeds
	 * are always more correct than what is found in mode page 2A.
	 */
	ul = 0;
	if (scsi_get_perf_curspeed(scgp, NULL, &ul, NULL) >= 0) {
		if (is_cdspeed(val) && !is_cdspeed(ul)) {
			curspeed = ul / 4495;
			*speedp = curspeed;
		}
	}
	scgp->silent--;
	return (0);
}
/*--------------------------------------------------------------------------*/

LOCAL long	dvd_next_addr;

LOCAL int
next_wr_addr_bdre(scgp, trackp, ap)
	SCSI	*scgp;
	track_t	*trackp;
	long	*ap;
{
	if (trackp == 0 || trackp->track <= 1) {
		dvd_next_addr = 0;
	}
	if (ap)
		*ap = dvd_next_addr;
	return (0);
}

LOCAL int
next_wr_addr_bdr(scgp, trackp, ap)
	SCSI	*scgp;
	track_t	*trackp;
	long	*ap;
{
	struct disk_info	di;
	struct rzone_info	rz;
	int			tracks;
	long			next_addr = -1;

	if (trackp == 0) {
		fillbytes((caddr_t)&di, sizeof (di), '\0');
		if (get_diskinfo(scgp, &di, sizeof (di)) < 0)
			return (-1);

		tracks = di.last_track_ls + di.last_track_ls_msb * 256;
		fillbytes((caddr_t)&rz, sizeof (rz), '\0');
		if (get_trackinfo(scgp, (caddr_t)&rz, TI_TYPE_TRACK, tracks, sizeof (rz)) < 0)
			return (-1);
		if (!rz.nwa_v)
			return (-1);
		next_addr = a_to_4_byte(rz.next_recordable_addr);
		if (ap)
			*ap = next_addr;
		return (0);
	}
	if (trackp->track <= 1) {
		/*
		 * XXX This is a workaround for the filesize > 2GB problem.
		 * XXX Check this if we support more than one track DAO
		 * XXX or if we give up this hack in favour of real 64bit
		 * XXX filesize support.
		 */
		fillbytes((caddr_t)&rz, sizeof (rz), '\0');
		read_rzone_info(scgp, (caddr_t)&rz, sizeof (struct rzone_info));
		dvd_next_addr = a_to_4_byte(rz.next_recordable_addr);
#ifdef	BD_DEBUG
error("NWA: %ld valid: %d\n", dvd_next_addr, rz.nwa_v);
#endif
		if (lverbose > 1)
			printf(_("next writable addr: %ld valid: %d\n"), dvd_next_addr, rz.nwa_v);
	}
	if (ap)
		*ap = dvd_next_addr;
	return (0);
}

LOCAL int
open_track_bd(scgp, dp, trackp)
	SCSI	*scgp;
	cdr_t	*dp;
	track_t *trackp;
{
	Uchar	mode[0x100];
	int	len;
	long	sectors;
	struct	cd_mode_page_05 *mp;

	if (trackp->track > 1)	/* XXX Hack to make one 'track' from several */
		return (0);	/* XXX files in Disk at once mode only.	    */

	fillbytes((caddr_t)mode, sizeof (mode), '\0');

	if (!get_mode_params(scgp, 0x05, _("CD write parameter"),
			mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len))
		return (-1);
	if (len == 0)
		return (-1);

	mp = (struct cd_mode_page_05 *)
		(mode + sizeof (struct scsi_mode_header) +
		((struct scsi_mode_header *)mode)->blockdesc_len);

	/*
	 * XXX as long as the Pioneer DVR-S101 only supports a single
	 * XXX data track in DAO mode,
	 * XXX do not set:
	 * XXX track_mode
	 * XXX copy
	 * XXX dbtype
	 *
	 * Track mode = 00 Reserved on Pioneer DVR-S101
	 * Data block type = 00 Reserved on Pioneer DVR-S101
	 * Session format = 00 Reserved on Pioneer DVR-S101
	 * XXX DVR-S101 uses ls_v and link size violating
	 * XXX the current MMC2 spec.
	 */
	/* XXX look into drv_mmc.c for re-integration of above settings */

#ifdef	DEBUG
	if (lverbose > 1)
		scg_prbytes(_("CD write parameter:"), (Uchar *)mode, len);
#endif
	if (!set_mode_params(scgp, _("CD write parameter"), mode, len, 0, trackp->secsize))
		return (-1);

	/*
	 * Compile vitual track list
	 */
	sectors = rzone_size(trackp);
#ifdef	__needed_for_bd__
	if (sectors < 0)
		return (-1);
#endif
#ifdef	__needed_for_bd__
	return (reserve_tr_rzone(scgp, sectors));
#else
	return (0);
#endif
}

/*
 * XXX rzone_size(trackp) muß aufgerufen werden, daher muß
 * XXX auch für DVD+RW eine non-dummy open_track() Funktion da sein.
 */
/*
 * XXX Hack to make one 'track' from several
 * XXX files in Disk at once mode only.
 * XXX Calculate track size and reserve rzone.
 */
LOCAL long
rzone_size(trackp)
	track_t *trackp;	/* Called with &track[1] */
{
	int	i;
	BOOL	vtracks = FALSE;
	long	sectors = 0L;
	Llong	ttrsize = 0L;
	Llong	tamount = 0L;
	Llong	amount;
	long	secsize = trackp->secsize;

	for (i = 0; i < MAX_TRACK; i++) {
		if (is_last(&trackp[i]))
			break;
	}
	if (i >= 1)
		vtracks = TRUE;
	if (vtracks && lverbose)
		printf(_("Compiling virtual track list ...\n"));

	for (i = 0; i < MAX_TRACK; i++) {
		if (trackp[i].tracksize < (tsize_t)0) {
			errmsgno(EX_BAD, _("VTrack %d has unknown length.\n"), i);
			return (-1);
		}
		amount = roundup(trackp[i].tracksize, secsize);
		amount += (Llong)trackp[i].padsecs * secsize;
		sectors += amount/secsize;
		ttrsize += trackp[i].tracksize;
		tamount += amount;
		if (vtracks && lverbose)
			printf(_("Vtrack:  %d size: %lld bytes %lld rounded (%lld sectors)\n"),
				(int)trackp[i].track, (Llong)trackp[i].tracksize,
				amount, amount / (Llong)secsize);

		if (is_last(&trackp[i]))
			break;

		/*
		 * XXX Is it possible for a BD that input sector size
		 * XXX differes from output sector size?
		 * XXX I believe that not.
		 */
		if (trackp[i].tracksize % secsize) {
			comerrno(EX_BAD, _("Virtual track %d is not a multiple of secsize.\n"), (int)trackp[i].track);
		}
	}

	if (vtracks && lverbose)
		printf(_("Vtracks: %d size: %lld bytes %lld rounded (%ld sectors) total\n"),
			i+1, ttrsize, tamount, sectors);

	return (sectors);
}

LOCAL int
close_track_bd(scgp, dp, trackp)
	SCSI	*scgp;
	cdr_t	*dp;
	track_t	*trackp;
{
	long	sectors = 0L;
	Llong	amount;
	long	secsize = trackp->secsize;

	/*
	 * Compute the start of the next "track" for the hack
	 * that allows to have a track in more than one file.
	 * XXX Check this if the vtrack code is removed.
	 */
	amount = roundup(trackp->tracksize, secsize);
	amount += (Llong)trackp->padsecs * secsize;
	sectors += amount/secsize;

	dvd_next_addr += sectors;

	return (0);
}

#ifdef	__needed_for_dvd_plusr__
LOCAL int
open_session_dvd(scgp, dp, trackp)
	SCSI	*scgp;
	cdr_t	*dp;
	track_t	*trackp;
{
	Uchar	mode[0x100];
	Uchar	moder[0x100];
	int	len;
	struct	cd_mode_page_05 *mp;
	struct	ricoh_mode_page_30 *rp = NULL;
	BOOL	burnfree = FALSE;

	fillbytes((caddr_t)mode, sizeof (mode), '\0');

	if (!get_mode_params(scgp, 0x05, _("CD write parameter"),
			mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len))
		return (-1);
	if (len == 0)
		return (-1);

	mp = (struct cd_mode_page_05 *)
		(mode + sizeof (struct scsi_mode_header) +
		((struct scsi_mode_header *)mode)->blockdesc_len);

	/*
	 * XXX as long as the Pioneer DVR-S101 only supports a single
	 * XXX data track in DAO mode,
	 * XXX do not set:
	 * XXX multi_session
	 * XXX sessipon_format
	 *
	 * Track mode = 00 Reserved on Pioneer DVR-S101
	 * Data block type = 00 Reserved on Pioneer DVR-S101
	 * Session format = 00 Reserved on Pioneer DVR-S101
	 * XXX DVR-S101 uses ls_v and link size violating
	 * XXX the current MMC2 spec.
	 */
	/* XXX look into drv_mmc.c for re-integration of above settings */
	mp->write_type = WT_SAO;


	rp = get_justlink_ricoh(scgp, moder);

	if (dp->cdr_cdcap->BUF != 0) {
		burnfree = mp->BUFE != 0;
	} else if ((dp->cdr_flags & CDR_BURNFREE) != 0) {
		burnfree = rp && rp->BUEFE != 0;
	}

	if (lverbose && (dp->cdr_flags & CDR_BURNFREE) != 0)
		printf(_("BURN-Free is %s.\n"), burnfree?_("ON"):_("OFF"));

	if (!burnfree && (dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) != 0) {
		printf(_("Turning BURN-Free on\n"));
		burnfree = TRUE;
	}
	if (burnfree && (dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0) {
		printf(_("Turning BURN-Free off\n"));
		burnfree = FALSE;
	}
	if (dp->cdr_cdcap->BUF != 0) {
		mp->BUFE = burnfree?1:0;
	} else if ((dp->cdr_flags & CDR_BURNFREE) != 0) {

		if (rp)
			rp->BUEFE = burnfree?1:0;
	}
	if (rp) {
		i_to_2_byte(rp->link_counter, 0);
		if (xdebug)
			scg_prbytes(_("Mode Select Data "), moder, moder[0]+1);

		set_mode_params(scgp, _("Ricoh Vendor Page"), moder, moder[0]+1, 0, -1);
		rp = get_justlink_ricoh(scgp, moder);
	}

#ifdef	DEBUG
	if (lverbose > 1)
		scg_prbytes(_("CD write parameter:"), (Uchar *)mode, len);
#endif
	if (!set_mode_params(scgp, _("CD write parameter"), mode, len, 0, -1))
		return (-1);

	return (0);
}
#endif

/*
 * We need to loop here because of a firmware bug in Pioneer DVD writers.
 */
LOCAL int
fixate_bdre(scgp, dp, trackp)
	SCSI	*scgp;
	cdr_t	*dp;
	track_t	*trackp;
{
	int	oldtimeout = scgp->deftimeout;
	int	ret = 0;
	int	i;
#define	MAX_TRIES	15

	/*
	 * XXX Is this timeout needed for BD-RE too?
	 * XXX It was introduced for DVD-R that writes at least 800 MB
	 */
	if (scgp->deftimeout < 1000)
		scg_settimeout(scgp, 1000);

/*scgp->verbose++;*/
	scgp->silent++;
	for (i = 0; i <= MAX_TRIES; i++) {
		if (scsi_flush_cache(scgp, TRUE) < 0) {
			if (!scsi_in_progress(scgp) || i >= MAX_TRIES) {
				if (scgp->verbose <= 0) {
					scg_printerr(scgp);
					scg_printresult(scgp);	/* XXX restore key/code in future */
				}
				printf(_("Trouble flushing the cache\n"));
				scgp->silent--;
				scg_settimeout(scgp, oldtimeout);
				return (-1);
			}
			sleep(1);
		} else {
			break;
		}
	}
	scgp->silent--;
	waitformat(scgp, 300);

#ifdef	__needed__	/* BD-RE does not need (permit) a close session */
	scgp->silent++;
	for (i = 0; i <= MAX_TRIES; i++) {
		if (scsi_close_tr_session(scgp, CL_TYPE_SESSION, 0, TRUE) < 0) {
			if (!scsi_in_progress(scgp) || i >= MAX_TRIES) {
				if (scgp->verbose <= 0) {
					scg_printerr(scgp);
					scg_printresult(scgp);	/* XXX restore key/code in future */
				}
				printf(_("Trouble closing the session\n"));
				break;
			}
			sleep(1);
		} else {
			break;
		}
	}
	scgp->silent--;
/*scgp->verbose--;*/
#endif	/* __needed__ */

	waitformat(scgp, 300);

	scg_settimeout(scgp, oldtimeout);
	return (ret);
#undef	MAX_TRIES
}


LOCAL int
fixate_bdr(scgp, dp, trackp)
	SCSI	*scgp;
	cdr_t	*dp;
	track_t	*trackp;
{
	int	oldtimeout = scgp->deftimeout;
	int	ret = 0;
	int	key = 0;
	int	code = 0;
	int	trackno;
	int	i;
#define	MAX_TRIES	15

	/*
	 * XXX Is this timeout needed for BD-R too?
	 * XXX It was introduced for DVD-R that writes at least 800 MB
	 */
	if (scgp->deftimeout < 1000)
		scg_settimeout(scgp, 1000);

/*scgp->verbose++;*/
	scgp->silent++;
	for (i = 0; i <= MAX_TRIES; i++) {
		if (scsi_flush_cache(scgp, TRUE) < 0) {
			if (!scsi_in_progress(scgp) || i >= MAX_TRIES) {
				if (scgp->verbose <= 0) {
					scg_printerr(scgp);
					scg_printresult(scgp);	/* XXX restore key/code in future */
				}
				printf(_("Trouble flushing the cache\n"));
				scgp->silent--;
				scg_settimeout(scgp, oldtimeout);
				return (-1);
			}
			sleep(1);
		} else {
			break;
		}
	}
	scgp->silent--;
	key = scg_sense_key(scgp);
	code = scg_sense_code(scgp);
	waitformat(scgp, 600);
	/*
	 * With CDs we used to close the invisible track (0xFF) but
	 * with DVDs this may not work anymore if the unit is MMC-3
	 * or above. Use the real track/border number instead.
	 * We always use the "IMMED" flag - is this OK?
	 */
	trackno = trackp->trackno;
	if (trackno <= 0)
		trackno = 1;
	scgp->silent++;
	for (i = 0; i <= MAX_TRIES; i++) {
		if (scsi_close_tr_session(scgp, CL_TYPE_TRACK, trackno, TRUE) < 0) {
			if (!scsi_in_progress(scgp) || i >= MAX_TRIES) {
				if (scgp->verbose <= 0) {
					scg_printerr(scgp);
					scg_printresult(scgp);	/* XXX restore key/code in future */
				}
				printf(_("Trouble closing the track\n"));
				break;
			}
			sleep(1);
		} else {
			break;
		}
	}
	scgp->silent--;
	key = scg_sense_key(scgp);
	code = scg_sense_code(scgp);
	waitformat(scgp, 600);

	scgp->silent++;
	for (i = 0; i <= MAX_TRIES; i++) {
		if (scsi_close_tr_session(scgp, 0x06, 0, TRUE) < 0) {
			if (!scsi_in_progress(scgp) || i >= MAX_TRIES) {
				if (scgp->verbose <= 0) {
					scg_printerr(scgp);
					scg_printresult(scgp);	/* XXX restore key/code in future */
				}
				printf(_("Trouble closing the last session\n"));
				break;
			}
			sleep(1);
		} else {
			break;
		}
	}
	scgp->silent--;
	key = scg_sense_key(scgp);
	code = scg_sense_code(scgp);
	waitformat(scgp, 600);
/*scgp->verbose--;*/

	scg_settimeout(scgp, oldtimeout);
	return (ret);
#undef	MAX_TRIES
}


/*--------------------------------------------------------------------------*/

LOCAL int
blank_bd(scgp, dp, addr, blanktype)
	SCSI	*scgp;
	cdr_t	*dp;
	long	addr;
	int	blanktype;
{
	BOOL	cdrr	 = FALSE;	/* Read CD-R	*/
	BOOL	cdwr	 = FALSE;	/* Write CD-R	*/
	BOOL	cdrrw	 = FALSE;	/* Read CD-RW	*/
	BOOL	cdwrw	 = FALSE;	/* Write CD-RW	*/
	BOOL	dvdwr	 = FALSE;	/* DVD writer	*/

	int	ret;

	mmc_check(scgp, &cdrr, &cdwr, &cdrrw, &cdwrw, NULL, &dvdwr);

	ret = blank_simul(scgp, dp, addr, blanktype);
	waitformat(scgp, 600);
	scsi_flush_cache(scgp, TRUE);
	waitformat(scgp, 600);
	return (ret);
}

LOCAL int
format_bd(scgp, dp, fmtflags)
	SCSI	*scgp;
	cdr_t	*dp;
	int	fmtflags;
{
	/*
	 * 1.3 Formatting
	 * --------------
	 *
	 * - For efficiency, please use the Format command only on blank discs
	 *
	 * - The command is:
	 *	- "04 11 00 00 00 00 00 00 00 00 00 00
	 *	    ^^^
	 *	    0x10	Format Data
	 *	    0x01	Format Code == 1
	 *	00 82 00 08 00 B4 74 00 C3 00 30 00"
	 *	^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
	 *	Format		Format
	 *	header		descriptor
	 *	0x80 FOV
	 *	0x20 IMMED
	 *	len == 0x08
	 */
	char	cap_buf[4096];
	char	fmt_buf[128];
	int	len;
	int	i;
	struct scsi_cap_data		*cp;
	struct scsi_format_cap_desc	*lp;
	struct scsi_format_header	*fhp;
	long	blocks = 0;
	long	blen = 0;
	struct timeval starttime;
	struct timeval stoptime;
#ifdef	BD_DEBUG
	struct timeval stoptime2;
#endif

	len = get_format_capacities(scgp, cap_buf, sizeof (cap_buf));
	if (len < 0)
		return (-1);

#ifdef	BD_DEBUG
	error(_("Cap len: %d\n"), len);
	scg_prbytes(_("Format cap:"), (Uchar *)cap_buf, len);
#endif

	cp = (struct scsi_cap_data *)cap_buf;
	lp = cp->list;
	len -= sizeof (struct scsi_format_cap_header);
	if (lp->desc_type == 2) {
		if ((dp->cdr_cmdflags & F_FORCE) == 0) {
			errmsgno(EX_BAD, _("Medium is already formatted.\n"));
			return (-1);
		}
	}
#ifdef	BD_DEBUG
	error("hd len: %d len: %d\n", cp->hd.len, len);
#endif

	for (i = len; i >= sizeof (struct scsi_format_cap_desc);
			i -= sizeof (struct scsi_format_cap_desc), lp++) {
#ifdef	BD_DEBUG
		error("blocks %ld fmt 0x%X desc %d blen: %ld\n",
			(long)a_to_u_4_byte(lp->nblock),
			lp->fmt_type,
			lp->desc_type,
			(long)a_to_u_3_byte(lp->blen));
#endif
		if (lp->fmt_type == FCAP_TYPE_BDRE_SPARE) {
			blocks = a_to_u_4_byte(lp->nblock);
			blen = (long)a_to_u_3_byte(lp->blen);
			break;
		}
	}
	if (blocks == 0) {
		errmsgno(EX_BAD, _("BD-RE Full format with spares, capacity not found.\n"));
		return (-1);
	}

	fhp = (struct scsi_format_header *)fmt_buf;
	lp = (struct scsi_format_cap_desc *)&fmt_buf[4];
	fillbytes((caddr_t)fmt_buf, sizeof (fmt_buf), '\0');

	fhp->enable = 1;
	fhp->immed = 1;
	i_to_2_byte(fhp->length, 8);
	i_to_4_byte(lp->nblock, blocks);
	lp->fmt_type = FCAP_TYPE_BDRE_SPARE;
	lp->desc_type = 3;			/* Quick certification */
	i_to_3_byte(lp->blen, blen);

#ifdef	BD_DEBUG
	scg_prbytes(_("Format desc:"), (Uchar *)fmt_buf, 12);
#endif

	if (lverbose) {
		/*
		 * XXX evt. restart Format ansagen...
		 */
		printf(_("Formatting media\n"));
		flush();
	}
	starttime.tv_sec = 0;
	starttime.tv_usec = 0;
	stoptime = starttime;
	gettimeofday(&starttime, (struct timezone *)0);

	if (format_unit(scgp, fhp, /*fhp->length*/ 8 + sizeof (struct scsi_format_header),
				1, 0, 0, 0,
				blocks > 20000000 ? 11000:5300) < 0) {
		return (-1);
	}


#ifdef	BD_DEBUG
/*	if (ret >= 0 && lverbose) {*/
	if (1) {
		gettimeofday(&stoptime, (struct timezone *)0);
		prtimediff(_("Format time: "), &starttime, &stoptime);
	}
#endif
	waitformat(scgp, 300);

#ifdef	BD_DEBUG
	gettimeofday(&stoptime2, (struct timezone *)0);
	prtimediff(_("Format WAIT time: "), &stoptime, &stoptime2);
	prtimediff(_("Format time TOTAL: "), &starttime, &stoptime2);
#endif
	return (0);
}

LOCAL int
waitformat(scgp, secs)
	SCSI	*scgp;
	int	secs;
{
	Uchar   sensebuf[CCS_SENSE_LEN];
	int	printed = 0;
	int	i;
	int	key;
#define	W_SLEEP	2

	scgp->silent++;
	for (i = 0; i < secs/W_SLEEP; i++) {
		if (test_unit_ready(scgp) >= 0) {
			scgp->silent--;
			return (0);
		}
		key = scg_sense_key(scgp);
		if (key != SC_UNIT_ATTENTION && key != SC_NOT_READY)
			break;
request_sense_b(scgp, (caddr_t)sensebuf, sizeof (sensebuf));
#ifdef	XXX
scg_prbytes("Sense:", sensebuf, sizeof (sensebuf));
scgp->scmd->u_scb.cmd_scb[0] = 2;
movebytes(sensebuf, scgp->scmd->u_sense.cmd_sense, sizeof (sensebuf));
scgp->scmd->sense_count = sizeof (sensebuf);
scg_printerr(scgp);
#endif
/*
 * status: 0x2 (CHECK CONDITION)
 * Sense Bytes: F0 00 00 00 24 1C 10 0C 00 00 00 00 04 04 00 80 03 F6
 * Sense Key: 0x0 No Additional Sense, Segment 0
 * Sense Code: 0x04 Qual 0x04 (logical unit not ready, format in progress) Fru 0x0
 * Sense flags: Blk 2366480 (valid)
 * cmd finished after 0.000s timeout 100s
 * Das Fehlt:
 * operation 1% done
 */

		if (lverbose && (sensebuf[15] & 0x80)) {
			printed++;
			error(_("operation %d%% done\r"),
				(100*(sensebuf[16] << 8 |
					sensebuf[17]))/(unsigned)65536);
		}
		sleep(W_SLEEP);
	}
	scgp->silent--;
	if (printed)
		error("\n");
	return (-1);
#undef	W_SLEEP
}

LOCAL int
stats_bd(scgp, dp)
	SCSI	*scgp;
	cdr_t	*dp;
{
	Uchar mode[256];
	struct	ricoh_mode_page_30 *rp;
	UInt32_t count;

	if ((dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0)
		return (0);

	rp = get_justlink_ricoh(scgp, mode);
	if (rp) {
		count = a_to_u_2_byte(rp->link_counter);
		if (lverbose) {
			if (count == 0)
				printf(_("BURN-Free was not used.\n"));
			else
				printf(_("BURN-Free was %d times used.\n"),
					(int)count);
		}
	}
	return (0);
}

#ifdef	NEEDED
#define	FMTDAT	0x10
#define	CMPLST	0x08

EXPORT int
format_unit(scgp, fmt, length, list_format, dgdl, interlv, pattern, timeout)
	SCSI	*scgp;
	void	*fmt;
	int	length;
	int	list_format;	/* 0 if fmt == 0			*/
	int	dgdl;		/* disable grown defect list		*/
	int	interlv;
	int	pattern;
	int	timeout;
{
	register struct	scg_cmd	*scmd = scgp->scmd;

	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
	scmd->addr = (caddr_t)fmt;
	scmd->size = length;
	scmd->flags = SCG_DISRE_ENA;
	scmd->cdb_len = SC_G0_CDBLEN;
	scmd->sense_len = CCS_SENSE_LEN;
	if (timeout < 0)
		timeout = 24*3600;	/* Kein Timeout XXX kann haengen */
	scmd->timeout = timeout;
	scmd->cdb.g0_cdb.cmd = 0x04;	/* Format Unit */
	scmd->cdb.g0_cdb.lun = scg_lun(scgp);
	scmd->cdb.g0_cdb.high_addr = (fmt?FMTDAT:0)|(dgdl?CMPLST:0)|list_format;
	scmd->cdb.g0_cdb.mid_addr = pattern;
	scmd->cdb.g0_cdb.count = interlv;

#ifdef	BD_DEBUG
	scg_prbytes(_("Format CDB: "), (u_char *)scmd->cdb.cmd_cdb, scmd->cdb_len);

/*	if (scgp->verbose && fmt)*/
		scg_prbytes(_("Format Data:"), (u_char *)fmt, length);
#endif

	scgp->cmdname = "format unit";

	return (scg_cmd(scgp));
}
#endif	/* NEEDED */

LOCAL void
print_bd_info(scgp)
	SCSI	*scgp;
{
	Uchar	mode[4096];
	int	ret;
	int	i;

	if (lverbose > 2)
		printf(_("Entering BD info....\n"));
	/*
	 * The ACARD TECH AEC-7720 ATAPI<->SCSI adaptor
	 * chokes if we try to transfer odd byte counts (rounds up to
	 * even byte counts and thus causes a DMA overflow and a
	 * bus reset), so make the byte count even.
	 * It also chokes if we try to transfer more than 0x40 bytes with
	 * mode_sense of all pages. As we don't want to avoid this
	 * command here, we need to wait until the drive recoveres
	 * from the reset?? it receives after the adapter dies.
	 */
	if (lverbose > 1)
		mode_sense(scgp, mode, 250, 0x3F, 0);
	if (lverbose > 2)
		scg_prbytes(_("Mode: "), mode, 250 - scg_getresid(scgp));
	wait_unit_ready(scgp, 120);
	if (lverbose > 1) {
		printf(_("Supported BD (readable) structures:"));
		scgp->silent++;
		for (i = 0; i <= 255; i++) {
			fillbytes((caddr_t)mode, sizeof (mode), '\0');
			ret = read_dvd_structure(scgp, (caddr_t)mode, sizeof (mode), 1, 0, 0, i);
			if (ret >= 0 && (sizeof (mode) - scg_getresid(scgp)) > 4)
				printf(" %02X", i);
		}
		scgp->silent--;
		printf("\n");
/*		printf("Page: %d ret: %d len: %d\n", i, ret, sizeof (mode) - scg_getresid(scgp));*/
		if (lverbose > 2)
			scg_prbytes(_("Page FF: "), mode, sizeof (mode) - scg_getresid(scgp));
		if (sizeof (mode) - scg_getresid(scgp) > 4) {
			int	len = a_to_u_2_byte(mode) - 2;
			Uchar	*p = &mode[4];
			int	m;

			len /= 4;
			for (i = 0; i < len; i++) {
				m = p[1] & 0xC0;
				printf(_("Page %02X %s  (%02X) len %d\n"),
					*p & 0xFF,
					m == 0xC0 ?
					_("read/write") :
					(m == 0x80 ? _("     write") :
					(m == 0x40 ? _("read      ") : _("unknown   "))),
					p[1] & 0xFF,
					a_to_u_2_byte(&p[2]));
				p += 4;
			}
		}
	}
	wait_unit_ready(scgp, 120);

	/*
	 * Disk Information 0x00
	 */
	fillbytes((caddr_t)mode, sizeof (mode), '\0');
	scgp->silent++;
	ret = read_dvd_structure(scgp, (caddr_t)mode, sizeof (mode), 1, 0, 0, 0);
	scgp->silent--;
	if (ret >= 0) {
		if (lverbose > 2) {
			scg_prbytes(_("BD structure[0]: "),
				mode, sizeof (mode) - scg_getresid(scgp));
/*			scg_prascii("BD structure[0]: ", mode, sizeof (mode) - scg_getresid(scgp));*/
		}
		print_bd00(mode);
	}

#ifdef	DEBUG
	fillbytes((caddr_t)mode, sizeof (mode), '\0');
	scgp->silent++;
	ret = read_dvd_structure(scgp, (caddr_t)mode, sizeof (mode), 1, 0, 0, 0x09);
	scgp->silent--;
	if (ret >= 0) {
		if (lverbose > 2) {
			scg_prbytes(_("BD structure[0x09]: "),
				mode, sizeof (mode) - scg_getresid(scgp));
			scg_prascii(_("BD structure[0x09]: "), mode, sizeof (mode) - scg_getresid(scgp));
		}
		print_bd09(mode);
	}
	fillbytes((caddr_t)mode, sizeof (mode), '\0');
	scgp->silent++;
	ret = read_dvd_structure(scgp, (caddr_t)mode, sizeof (mode), 1, 0, 0, 0x0A);
	scgp->silent--;
	if (ret >= 0) {
		if (lverbose > 2) {
			scg_prbytes(_("BD structure[0x0A]: "),
				mode, sizeof (mode) - scg_getresid(scgp));
			scg_prascii(_("BD structure[0x0A]: "), mode, sizeof (mode) - scg_getresid(scgp));
		}
		print_bd0A(mode);
	}
	fillbytes((caddr_t)mode, sizeof (mode), '\0');
	scgp->silent++;
	ret = read_dvd_structure(scgp, (caddr_t)mode, sizeof (mode), 1, 0, 0, 0xC0);
	scgp->silent--;
	if (ret >= 0) {
		if (lverbose > 2) {
			scg_prbytes(_("BD structure[0xC0]: "),
				mode, sizeof (mode) - scg_getresid(scgp));
			scg_prascii(_("BD structure[0xC0]: "), mode, sizeof (mode) - scg_getresid(scgp));
		}
	}
#endif
}

LOCAL void
print_bd00(dp)
	Uchar	*dp;
{
	Uchar	*bd = &((Uchar *)dp)[4];
#ifdef	BD_DEBUG
	int	len = a_to_2_byte(dp)+2;

	error("len %d\n", len);
	scg_prbytes("BD structure[0]: ", bd, 255);
	scg_prascii("BD structure[0]: ", bd, 255);
	printf("ID: '%.3s'\n", &bd[8]);
	printf("Disk size: %2.2X\n", bd[11]);
	scg_prbytes("Format dep content: ", &bd[12], 100);
	scg_prascii("Format dep content: ", &bd[12], 100);
	printf("Manufacturer: '%.6s'\n", &bd[12+88]);
	printf("Type: '%.3s'\n", &bd[12+94]);
#endif

	printf(_("Disk type:          '%.3s' (BD-%s)\n"), &bd[8],
		bd[10] == 'O' ? "ROM":
		(bd[10] == 'R' ? "R" :
		(bd[10] == 'W' ? "RE": "???")));
	printf(_("Disk class:         %2.2X\n"), bd[11]);
	printf(_("Manufacturer:       '%.6s'\n"), &bd[12+88]);
	printf(_("Media type:         '%.3s'\n"), &bd[12+94]);

}

LOCAL void
print_bd09(dp)
	Uchar	*dp;
{
	Uchar	*bd = &((Uchar *)dp)[4];
#ifdef	BD_DEBUG
	int	len = a_to_2_byte(dp)+2;

	error("len %d\n", len);
	scg_prbytes("BD structure[09]: ", bd, len);
	scg_prascii("BD structure[09]: ", bd, len);
#endif

	printf(_("Disk:               is %sin cartridge\n"),
					(bd[4] & 0x80) == 0 ? _("not "): "");
	if (bd[4] & 0x80) {
		printf(_("Disk:               was %staken out of cartridge\n"),
					(bd[4] & 0x40) == 0 ? _("not "): "");
	}
	printf(_("Media cartrige:     write protect is %s\n"),
					(bd[4] & 0x04) ? _("on"): _("off"));
}

LOCAL void
print_bd0A(dp)
	Uchar	*dp;
{
#ifdef	BD_DEBUG
	int	len = a_to_2_byte(dp)+2;
	Uchar	*bd = &((Uchar *)dp)[4];

	error("len %d\n", len);
	scg_prbytes("BD structure[0A]: ", bd, len);
	scg_prascii("BD structure[0A]: ", bd, len);
#endif

	printf(_("Free Spare Blocks:  %lu\n"), (unsigned long)
					a_to_4_byte(&dp[8]));
	printf(_("Alloc Spare Blocks: %lu\n"), (unsigned long)
					a_to_4_byte(&dp[12]));
}
