From f2176acab00126c6ce23bd18ad1e95ad0b5aee4f Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 17 2020 17:27:52 +0000 Subject: Apply patch cdrkit-1.1.11-format.patch patch_name: cdrkit-1.1.11-format.patch present_in_specfile: true --- diff --git a/wodim/drv_mmc.c b/wodim/drv_mmc.c index b075889..8b72dda 100644 --- a/wodim/drv_mmc.c +++ b/wodim/drv_mmc.c @@ -1507,6 +1507,11 @@ getdisktype_mdvd(SCSI *usalp, cdr_t *dp) dstat_t *dsp = dp->cdr_dstat; struct track_info track_info; + + extern char *buf; + struct disk_info *dip; + int profile; + if(lverbose) printf("HINT: use dvd+rw-mediainfo from dvd+rw-tools for information extraction.\n"); /* if(getdisktype_mmc(usalp, dp)<0) @@ -1521,6 +1526,18 @@ getdisktype_mdvd(SCSI *usalp, cdr_t *dp) dsp->ds_disktype&= ~DT_CD; dsp->ds_disktype|= DT_DVD; + profile = get_curprofile(usalp); + if (profile == 0x1A) { + dip = (struct disk_info *)buf; + if (get_diskinfo(usalp, dip) < 0) + return (-1); + if (dip->disk_status == DS_EMPTY) { /* Unformatted */ + dsp->ds_flags |= DSF_NEED_FORMAT; + if(lverbose) + printf("The medium is empty, it will be auto formatted.\n"); + } + } + return (ret); } diff --git a/wodim/drv_mmc.c.format b/wodim/drv_mmc.c.format new file mode 100644 index 0000000..b075889 --- /dev/null +++ b/wodim/drv_mmc.c.format @@ -0,0 +1,4362 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)drv_mmc.c 1.163 06/01/12 Copyright 1997-2006 J. Schilling */ +/* + * CDR device implementation for + * SCSI-3/mmc conforming drives + * e.g. Yamaha CDR-400, Ricoh MP6200 + * + * Copyright (c) 1997-2006 J. Schilling + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*#define DEBUG*/ +#define PRINT_ATIP +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "scsimmc.h" +#include "mmcvendor.h" +#include "wodim.h" +#include "scsi_scan.h" + +extern char *driveropts; + +extern int debug; +extern int lverbose; +extern int xdebug; + +static int curspeed = 1; + +static char clv_to_speed[16] = { +/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + 0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static char hs_clv_to_speed[16] = { +/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + 0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static char us_clv_to_speed[16] = { +/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + 0, 2, 4, 8, 0, 0, 16, 0, 24, 32, 40, 48, 0, 0, 0, 0 +}; + +#ifdef __needed__ +static int mmc_load(SCSI *usalp, cdr_t *dp); +static int mmc_unload(SCSI *usalp, cdr_t *dp); +#endif +void mmc_opthelp(cdr_t *dp, int excode); +char *hasdrvopt(char *optstr, char *optname); +static cdr_t *identify_mmc(SCSI *usalp, cdr_t *, struct scsi_inquiry *); +static int attach_mmc(SCSI *usalp, cdr_t *); +static int attach_mdvd(SCSI *usalp, cdr_t *); +int check_writemodes_mmc(SCSI *usalp, cdr_t *dp); +int check_writemodes_mdvd(SCSI *usalp, cdr_t *dp); +static int deflt_writemodes_mmc(SCSI *usalp, BOOL reset_dummy); +static int deflt_writemodes_mdvd(SCSI *usalp, BOOL reset_dummy); +static int get_diskinfo(SCSI *usalp, struct disk_info *dip); +static void di_to_dstat(struct disk_info *dip, dstat_t *dsp); +static int get_atip(SCSI *usalp, struct atipinfo *atp); +#ifdef PRINT_ATIP +static int get_pma(SCSI *usalp); +#endif +static int init_mmc(SCSI *usalp, cdr_t *dp); +static int getdisktype_mmc(SCSI *usalp, cdr_t *dp); +static int getdisktype_mdvd(SCSI *usalp, cdr_t *dp); +static int speed_select_mmc(SCSI *usalp, cdr_t *dp, int *speedp); +static int speed_select_mdvd(SCSI *usalp, cdr_t *dp, int *speedp); +static int mmc_set_speed(SCSI *usalp, int readspeed, int writespeed, + int rotctl); +static int next_wr_addr_mmc(SCSI *usalp, track_t *trackp, long *ap); +static int next_wr_addr_mdvd(SCSI *usalp, track_t *trackp, long *ap); +static int write_leadin_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int open_track_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int open_track_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int close_track_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int close_track_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int open_session_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int open_session_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int waitfix_mmc(SCSI *usalp, int secs); +static int fixate_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int fixate_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int blank_mmc(SCSI *usalp, cdr_t *dp, long addr, int blanktype); +static int format_mdvd(SCSI *usalp, cdr_t *dp, int formattype); +static int send_opc_mmc(SCSI *usalp, caddr_t, int cnt, int doopc); +static int opt1_mmc(SCSI *usalp, cdr_t *dp); +static int opt1_mdvd(SCSI *usalp, cdr_t *dp); +static int opt2_mmc(SCSI *usalp, cdr_t *dp); +static int scsi_sony_write(SCSI *usalp, caddr_t bp, long sectaddr, long size, + int blocks, BOOL islast); +static int gen_cue_mmc(track_t *trackp, void *vcuep, BOOL needgap); +static void fillcue(struct mmc_cue *cp, int ca, int tno, int idx, int dataform, + int scms, msf_t *mp); +static int send_cue_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int stats_mmc(SCSI *usalp, cdr_t *dp); +static BOOL mmc_isplextor(SCSI *usalp); +static BOOL mmc_isyamaha(SCSI *usalp); +static void do_varirec_plextor(SCSI *usalp); +static int do_gigarec_plextor(SCSI *usalp); +static int drivemode_plextor(SCSI *usalp, caddr_t bp, int cnt, int modecode, + void *modeval); +static int drivemode2_plextor(SCSI *usalp, caddr_t bp, int cnt, int modecode, + void *modeval); +static int check_varirec_plextor(SCSI *usalp); +static int check_gigarec_plextor(SCSI *usalp); +static int varirec_plextor(SCSI *usalp, BOOL on, int val); +static int gigarec_plextor(SCSI *usalp, int val); +static Int32_t gigarec_mult(int code, Int32_t val); +static int check_ss_hide_plextor(SCSI *usalp); +static int check_speed_rd_plextor(SCSI *usalp); +static int check_powerrec_plextor(SCSI *usalp); +static int ss_hide_plextor(SCSI *usalp, BOOL do_ss, BOOL do_hide); +static int speed_rd_plextor(SCSI *usalp, BOOL do_speedrd); +static int powerrec_plextor(SCSI *usalp, BOOL do_powerrec); +static int get_speeds_plextor(SCSI *usalp, int *selp, int *maxp, int *lastp); +static int bpc_plextor(SCSI *usalp, int mode, int *bpp); +static int set_audiomaster_yamaha(SCSI *usalp, cdr_t *dp, BOOL keep_mode); + +struct ricoh_mode_page_30 * get_justlink_ricoh(SCSI *usalp, Uchar *mode); +static int force_speed_yamaha(SCSI *usalp, int readspeed, int writespeed); +static BOOL get_tattoo_yamaha(SCSI *usalp, BOOL print, Int32_t *irp, + Int32_t *orp); +static int do_tattoo_yamaha(SCSI *usalp, FILE *f); +static int yamaha_write_buffer(SCSI *usalp, int mode, int bufferid, long offset, + long parlen, void *buffer, long buflen); +static int dvd_dual_layer_split(SCSI *usalp, cdr_t *dp, long tsize); + +extern int reserve_track(SCSI *usalp, Ulong size); /* FIXME */ +extern int scsi_format(SCSI *usalp, caddr_t addr, int size, BOOL background); /* FIXME */ + +#ifdef __needed__ +static int +mmc_load(SCSI *usalp, cdr_t *dp) +{ + return (scsi_load_unload(usalp, 1)); +} + +static int +mmc_unload(SCSI *usalp, cdr_t *dp) +{ + return (scsi_load_unload(usalp, 0)); +} +#endif + +/* + * MMC CD-writer + */ +cdr_t cdr_mmc = { + 0, 0, + CDR_SWABAUDIO, + CDR_CDRW_ALL, + 372, 372, + "mmc_cdr", + "generic SCSI-3/mmc CD-R/CD-RW driver", + 0, + (dstat_t *)0, + identify_mmc, + attach_mmc, + init_mmc, + getdisktype_mmc, + scsi_load, + scsi_unload, + read_buff_cap, + cmd_dummy, /* check_recovery */ + (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */ + speed_select_mmc, + select_secsize, + next_wr_addr_mmc, + (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */ + scsi_cdr_write, + gen_cue_mmc, + send_cue_mmc, + write_leadin_mmc, + open_track_mmc, + close_track_mmc, + open_session_mmc, + cmd_dummy, + cmd_dummy, /* abort */ + read_session_offset, + fixate_mmc, + stats_mmc, + blank_mmc, + format_dummy, + send_opc_mmc, + opt1_mmc, + opt2_mmc, +}; + +cdr_t cdr_mdvd = { + 0, 0, + CDR_SWABAUDIO, + CDR_CDRW_ALL, + 370,370, + "mmc_mdvd", + "generic SCSI-3/mmc DVD-R(W) driver", + 0, + (dstat_t *)0, + identify_mmc, + attach_mdvd, + init_mmc, + getdisktype_mdvd, + scsi_load, + scsi_unload, + read_buff_cap, + cmd_dummy, /* check_recovery */ + (int(*)__PR((SCSI *, cdr_t *, int)))cmd_dummy, /* recover */ + speed_select_mdvd, + select_secsize, + next_wr_addr_mdvd, + (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */ + scsi_cdr_write, + (int(*)__PR((track_t *, void *, BOOL)))cmd_dummy, /* gen_cue */ + (int(*)__PR((SCSI *usalp, cdr_t *, track_t *)))cmd_dummy, /* send_cue */ + write_leadin_mmc, + open_track_mdvd, + close_track_mdvd, + open_session_mdvd, + cmd_dummy, + cmd_dummy, /* abort */ + read_session_offset, + fixate_mdvd, + stats_mmc, + blank_mmc, + format_mdvd, + send_opc_mmc, + opt1_mdvd, + opt2_mmc, + dvd_dual_layer_split, +}; + +/* + * Sony MMC CD-writer + */ +cdr_t cdr_mmc_sony = { + 0, 0, + CDR_SWABAUDIO, + CDR_CDRW_ALL, + 372, 372, + "mmc_cdr_sony", + "generic SCSI-3/mmc CD-R/CD-RW driver (Sony 928 variant)", + 0, + (dstat_t *)0, + identify_mmc, + attach_mmc, + init_mmc, + getdisktype_mmc, + scsi_load, + scsi_unload, + read_buff_cap, + cmd_dummy, /* check_recovery */ + (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */ + speed_select_mmc, + select_secsize, + next_wr_addr_mmc, + (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */ + scsi_sony_write, + gen_cue_mmc, + send_cue_mmc, + write_leadin_mmc, + open_track_mmc, + close_track_mmc, + open_session_mmc, + cmd_dummy, + cmd_dummy, /* abort */ + read_session_offset, + fixate_mmc, + cmd_dummy, /* stats */ + blank_mmc, + format_dummy, + send_opc_mmc, + opt1_mmc, + opt2_mmc, +}; + +/* + * SCSI-3/mmc conformant CD-ROM drive + */ +cdr_t cdr_cd = { + 0, 0, + CDR_ISREADER|CDR_SWABAUDIO, + CDR_CDRW_NONE, + 372, 372, + "mmc_cd", + "generic SCSI-3/mmc CD-ROM driver", + 0, + (dstat_t *)0, + identify_mmc, + attach_mmc, + cmd_dummy, + drive_getdisktype, + scsi_load, + scsi_unload, + read_buff_cap, + cmd_dummy, /* check_recovery */ + (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */ + speed_select_mmc, + select_secsize, + (int(*)(SCSI *usalp, track_t *, long *))cmd_ill, /* next_wr_addr */ + (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */ + scsi_cdr_write, + (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */ + no_sendcue, + (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */ + open_track_mmc, + close_track_mmc, + (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, + cmd_dummy, + cmd_dummy, /* abort */ + read_session_offset, + (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* fixation */ + cmd_dummy, /* stats */ + blank_dummy, + format_dummy, + (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */ + cmd_dummy, /* opt1 */ + cmd_dummy, /* opt2 */ +}; + +/* + * Old pre SCSI-3/mmc CD drive + */ +cdr_t cdr_oldcd = { + 0, 0, + CDR_ISREADER, + CDR_CDRW_NONE, + 372, 372, + "scsi2_cd", + "generic SCSI-2 CD-ROM driver", + 0, + (dstat_t *)0, + identify_mmc, + drive_attach, + cmd_dummy, + drive_getdisktype, + scsi_load, + scsi_unload, + buf_dummy, + cmd_dummy, /* check_recovery */ + (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */ + speed_select_mmc, + select_secsize, + (int(*)(SCSI *usal, track_t *, long *))cmd_ill, /* next_wr_addr */ + (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */ + scsi_cdr_write, + (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */ + no_sendcue, + (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */ + open_track_mmc, + close_track_mmc, + (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, + cmd_dummy, + cmd_dummy, /* abort */ + read_session_offset_philips, + (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* fixation */ + cmd_dummy, /* stats */ + blank_dummy, + format_dummy, + (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */ + cmd_dummy, /* opt1 */ + cmd_dummy, /* opt2 */ +}; + +/* + * SCSI-3/mmc conformant CD or DVD writer + * Checks the current medium and then returns either cdr_mmc or cdr_dvd + */ +cdr_t cdr_cd_dvd = { + 0, 0, + CDR_SWABAUDIO, + CDR_CDRW_ALL, + 372, 372, + "mmc_cd_dvd", + "generic SCSI-3/mmc CD/DVD driver (checks media)", + 0, + (dstat_t *)0, + identify_mmc, + attach_mmc, + cmd_dummy, + drive_getdisktype, + scsi_load, + scsi_unload, + read_buff_cap, + cmd_dummy, /* check_recovery */ + (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */ + speed_select_mmc, + select_secsize, + (int(*)(SCSI *usalp, track_t *, long *))cmd_ill, /* next_wr_addr */ + (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */ + scsi_cdr_write, + (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */ + no_sendcue, + (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */ + open_track_mmc, + close_track_mmc, + (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, + cmd_dummy, + cmd_dummy, /* abort */ + read_session_offset, + (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* fixation */ + cmd_dummy, /* stats */ + blank_dummy, + format_dummy, + (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */ + cmd_dummy, /* opt1 */ + cmd_dummy, /* opt2 */ +}; + +void +mmc_opthelp(cdr_t *dp, int excode) +{ + BOOL haveopts = FALSE; + + fprintf(stderr, "Driver options:\n"); + if (dp->cdr_flags & CDR_BURNFREE) { + fprintf(stderr, "burnfree Prepare writer to use BURN-Free technology\n"); + fprintf(stderr, "noburnfree Disable using BURN-Free technology\n"); + haveopts = TRUE; + } + if (dp->cdr_flags & CDR_VARIREC) { + fprintf(stderr, "varirec=val Set VariRec Laserpower to -2, -1, 0, 1, 2\n"); + fprintf(stderr, " Only works for audio and if speed is set to 4\n"); + haveopts = TRUE; + } + if (dp->cdr_flags & CDR_GIGAREC) { + fprintf(stderr, "gigarec=val Set GigaRec capacity ratio to 0.6, 0.7, 0.8, 1.0, 1.2, 1.3, 1.4\n"); + haveopts = TRUE; + } + if (dp->cdr_flags & CDR_AUDIOMASTER) { + fprintf(stderr, "audiomaster Turn Audio Master feature on (SAO CD-R Audio/Data only)\n"); + haveopts = TRUE; + } + if (dp->cdr_flags & CDR_FORCESPEED) { + fprintf(stderr, "forcespeed Tell the drive to force speed even for low quality media\n"); + haveopts = TRUE; + } + if (dp->cdr_flags & CDR_SPEEDREAD) { + fprintf(stderr, "speedread Tell the drive to read as fast as possible\n"); + fprintf(stderr, "nospeedread Disable to read as fast as possible\n"); + haveopts = TRUE; + } + if (dp->cdr_flags & CDR_DISKTATTOO) { + fprintf(stderr, "tattooinfo Print image size info for DiskT@2 feature\n"); + fprintf(stderr, "tattoofile=name Use 'name' as DiskT@2 image file\n"); + haveopts = TRUE; + } + if (dp->cdr_flags & CDR_SINGLESESS) { + fprintf(stderr, "singlesession Tell the drive to behave as single session only drive\n"); + fprintf(stderr, "nosinglesession Disable single session only mode\n"); + haveopts = TRUE; + } + if (dp->cdr_flags & CDR_HIDE_CDR) { + fprintf(stderr, "hidecdr Tell the drive to hide CD-R media\n"); + fprintf(stderr, "nohidecdr Disable hiding CD-R media\n"); + haveopts = TRUE; + } + if (!haveopts) { + fprintf(stderr, "None supported for this drive.\n"); + } + exit(excode); +} + +char * +hasdrvopt(char *optstr, char *optname) +{ + char *ep; + char *np; + char *ret = NULL; + int optnamelen; + int optlen; + BOOL not = FALSE; + + if (optstr == NULL) + return (ret); + + optnamelen = strlen(optname); + + while (*optstr) { + not = FALSE; /* Reset before every token */ + if ((ep = strchr(optstr, ',')) != NULL) { + optlen = ep - optstr; + np = &ep[1]; + } else { + optlen = strlen(optstr); + np = &optstr[optlen]; + } + if ((ep = strchr(optstr, '=')) != NULL) { + if (ep < np) + optlen = ep - optstr; + } + if (optstr[0] == '!') { + optstr++; + optlen--; + not = TRUE; + } + if (strncmp(optstr, "no", 2) == 0) { + optstr += 2; + optlen -= 2; + not = TRUE; + } + if (strncmp(optstr, optname, optlen) == 0) { + ret = &optstr[optlen]; + break; + } + optstr = np; + } + if (ret != NULL) { + if (*ret == ',' || *ret == '\0') { + if (not) + return ("0"); + return ("1"); + } + if (*ret == '=') { + if (not) + return (NULL); + return (++ret); + } + } + return (ret); +} + +static cdr_t * +identify_mmc(SCSI *usalp, cdr_t *dp, struct scsi_inquiry *ip) +{ + 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 */ + BOOL is_dvd = FALSE; /* use DVD driver*/ + 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(usalp, TRUE); /* Try to switch to 10 byte mode cmds */ + + usalp->silent++; + mp = mmc_cap(usalp, mode); /* Get MMC capabilities */ + usalp->silent--; + if (mp == NULL) + return (&cdr_oldcd); /* 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; + + /* + * First handle exceptions.... + */ + if (strncmp(ip->vendor_info, "SONY", 4) == 0 && + strncmp(ip->prod_ident, "CD-R CDU928E", 14) == 0) { + return (&cdr_mmc_sony); + } + + /* + * Now try to do it the MMC-3 way.... + */ + profile = get_curprofile(usalp); + if (xdebug) + printf("Current profile: 0x%04X\n", profile); + if (profile == 0) { + if (xdebug) + print_profiles(usalp); + /* + * If the current profile is 0x0000, then the + * drive does not know about the media. First + * close the tray and then try to issue the + * get_curprofile() command again. + */ + usalp->silent++; + load_media(usalp, dp, FALSE); + usalp->silent--; + profile = get_curprofile(usalp); + scsi_prevent_removal(usalp, 0); + if (xdebug) + printf("Current profile: 0x%04X\n", profile); + } + if (profile >= 0) { + if (lverbose) + print_profiles(usalp); + if (profile == 0 || (profile >= 0x10 && profile <= 0x15) || profile > 0x19) { + /* + * 10h DVD-ROM + * 11h DVD-R + * 12h DVD-RAM + * 13h DVD-RW (Restricted overwrite) + * 14h DVD-RW (Sequential recording) + * 1Ah DVD+RW + * 1Bh DVD+R + * 2Bh DVD+R DL + * + */ + if (profile == 0x11 || profile == 0x13 || profile == 0x14 || profile == 0x1A || profile == 0x1B || profile == 0x2B) { + is_dvd = TRUE; + dp = &cdr_mdvd; + } else { + is_dvd = FALSE; + dp = &cdr_cd; + + if (profile == 0) { /* No Medium */ + BOOL is_cdr = FALSE; + + /* + * Check for CD-writer + */ + get_wproflist(usalp, &is_cdr, NULL, + NULL, NULL); + if (is_cdr) + return (&cdr_mmc); + /* + * Other MMC-3 drive without media + */ + return (dp); + } if (profile == 0x12) { /* DVD-RAM */ + errmsgno(EX_BAD, + "Found unsupported DVD-RAM media.\n"); + return (dp); + } + } + } + } else { + if (xdebug) + printf("Drive is pre MMC-3\n"); + } + + mmc_getval(mp, &cdrr, &cdwr, &cdrrw, &cdwrw, NULL, &dvdwr); + + if (!cdwr && !cdwrw) { /* SCSI-3/mmc CD drive */ + /* + * If the drive does not support to write CD's, we select the + * CD-ROM driver here. If we have DVD-R/DVD-RW support compiled + * in, we may later decide to switch to the DVD driver. + */ + dp = &cdr_cd; + } else { + /* + * We need to set the driver to cdr_mmc because we may come + * here with driver set to cdr_cd_dvd which is not a driver + * that may be used for actual CD/DVD writing. + */ + dp = &cdr_mmc; + } + +/*#define DVD_DEBUG*/ +#ifdef DVD_DEBUG + if (1) { /* Always check for DVD media in debug mode */ +#else + if ((cdwr || cdwrw) && dvdwr) { +#endif + char xb[32]; + +#ifndef DVD_DEBUG + usalp->silent++; +#else + fprintf(stderr, "identify_dvd: checking for DVD media\n"); +#endif + if (read_dvd_structure(usalp, (caddr_t)xb, 32, 0, 0, 0) >= 0) { + /* + * If read DVD structure is supported and works, then + * we must have a DVD media in the drive. Signal to + * use the DVD driver. + */ + is_dvd = TRUE; + } else { + if (usal_sense_key(usalp) == SC_NOT_READY) { + /* + * If the SCSI sense key is NOT READY, then the + * drive does not know about the media. First + * close the tray and then try to issue the + * read_dvd_structure() command again. + */ + load_media(usalp, dp, FALSE); + if (read_dvd_structure(usalp, (caddr_t)xb, 32, 0, 0, 0) >= 0) { + is_dvd = TRUE; + } + scsi_prevent_removal(usalp, 0); + } + } +#ifndef DVD_DEBUG + usalp->silent--; +#else + fprintf(stderr, "identify_dvd: is_dvd: %d\n", is_dvd); +#endif + } + if (is_dvd) { + if(lverbose>2) + fprintf(stderr, "Found DVD media: using cdr_mdvd.\n"); + dp = &cdr_mdvd; + } + dp->profile = profile; + dp->is_dvd = is_dvd; + return (dp); +} + +static int +attach_mmc(SCSI *usalp, cdr_t *dp) +{ + int ret; + Uchar mode[0x100]; + struct cd_mode_page_2A *mp; + struct ricoh_mode_page_30 *rp = NULL; + + allow_atapi(usalp, TRUE); /* Try to switch to 10 byte mode cmds */ + + usalp->silent++; + mp = mmc_cap(usalp, NULL); /* Get MMC capabilities in allocated mp */ + usalp->silent--; + if (mp == NULL) + return (-1); /* Pre SCSI-3/mmc drive */ + + dp->cdr_cdcap = mp; /* Store MMC cap pointer */ + + dp->cdr_dstat->ds_dr_max_rspeed = a_to_u_2_byte(mp->max_read_speed)/176; + if (dp->cdr_dstat->ds_dr_max_rspeed == 0) + dp->cdr_dstat->ds_dr_max_rspeed = 372; + dp->cdr_dstat->ds_dr_cur_rspeed = a_to_u_2_byte(mp->cur_read_speed)/176; + if (dp->cdr_dstat->ds_dr_cur_rspeed == 0) + dp->cdr_dstat->ds_dr_cur_rspeed = 372; + + dp->cdr_dstat->ds_dr_max_wspeed = a_to_u_2_byte(mp->max_write_speed)/176; + if (mp->p_len >= 28) + dp->cdr_dstat->ds_dr_cur_wspeed = a_to_u_2_byte(mp->v3_cur_write_speed)/176; + else + dp->cdr_dstat->ds_dr_cur_wspeed = a_to_u_2_byte(mp->cur_write_speed)/176; + + 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(usalp, mode); + + if (mp->p_len >= 28) + 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; + } 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 (mmc_isplextor(usalp)) { + if (check_varirec_plextor(usalp) >= 0) + dp->cdr_flags |= CDR_VARIREC; + + if (check_gigarec_plextor(usalp) >= 0) + dp->cdr_flags |= CDR_GIGAREC; + + if (check_ss_hide_plextor(usalp) >= 0) + dp->cdr_flags |= CDR_SINGLESESS|CDR_HIDE_CDR; + + if (check_powerrec_plextor(usalp) >= 0) + dp->cdr_flags |= CDR_FORCESPEED; + + if (check_speed_rd_plextor(usalp) >= 0) + dp->cdr_flags |= CDR_SPEEDREAD; + } + if (mmc_isyamaha(usalp)) { + if (set_audiomaster_yamaha(usalp, dp, FALSE) >= 0) + dp->cdr_flags |= CDR_AUDIOMASTER; + + /* + * Starting with CRW 2200 / CRW 3200 + */ + if ((mp->p_len+2) >= (unsigned)28) + dp->cdr_flags |= CDR_FORCESPEED; + + if (get_tattoo_yamaha(usalp, FALSE, 0, 0)) + dp->cdr_flags |= CDR_DISKTATTOO; + } + + if (rp && rp->AWSCS) + dp->cdr_flags |= CDR_FORCESPEED; + +#ifdef FUTURE_ROTCTL + if (mp->p_len >= 28) { + int val; + + val = dp->cdr_dstat->ds_dr_cur_wspeed; + if (val == 0) + val = 372; + + usalp->verbose++; + if (scsi_set_speed(usalp, -1, val, ROTCTL_CAV) < 0) { + fprintf(stderr, "XXX\n"); + } + usalp->verbose--; + } +#endif + + check_writemodes_mmc(usalp, dp); + + /* Enable Burnfree by default, it can be disabled later */ + if ((dp->cdr_flags & CDR_BURNFREE) != 0) + dp->cdr_dstat->ds_cdrflags |= RF_BURNFREE; + + if (driveropts != NULL) { + char *p; + + if (strcmp(driveropts, "help") == 0) { + mmc_opthelp(dp, 0); + } + + p = hasdrvopt(driveropts, "varirec"); + if (p != NULL && (dp->cdr_flags & CDR_VARIREC) != 0) { + dp->cdr_dstat->ds_cdrflags |= RF_VARIREC; + } + + p = hasdrvopt(driveropts, "gigarec"); + if (p != NULL && (dp->cdr_flags & CDR_GIGAREC) != 0) { + dp->cdr_dstat->ds_cdrflags |= RF_GIGAREC; + } + + p = hasdrvopt(driveropts, "audiomaster"); + if (p != NULL && *p == '1' && (dp->cdr_flags & CDR_AUDIOMASTER) != 0) { + dp->cdr_dstat->ds_cdrflags |= RF_AUDIOMASTER; + 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; + } + p = hasdrvopt(driveropts, "tattooinfo"); + if (p != NULL && *p == '1' && (dp->cdr_flags & CDR_DISKTATTOO) != 0) { + get_tattoo_yamaha(usalp, TRUE, 0, 0); + } + p = hasdrvopt(driveropts, "tattoofile"); + if (p != NULL && (dp->cdr_flags & CDR_DISKTATTOO) != 0) { + FILE *f; + + if ((f = fileopen(p, "rb")) == NULL) + comerr("Cannot open '%s'.\n", p); + + if (do_tattoo_yamaha(usalp, f) < 0) + errmsgno(EX_BAD, "Cannot do DiskT@2.\n"); + fclose(f); + } + p = hasdrvopt(driveropts, "singlesession"); + if (p != NULL && (dp->cdr_flags & CDR_SINGLESESS) != 0) { + if (*p == '1') { + dp->cdr_dstat->ds_cdrflags |= RF_SINGLESESS; + } else if (*p == '0') { + dp->cdr_dstat->ds_cdrflags &= ~RF_SINGLESESS; + } + } + p = hasdrvopt(driveropts, "hidecdr"); + if (p != NULL && (dp->cdr_flags & CDR_HIDE_CDR) != 0) { + if (*p == '1') { + dp->cdr_dstat->ds_cdrflags |= RF_HIDE_CDR; + } else if (*p == '0') { + dp->cdr_dstat->ds_cdrflags &= ~RF_HIDE_CDR; + } + } + p = hasdrvopt(driveropts, "speedread"); + if (p != NULL && (dp->cdr_flags & CDR_SPEEDREAD) != 0) { + if (*p == '1') { + dp->cdr_dstat->ds_cdrflags |= RF_SPEEDREAD; + } else if (*p == '0') { + dp->cdr_dstat->ds_cdrflags &= ~RF_SPEEDREAD; + } + } + } + + if ((ret = get_supported_cdrw_media_types(usalp)) < 0) { + dp->cdr_cdrw_support = CDR_CDRW_ALL; + return (0); + } + dp->cdr_cdrw_support = ret; + if (lverbose > 1) + printf("Supported CD-RW media types: %02X\n", dp->cdr_cdrw_support); + + return (0); +} + +static int +attach_mdvd(SCSI *usalp, cdr_t *dp) +{ + struct cd_mode_page_2A *mp; + + + allow_atapi(usalp, TRUE);/* Try to switch to 10 byte mode cmds */ + + usalp->silent++; + mp = mmc_cap(usalp, NULL);/* Get MMC capabilities in allocated mp */ + usalp->silent--; + if (mp == NULL) + return (-1); /* Pre SCSI-3/mmc drive */ + + dp->cdr_cdcap = mp; /* Store MMC cap pointer */ + + dp->cdr_dstat->ds_dr_max_rspeed = a_to_u_2_byte(mp->max_read_speed)/1385; + if (dp->cdr_dstat->ds_dr_max_rspeed == 0) + dp->cdr_dstat->ds_dr_max_rspeed = 1385; + dp->cdr_dstat->ds_dr_cur_rspeed = a_to_u_2_byte(mp->cur_read_speed)/1385; + if (dp->cdr_dstat->ds_dr_cur_rspeed == 0) + dp->cdr_dstat->ds_dr_cur_rspeed = 1385; + + dp->cdr_dstat->ds_dr_max_wspeed = a_to_u_2_byte(mp->max_write_speed)/1385; + if (mp->p_len >= 28) + dp->cdr_dstat->ds_dr_cur_wspeed = a_to_u_2_byte(mp->v3_cur_write_speed)/1385; + else + dp->cdr_dstat->ds_dr_cur_wspeed = a_to_u_2_byte(mp->cur_write_speed)/1385; + + 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; + + + 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_mdvd(usalp, dp); + + if (driveropts != NULL) { + if (strcmp(driveropts, "help") == 0) { + mmc_opthelp(dp, 0); + } + } + + return (0); +} + +int +check_writemodes_mmc(SCSI *usalp, cdr_t *dp) +{ + Uchar mode[0x100]; + int len; + struct cd_mode_page_05 *mp; + + if (xdebug) + printf("Checking possible write modes: "); + + /* + * Reset mp->test_write (-dummy) here. + */ + deflt_writemodes_mmc(usalp, TRUE); + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + usalp->silent++; + if (!get_mode_params(usalp, 0x05, "CD write parameter", + mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) { + usalp->silent--; + return (-1); + } + if (len == 0) { + usalp->silent--; + return (-1); + } + + mp = (struct cd_mode_page_05 *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); +#ifdef DEBUG + usal_prbytes("CD write parameter:", (Uchar *)mode, len); +#endif + + /* + * mp->test_write has already been reset in deflt_writemodes_mmc() + * Do not reset mp->test_write (-dummy) here. It should be set + * only at one place and only one time. + */ + + mp->write_type = WT_TAO; + mp->track_mode = TM_DATA; + mp->dbtype = DB_ROM_MODE1; + + if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_TAO; + if (xdebug) + printf("TAO "); + } else + dp->cdr_flags &= ~CDR_TAO; + + mp->write_type = WT_PACKET; + mp->track_mode |= TM_INCREMENTAL; +/* mp->fp = (trackp->pktsize > 0) ? 1 : 0;*/ +/* i_to_4_byte(mp->packet_size, trackp->pktsize);*/ + mp->fp = 0; + i_to_4_byte(mp->packet_size, 0); + + if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_PACKET; + if (xdebug) + printf("PACKET "); + } else + dp->cdr_flags &= ~CDR_PACKET; + mp->fp = 0; + i_to_4_byte(mp->packet_size, 0); + mp->track_mode = TM_DATA; + mp->write_type = WT_SAO; + + if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_SAO; + if (xdebug) + printf("SAO "); + } else + dp->cdr_flags &= ~CDR_SAO; + + if (dp->cdr_flags & CDR_SAO) { + mp->dbtype = DB_RAW_PQ; + +#ifdef __needed__ + if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_SRAW16; + if (xdebug) + printf("SAO/R16 "); + } +#endif + + mp->dbtype = DB_RAW_PW; + + if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_SRAW96P; + if (xdebug) + printf("SAO/R96P "); + } + + mp->dbtype = DB_RAW_PW_R; + + if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_SRAW96R; + if (xdebug) + printf("SAO/R96R "); + } + } + + mp->write_type = WT_RAW; + mp->dbtype = DB_RAW_PQ; + + if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_RAW; + dp->cdr_flags |= CDR_RAW16; + if (xdebug) + printf("RAW/R16 "); + } + + mp->dbtype = DB_RAW_PW; + + if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_RAW; + dp->cdr_flags |= CDR_RAW96P; + if (xdebug) + printf("RAW/R96P "); + } + + mp->dbtype = DB_RAW_PW_R; + + if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_RAW; + dp->cdr_flags |= CDR_RAW96R; + if (xdebug) + printf("RAW/R96R "); + } + + if (xdebug) + printf("\n"); + + /* + * Reset mp->test_write (-dummy) here. + */ + deflt_writemodes_mmc(usalp, TRUE); + usalp->silent--; + + return (0); +} + +int +check_writemodes_mdvd(SCSI *usalp, cdr_t *dp) +{ + Uchar mode[0x100]; + int len; + struct cd_mode_page_05 *mp; + + if (xdebug) + printf("Checking possible write modes: "); + + deflt_writemodes_mdvd(usalp, FALSE); + + fillbytes((caddr_t)mode, sizeof(mode), '\0'); + + usalp->silent++; + if (!get_mode_params(usalp, 0x05, "DVD write parameter", + mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) { + usalp->silent--; + return (-1); + } + if (len == 0) { + usalp->silent--; + return (-1); + } + + mp = (struct cd_mode_page_05 *) + (mode + sizeof(struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); + + mp->test_write = 0; + + /*We only check for PACKET and SAO since these are the only supported modes for DVD */ + /*XXX these checks are irrelevant because they are not medium sensitive. ie the device returns + error only when it does not support a given mode for ALL mediums. It should check using + GET CONFIGURATION command.*/ + + mp->write_type = WT_PACKET; + mp->fp = 0; + i_to_4_byte(mp->packet_size, 0); + + if (set_mode_params(usalp, "DVD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_PACKET; + if (xdebug) + printf("PACKET "); + } else + dp->cdr_flags &= ~CDR_PACKET; + mp->fp = 0; + i_to_4_byte(mp->packet_size, 0); + mp->track_mode = TM_DATA; + + + mp->write_type = WT_SAO; + + if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + dp->cdr_flags |= CDR_SAO; + if (xdebug) + printf("SAO "); + } else + dp->cdr_flags &= ~CDR_SAO; + + + if (xdebug) + printf("\n"); + + deflt_writemodes_mdvd(usalp, TRUE); + usalp->silent--; + return (0); +} + +static int +deflt_writemodes_mmc(SCSI *usalp, BOOL reset_dummy) +{ + Uchar mode[0x100]; + int len; + struct cd_mode_page_05 *mp; + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + usalp->silent++; + if (!get_mode_params(usalp, 0x05, "CD write parameter", + mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) { + usalp->silent--; + return (-1); + } + if (len == 0) { + usalp->silent--; + return (-1); + } + + mp = (struct cd_mode_page_05 *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); +#ifdef DEBUG + usal_prbytes("CD write parameter:", (Uchar *)mode, len); + fprintf(stderr, "Audio pause len: %d\n", a_to_2_byte(mp->audio_pause_len)); +#endif + + /* + * This is the only place where we reset mp->test_write (-dummy) + */ + if (reset_dummy) + mp->test_write = 0; + + /* + * Set default values: + * Write type = 01 (track at once) + * Track mode = 04 (CD-ROM) + * Data block type = 08 (CD-ROM) + * Session format = 00 (CD-ROM) + * + * XXX Note: the same code appears in check_writemodes_mmc() and + * XXX in speed_select_mmc(). + */ + mp->write_type = WT_TAO; + mp->track_mode = TM_DATA; + mp->dbtype = DB_ROM_MODE1; + mp->session_format = SES_DA_ROM; /* Matsushita has illegal def. value */ + + i_to_2_byte(mp->audio_pause_len, 150); /* LG has illegal def. value */ + +#ifdef DEBUG + usal_prbytes("CD write parameter:", (Uchar *)mode, len); +#endif + if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + + mp->write_type = WT_SAO; + mp->LS_V = 0; + mp->copy = 0; + mp->fp = 0; + mp->multi_session = MS_NONE; + mp->host_appl_code = 0; + + if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) { + usalp->silent--; + return (-1); + } + } + usalp->silent--; + return (0); +} + +static int +deflt_writemodes_mdvd(SCSI *usalp, BOOL reset_dummy) +{ + Uchar mode[0x100]; + int len; + struct cd_mode_page_05 *mp; + + fillbytes((caddr_t)mode, sizeof(mode), '\0'); + + usalp->silent++; + if (!get_mode_params(usalp, 0x05, "DVD write parameter", + mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) { + usalp->silent--; + return (-1); + } + if (len == 0) { + usalp->silent--; + return (-1); + } + + mp = (struct cd_mode_page_05 *) + (mode + sizeof(struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); + + mp->test_write = 0; + /* + * This is the only place where we reset mp->test_write (-dummy) for DVD + */ + if (reset_dummy) + mp->test_write = 0; + + /* + * Set default values: + * Write type = 02 (session at once) + * + * XXX Note: the same code appears in check_writemodes_mmc() and + * XXX in speed_select_mmc(). + */ + mp->write_type = WT_SAO; + mp->track_mode = TM_DATA; + mp->dbtype = DB_ROM_MODE1; + mp->session_format = SES_DA_ROM; + + + if (set_mode_params(usalp, "DVD write parameter", mode, len, 0, -1) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + return (0); +} + +#ifdef PRINT_ATIP +static void print_di(struct disk_info *dip); +static void atip_printspeed(char *fmt, int speedindex, char speedtab[]); +static void print_atip(SCSI *usalp, struct atipinfo *atp); +#endif /* PRINT_ATIP */ + +static int +get_diskinfo(SCSI *usalp, struct disk_info *dip) +{ + int len; + int ret; + + fillbytes((caddr_t)dip, sizeof (*dip), '\0'); + + /* + * Used to be 2 instead of 4 (now). But some Y2k ATAPI drives as used + * by IOMEGA create a DMA overrun if we try to transfer only 2 bytes. + */ +/* if (read_disk_info(usalp, (caddr_t)dip, 2) < 0)*/ + if (read_disk_info(usalp, (caddr_t)dip, 4) < 0) + return (-1); + len = a_to_u_2_byte(dip->data_len); + len += 2; + ret = read_disk_info(usalp, (caddr_t)dip, len); + +#ifdef DEBUG + usal_prbytes("Disk info:", (Uchar *)dip, + len-usal_getresid(usalp)); +#endif + return (ret); +} + +static void +di_to_dstat(struct disk_info *dip, dstat_t *dsp) +{ + dsp->ds_diskid = a_to_u_4_byte(dip->disk_id); + 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; + + dsp->ds_maxblocks = msf_to_lba(dip->last_lead_out[1], + dip->last_lead_out[2], + dip->last_lead_out[3], TRUE); + /* + * Check for 0xFF:0xFF/0xFF which is an indicator for a complete disk + */ + if (dsp->ds_maxblocks == 1166730) + dsp->ds_maxblocks = -1L; + + dsp->ds_first_leadin = msf_to_lba(dip->last_lead_in[1], + dip->last_lead_in[2], + dip->last_lead_in[3], FALSE); + if (dsp->ds_first_leadin > 0) + dsp->ds_first_leadin = 0; + + if (dsp->ds_last_leadout == 0 && dsp->ds_maxblocks >= 0) + dsp->ds_last_leadout = dsp->ds_maxblocks; + dsp->ds_trfirst=dip->first_track; + dsp->ds_trlast=dip->last_track_ls; + dsp->ds_trfirst_ls=dip->first_track_ls; +} + +static int +get_atip(SCSI *usalp, struct atipinfo *atp) +{ + int len; + int ret; + + fillbytes((caddr_t)atp, sizeof (*atp), '\0'); + + /* + * Used to be 2 instead of sizeof (struct tocheader), but all + * other places in the code use sizeof (struct tocheader) too and + * some Y2k ATAPI drives as used by IOMEGA create a DMA overrun if we + * try to transfer only 2 bytes. + */ + if (read_toc(usalp, (caddr_t)atp, 0, sizeof (struct tocheader), 0, FMT_ATIP) < 0) + return (-1); + len = a_to_u_2_byte(atp->hd.len); + len += 2; + ret = read_toc(usalp, (caddr_t)atp, 0, len, 0, FMT_ATIP); + +#ifdef DEBUG + usal_prbytes("ATIP info:", (Uchar *)atp, + len-usal_getresid(usalp)); +#endif + /* + * Yamaha sometimes returns zeroed ATIP info for disks without ATIP + */ + if (atp->desc.lead_in[1] == 0 && + atp->desc.lead_in[2] == 0 && + atp->desc.lead_in[3] == 0 && + atp->desc.lead_out[1] == 0 && + atp->desc.lead_out[2] == 0 && + atp->desc.lead_out[3] == 0) + return (-1); + + if (atp->desc.lead_in[1] >= 0x90 && debug) { + /* + * Only makes sense with buggy Ricoh firmware. + */ + errmsgno(EX_BAD, "Converting ATIP from BCD\n"); + atp->desc.lead_in[1] = from_bcd(atp->desc.lead_in[1]); + atp->desc.lead_in[2] = from_bcd(atp->desc.lead_in[2]); + atp->desc.lead_in[3] = from_bcd(atp->desc.lead_in[3]); + + atp->desc.lead_out[1] = from_bcd(atp->desc.lead_out[1]); + atp->desc.lead_out[2] = from_bcd(atp->desc.lead_out[2]); + atp->desc.lead_out[3] = from_bcd(atp->desc.lead_out[3]); + } + + return (ret); +} + +#ifdef PRINT_ATIP + +static int +get_pma(SCSI *usalp) +{ + int len; + int ret; + char atp[1024]; + + fillbytes((caddr_t)atp, sizeof (*atp), '\0'); + + /* + * Used to be 2 instead of sizeof (struct tocheader), but all + * other places in the code use sizeof (struct tocheader) too and + * some Y2k ATAPI drives as used by IOMEGA create a DMA overrun if we + * try to transfer only 2 bytes. + */ +/* if (read_toc(usalp, (caddr_t)atp, 0, 2, 1, FMT_PMA) < 0)*/ +/* if (read_toc(usalp, (caddr_t)atp, 0, 2, 0, FMT_PMA) < 0)*/ + if (read_toc(usalp, (caddr_t)atp, 0, sizeof (struct tocheader), 0, FMT_PMA) < 0) + return (-1); +/* len = a_to_u_2_byte(atp->hd.len);*/ + len = a_to_u_2_byte(atp); + len += 2; +/* ret = read_toc(usalp, (caddr_t)atp, 0, len, 1, FMT_PMA);*/ + ret = read_toc(usalp, (caddr_t)atp, 0, len, 0, FMT_PMA); + +#ifdef DEBUG + usal_prbytes("PMA:", (Uchar *)atp, + len-usal_getresid(usalp)); +#endif + ret = read_toc(usalp, (caddr_t)atp, 0, len, 1, FMT_PMA); + +#ifdef DEBUG + usal_prbytes("PMA:", (Uchar *)atp, + len-usal_getresid(usalp)); +#endif + return (ret); +} + +#endif /* PRINT_ATIP */ + +static int +init_mmc(SCSI *usalp, cdr_t *dp) +{ + return (speed_select_mmc(usalp, dp, NULL)); +} + +static int +getdisktype_mdvd(SCSI *usalp, cdr_t *dp) +{ + int ret = 0; + dstat_t *dsp = dp->cdr_dstat; + + struct track_info track_info; + if(lverbose) + printf("HINT: use dvd+rw-mediainfo from dvd+rw-tools for information extraction.\n"); + /* if(getdisktype_mmc(usalp, dp)<0) + return -1; + */ + + /* read rzone info to get the space left on disk */ + /*ds_trlast is the last rzone on disk, can be invisible */ + if(read_rzone_info(usalp, (caddr_t)&track_info, sizeof(track_info))>=0) + dsp->ds_maxblocks=a_to_u_4_byte(track_info.free_blocks)+a_to_4_byte(track_info.next_writable_addr); + + dsp->ds_disktype&= ~DT_CD; + dsp->ds_disktype|= DT_DVD; + + return (ret); + +} + +static int +getdisktype_mmc(SCSI *usalp, cdr_t *dp) +{ +extern char *buf; + dstat_t *dsp = dp->cdr_dstat; + struct disk_info *dip; + Uchar mode[0x100]; + msf_t msf; + BOOL did_atip = FALSE; + BOOL did_dummy = FALSE; + int rplus; + + msf.msf_min = msf.msf_sec = msf.msf_frame = 0; + + /* + * It seems that there are drives that do not support to + * read ATIP (e.g. HP 7100) + * Also if a NON CD-R media is inserted, this will never work. + * For this reason, make a failure non-fatal. + */ + usalp->silent++; + if (get_atip(usalp, (struct atipinfo *)mode) >= 0) { + struct atipinfo *atp = (struct atipinfo *)mode; + + msf.msf_min = mode[8]; + msf.msf_sec = mode[9]; + msf.msf_frame = mode[10]; + if (atp->desc.erasable) { + dsp->ds_flags |= DSF_ERA; + if (atp->desc.sub_type == 1) + dsp->ds_flags |= DSF_HIGHSP_ERA; + else if (atp->desc.sub_type == 2) + dsp->ds_flags |= DSF_ULTRASP_ERA; + else if (atp->desc.sub_type == 3) + dsp->ds_flags |= DSF_ULTRASP_ERA | DSF_ULTRASPP_ERA; + } + if (atp->desc.a1_v) { + if (atp->desc.clv_low != 0) + dsp->ds_at_min_speed = clv_to_speed[atp->desc.clv_low]; + if (atp->desc.clv_high != 0) + dsp->ds_at_max_speed = clv_to_speed[atp->desc.clv_high]; + + if (atp->desc.erasable && atp->desc.sub_type == 1) { + if (atp->desc.clv_high != 0) + dsp->ds_at_max_speed = hs_clv_to_speed[atp->desc.clv_high]; + } + } + if (atp->desc.a2_v && atp->desc.erasable && (atp->desc.sub_type == 2 || atp->desc.sub_type == 3)) { + Uint vlow; + Uint vhigh; + + vlow = (atp->desc.a2[0] >> 4) & 0x07; + vhigh = atp->desc.a2[0] & 0x0F; + if (vlow != 0) + dsp->ds_at_min_speed = us_clv_to_speed[vlow]; + if (vhigh != 0) + dsp->ds_at_max_speed = us_clv_to_speed[vhigh]; + } + did_atip = TRUE; + } + usalp->silent--; + +#ifdef PRINT_ATIP + if ((dp->cdr_dstat->ds_cdrflags & RF_PRATIP) != 0 && did_atip) { + print_atip(usalp, (struct atipinfo *)mode); + pr_manufacturer(&msf, + ((struct atipinfo *)mode)->desc.erasable, + ((struct atipinfo *)mode)->desc.uru); + } +#endif +again: + dip = (struct disk_info *)buf; + if (get_diskinfo(usalp, dip) < 0) + return (-1); + + /* + * Check for non writable disk first. + */ + + /* DVD+RW does not need to be blanked */ + rplus = dsp->ds_cdrflags; + if (dp->profile == 0x1A) rplus = RF_BLANK; + + if (dip->disk_status == DS_COMPLETE && + (rplus & dsp->ds_cdrflags & (RF_WRITE|RF_BLANK)) == RF_WRITE) { + 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_mmc(usalp, dp, &xspeed); + dp->cdr_cmdflags = oflags; + did_dummy = TRUE; + goto again; + } + /* + * Trying to clear drive status did not work... + */ + reload_media(usalp, dp); + } + if (get_diskinfo(usalp, dip) < 0) + return (-1); + di_to_dstat(dip, dsp); + if (!did_atip && dsp->ds_first_leadin < 0) + lba_to_msf(dsp->ds_first_leadin, &msf); + + if ((dp->cdr_dstat->ds_cdrflags & RF_PRATIP) != 0 && !did_atip) { + print_min_atip(dsp->ds_first_leadin, dsp->ds_last_leadout); + if (dsp->ds_first_leadin < 0) + pr_manufacturer(&msf, + dip->erasable, + dip->uru); + } + dsp->ds_maxrblocks = disk_rcap(&msf, dsp->ds_maxblocks, + dip->erasable, + dip->uru); + + +#ifdef PRINT_ATIP +#ifdef DEBUG + if (get_atip(usalp, (struct atipinfo *)mode) < 0) + return (-1); + /* + * Get pma gibt �rger mit CW-7502 + * Wenn Die Disk leer ist, dann stuerzt alles ab. + * Firmware 4.02 kann nicht get_pma + */ + if (dip->disk_status != DS_EMPTY) { +/* get_pma();*/ + } + printf("ATIP lead in: %ld (%02d:%02d/%02d)\n", + msf_to_lba(mode[8], mode[9], mode[10], FALSE), + mode[8], mode[9], mode[10]); + printf("ATIP lead out: %ld (%02d:%02d/%02d)\n", + msf_to_lba(mode[12], mode[13], mode[14], TRUE), + mode[12], mode[13], mode[14]); + print_di(dip); + print_atip(usalp, (struct atipinfo *)mode); +#endif +#endif /* PRINT_ATIP */ + return (drive_getdisktype(usalp, dp)); +} + +#ifdef PRINT_ATIP + +#define DOES(what, flag) printf(" Does %s%s\n", flag?"":"not ", what); +#define IS(what, flag) printf(" Is %s%s\n", flag?"":"not ", what); +#define VAL(what, val) printf(" %s: %d\n", what, val[0]*256 + val[1]); +#define SVAL(what, val) printf(" %s: %s\n", what, val); + +static void +print_di(struct disk_info *dip) +{ + static char *ds_name[] = { "empty", "incomplete/appendable", "complete", "illegal" }; + static char *ss_name[] = { "empty", "incomplete/appendable", "illegal", "complete", }; + + IS("erasable", dip->erasable); + printf("disk status: %s\n", ds_name[dip->disk_status]); + printf("session status: %s\n", ss_name[dip->sess_status]); + printf("first track: %d number of sessions: %d first track in last sess: %d last track in last sess: %d\n", + dip->first_track, + dip->numsess, + dip->first_track_ls, + dip->last_track_ls); + IS("unrestricted", dip->uru); + printf("Disk type: "); + switch (dip->disk_type) { + + case SES_DA_ROM: printf("CD-DA or CD-ROM"); break; + case SES_CDI: printf("CDI"); break; + case SES_XA: printf("CD-ROM XA"); break; + case SES_UNDEF: printf("undefined"); break; + default: printf("reserved"); break; + } + printf("\n"); + if (dip->did_v) + printf("Disk id: 0x%lX\n", a_to_u_4_byte(dip->disk_id)); + + printf("last start of lead in: %ld\n", + msf_to_lba(dip->last_lead_in[1], + dip->last_lead_in[2], + dip->last_lead_in[3], FALSE)); + printf("last start of lead out: %ld\n", + msf_to_lba(dip->last_lead_out[1], + dip->last_lead_out[2], + dip->last_lead_out[3], TRUE)); + + if (dip->dbc_v) + printf("Disk bar code: 0x%lX%lX\n", + a_to_u_4_byte(dip->disk_barcode), + a_to_u_4_byte(&dip->disk_barcode[4])); + + if (dip->num_opc_entries > 0) { + printf("OPC table:\n"); + } +} + +char *cdr_subtypes[] = { + "Normal Rewritable (CLV) media", + "High speed Rewritable (CAV) media", + "Medium Type A, low Beta category (A-)", + "Medium Type A, high Beta category (A+)", + "Medium Type B, low Beta category (B-)", + "Medium Type B, high Beta category (B+)", + "Medium Type C, low Beta category (C-)", + "Medium Type C, high Beta category (C+)", +}; + +char *cdrw_subtypes[] = { + "Normal Rewritable (CLV) media", + "High speed Rewritable (CAV) media", + "Ultra High speed Rewritable media", + "Ultra High speed+ Rewritable media", + "Medium Type B, low Beta category (B-)", + "Medium Type B, high Beta category (B+)", + "Medium Type C, low Beta category (C-)", + "Medium Type C, high Beta category (C+)", +}; + +static void +atip_printspeed(char *fmt, int speedindex, char speedtab[]) +{ + printf("%s:", fmt); + if (speedtab[speedindex] == 0) { + printf(" %2d (reserved val %2d)", + speedtab[speedindex], speedindex); + } else { + printf(" %2d", speedtab[speedindex]); + } +} + +static void +print_atip(SCSI *usalp, struct atipinfo *atp) +{ + char *sub_type; + char *speedvtab = clv_to_speed; + + if (usalp->verbose) + usal_prbytes("ATIP info: ", (Uchar *)atp, sizeof (*atp)); + + printf("ATIP info from disk:\n"); + printf(" Indicated writing power: %d\n", atp->desc.ind_wr_power); + if (atp->desc.erasable || atp->desc.ref_speed) + printf(" Reference speed: %d\n", clv_to_speed[atp->desc.ref_speed]); + IS("unrestricted", atp->desc.uru); +/* printf(" Disk application code: %d\n", atp->desc.res5_05);*/ + IS("erasable", atp->desc.erasable); + if (atp->desc.erasable) + sub_type = cdrw_subtypes[atp->desc.sub_type]; + else + sub_type = cdr_subtypes[atp->desc.sub_type]; + if (atp->desc.sub_type) + printf(" Disk sub type: %s (%d)\n", sub_type, atp->desc.sub_type); + printf(" ATIP start of lead in: %ld (%02d:%02d/%02d)\n", + msf_to_lba(atp->desc.lead_in[1], + atp->desc.lead_in[2], + atp->desc.lead_in[3], FALSE), + atp->desc.lead_in[1], + atp->desc.lead_in[2], + atp->desc.lead_in[3]); + printf(" ATIP start of lead out: %ld (%02d:%02d/%02d)\n", + msf_to_lba(atp->desc.lead_out[1], + atp->desc.lead_out[2], + atp->desc.lead_out[3], TRUE), + atp->desc.lead_out[1], + atp->desc.lead_out[2], + atp->desc.lead_out[3]); + if (atp->desc.a1_v) { + if (atp->desc.erasable && atp->desc.sub_type == 1) { + speedvtab = hs_clv_to_speed; + } + if (atp->desc.a2_v && (atp->desc.sub_type == 2 || atp->desc.sub_type == 3)) { + speedvtab = us_clv_to_speed; + } + if (atp->desc.clv_low != 0 || atp->desc.clv_high != 0) { + atip_printspeed(" 1T speed low", + atp->desc.clv_low, speedvtab); + atip_printspeed(" 1T speed high", + atp->desc.clv_high, speedvtab); + printf("\n"); + } + } + if (atp->desc.a2_v) { + Uint vlow; + Uint vhigh; + + vlow = (atp->desc.a2[0] >> 4) & 0x07; + vhigh = atp->desc.a2[0] & 0x0F; + + if (vlow != 0 || vhigh != 0) { + atip_printspeed(" 2T speed low", + vlow, speedvtab); + atip_printspeed(" 2T speed high", + vhigh, speedvtab); + printf("\n"); + } + } + if (atp->desc.a1_v) { + printf(" power mult factor: %d %d\n", atp->desc.power_mult, atp->desc.tgt_y_pow); + if (atp->desc.erasable) + printf(" recommended erase/write power: %d\n", atp->desc.rerase_pwr_ratio); + } + if (atp->desc.a1_v) { + printf(" A1 values: %02X %02X %02X\n", + (&atp->desc.res15)[1], + (&atp->desc.res15)[2], + (&atp->desc.res15)[3]); + } + if (atp->desc.a2_v) { + printf(" A2 values: %02X %02X %02X\n", + atp->desc.a2[0], + atp->desc.a2[1], + atp->desc.a2[2]); + } + if (atp->desc.a3_v) { + printf(" A3 values: %02X %02X %02X\n", + atp->desc.a3[0], + atp->desc.a3[1], + atp->desc.a3[2]); + } +} +#endif /* PRINT_ATIP */ + +static int +speed_select_mmc(SCSI *usalp, 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; + BOOL forcespeed = FALSE; + BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0; + + if (speedp) + curspeed = *speedp; + + /* + * Do not reset mp->test_write (-dummy) here. + */ + deflt_writemodes_mmc(usalp, FALSE); + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + if (!get_mode_params(usalp, 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 + usal_prbytes("CD write parameter:", (Uchar *)mode, len); +#endif + + if(dummy) { + mp->test_write = 1; + /* but it does not work on DVD+RW and -RAM, also bail out on other + * types that have not been tested yet */ + int profile=get_curprofile(usalp); + switch(profile) { + case(0x12): + case(0x1a): + case(0x2a): + case(0x43): + case(0x52): + { + fprintf(stderr, + "Dummy mode not possible with %s.\n", + mmc_obtain_profile_name(profile) ); + exit(EXIT_FAILURE); + } + } + } + else + mp->test_write = 0; + +#ifdef DEBUG + usal_prbytes("CD write parameter:", (Uchar *)mode, len); +#endif + if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) + return (-1); + + /* + * Neither set nor get speed. + */ + if (speedp == 0) + return (0); + + + rp = get_justlink_ricoh(usalp, moder); + if (mmc_isyamaha(usalp)) { + forcespeed = FALSE; + } else if (mmc_isplextor(usalp) && (dp->cdr_flags & CDR_FORCESPEED) != 0) { + int pwr; + + pwr = check_powerrec_plextor(usalp); + if (pwr >= 0) + forcespeed = (pwr == 0); + } else 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 (mmc_isplextor(usalp) && (dp->cdr_flags & CDR_FORCESPEED) != 0) { + powerrec_plextor(usalp, !forcespeed); + } + if (!mmc_isyamaha(usalp) && (dp->cdr_flags & CDR_FORCESPEED) != 0) { + + if (rp) { + rp->AWSCD = forcespeed?1:0; + set_mode_params(usalp, "Ricoh Vendor Page", moder, moder[0]+1, 0, -1); + rp = get_justlink_ricoh(usalp, moder); + } + } + + /* + * 44100 * 2 * 2 = 176400 bytes/s + * + * The right formula would be: + * tmp = (((long)curspeed) * 1764) / 10; + * + * But the standard is rounding the wrong way. + * Furtunately rounding down is guaranteed. + */ + val = curspeed*177; + if (val > 0xFFFF) + val = 0xFFFF; + if (mmc_isyamaha(usalp) && forcespeed) { + if (force_speed_yamaha(usalp, -1, val) < 0) + return (-1); + } else if (mmc_set_speed(usalp, -1, val, ROTCTL_CLV) < 0) { + return (-1); + } + + if (scsi_get_speed(usalp, 0, &val) >= 0) { + if (val > 0) { + fprintf(stderr, "Speed set to %d KB/s\n", val); + curspeed = val / 176; + *speedp = curspeed; + } + } + return (0); +} + +/* + * Some drives do not round up when writespeed is e.g. 1 and + * the minimum write speed of the drive is higher. Try to increment + * the write speed unti it gets accepted by the drive. + */ +static int +mmc_set_speed(SCSI *usalp, int readspeed, int writespeed, int rotctl) +{ + int rs; + int ws; + int ret = -1; + int c; + int k; + + if (scsi_get_speed(usalp, &rs, &ws) >= 0) { + if (readspeed < 0) + readspeed = rs; + if (writespeed < 0) + writespeed = ws; + } + if (writespeed < 0 || writespeed > 0xFFFF) + return (ret); + + usalp->silent++; + while (writespeed <= 0xFFFF) { + ret = scsi_set_speed(usalp, readspeed, writespeed, rotctl); + if (ret >= 0) + break; + c = usal_sense_code(usalp); + k = usal_sense_key(usalp); + /* + * Abort quickly if it does not make sense to repeat. + * 0x24 == Invalid field in cdb + * 0x24 means illegal speed. + */ + if ((k != SC_ILLEGAL_REQUEST) || (c != 0x24)) { + if (usalp->silent <= 1) + usal_printerr(usalp); + usalp->silent--; + return (-1); + } + writespeed += 177; + } + if (ret < 0 && usalp->silent <= 1) + usal_printerr(usalp); + usalp->silent--; + + return (ret); +} + +static int +speed_select_mdvd(SCSI *usalp, cdr_t *dp, int *speedp) +{ + int retcode; + char perf_desc[28]; + int write_speed = *speedp * 1385; + + /* For the moment we just divide the CD speed by 7*/ + + if(speedp!=NULL) + (*speedp)=(*speedp)*8; + + memset(perf_desc, 0, sizeof(perf_desc)); + + /* Write Rotation Control = ROTCTL_CLV + * | Restore Logical Unit Defaults = 0 + * | Exact = 0 + * | Random Access = 0) + */ + perf_desc[0]= ROTCTL_CLV << 3 | 0 << 2 | 0 << 1 | 0; + /* Start LBA to 0 */ + perf_desc[4] = 0; + perf_desc[5] = 0; + perf_desc[6] = 0; + perf_desc[7] = 0; + /* End LBA set to 0 (setting to 0xffffffff failed on my LG burner + */ + perf_desc[8] = 0; + perf_desc[9] = 0; + perf_desc[10] = 0; + perf_desc[11] = 0; + /* Read Speed = 0xFFFF */ + perf_desc[12] = 0; + perf_desc[13] = 0; + perf_desc[14] = 0xFF; + perf_desc[15] = 0xFF; + /* Read Time = 1s */ + perf_desc[18] = 1000 >> 8; + perf_desc[19] = 1000 & 0xFF; + /* Write Speed */ + perf_desc[20] = write_speed >> 24; + perf_desc[21] = write_speed >> 16 & 0xFF; + perf_desc[22] = write_speed >> 8 & 0xFF; + perf_desc[23] = write_speed & 0xFF; + /* Write Time = 1s */ + perf_desc[26] = 1000 >> 8; + perf_desc[27] = 1000 & 0xFF; + + /* retcode = scsi_set_streaming(usalp, NULL, 0); */ + retcode = scsi_set_streaming(usalp, perf_desc, sizeof(perf_desc)); + if (retcode == -1) return retcode; + retcode = speed_select_mmc(usalp, dp, speedp); + if(speedp!=NULL) + (*speedp)=(*speedp)/7; + return retcode; +} + +static int +next_wr_addr_mmc(SCSI *usalp, track_t *trackp, long *ap) +{ + struct track_info track_info; + long next_addr; + int result = -1; + + + /* + * Reading info for current track may require doing the read_track_info + * with either the track number (if the track is currently being written) + * or with 0xFF (if the track hasn't been started yet and is invisible + */ + + if (trackp != 0 && trackp->track > 0 && is_packet(trackp)) { + usalp->silent++; + result = read_track_info(usalp, (caddr_t)&track_info, TI_TYPE_TRACK, + trackp->trackno, + sizeof (track_info)); + usalp->silent--; + } + + if (result < 0) { + if (read_track_info(usalp, (caddr_t)&track_info, TI_TYPE_TRACK, 0xFF, + sizeof (track_info)) < 0) { + errmsgno(EX_BAD, "Cannot get next writable address for 'invisible' track.\n"); + errmsgno(EX_BAD, "This means that we are checking recorded media.\n"); + errmsgno(EX_BAD, "This media cannot be written in streaming mode anymore.\n"); + errmsgno(EX_BAD, "If you like to write to 'preformatted' RW media, try to blank the media first.\n"); + return (-1); + } + } + if (usalp->verbose) + usal_prbytes("track info:", (Uchar *)&track_info, + sizeof (track_info)-usal_getresid(usalp)); + next_addr = a_to_4_byte(track_info.next_writable_addr); + if (ap) + *ap = next_addr; + return (0); +} + +static int +write_leadin_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + Uint i; + long startsec = 0L; + +/* if (flags & F_SAO) {*/ + if (wm_base(dp->cdr_dstat->ds_wrmode) == WM_SAO) { + if (debug || lverbose) { + printf("Sending CUE sheet...\n"); + flush(); + } + if ((*dp->cdr_send_cue)(usalp, dp, trackp) < 0) { + errmsgno(EX_BAD, "Cannot send CUE sheet.\n"); + return (-1); + } + + (*dp->cdr_next_wr_address)(usalp, &trackp[0], &startsec); + if (trackp[0].flags & TI_TEXT) { + startsec = dp->cdr_dstat->ds_first_leadin; + printf("SAO startsec: %ld\n", startsec); + } else if (startsec <= 0 && startsec != -150) { + if(lverbose>2) + fprintf(stderr, "WARNING: Drive returns wrong startsec (%ld) using -150\n", + startsec); + startsec = -150; + } + if (debug) + printf("SAO startsec: %ld\n", startsec); + + if (trackp[0].flags & TI_TEXT) { + if (startsec > 0) { + errmsgno(EX_BAD, "CD-Text must be in first session.\n"); + return (-1); + } + if (debug || lverbose) + printf("Writing lead-in...\n"); + if (write_cdtext(usalp, dp, startsec) < 0) + return (-1); + + dp->cdr_dstat->ds_cdrflags |= RF_LEADIN; + } else for (i = 1; i <= trackp->tracks; i++) { + trackp[i].trackstart += startsec +150; + } +#ifdef XXX + if (debug || lverbose) + printf("Writing lead-in...\n"); + + pad_track(usalp, dp, &trackp[1], -150, (Llong)0, + FALSE, 0); +#endif + } +/* if (flags & F_RAW) {*/ + if (wm_base(dp->cdr_dstat->ds_wrmode) == WM_RAW) { + /* + * In RAW write mode, we now write the lead in (TOC). + */ + (*dp->cdr_next_wr_address)(usalp, &trackp[0], &startsec); + if (startsec > -4500) { + /* + * There must be at least 1 minute lead-in. + */ + errmsgno(EX_BAD, "WARNING: Drive returns wrong startsec (%ld) using %ld from ATIP\n", + startsec, (long)dp->cdr_dstat->ds_first_leadin); + startsec = dp->cdr_dstat->ds_first_leadin; + } + if (startsec > -4500) { + errmsgno(EX_BAD, "Illegal startsec (%ld)\n", startsec); + return (-1); + } + if (debug || lverbose) + printf("Writing lead-in at sector %ld\n", startsec); + if (write_leadin(usalp, dp, trackp, startsec) < 0) + return (-1); + dp->cdr_dstat->ds_cdrflags |= RF_LEADIN; + } + return (0); +} + +int st2mode[] = { + 0, /* 0 */ + TM_DATA, /* 1 ST_ROM_MODE1 */ + TM_DATA, /* 2 ST_ROM_MODE2 */ + 0, /* 3 */ + 0, /* 4 ST_AUDIO_NOPRE */ + TM_PREEM, /* 5 ST_AUDIO_PRE */ + 0, /* 6 */ + 0, /* 7 */ +}; + +static int +next_wr_addr_mdvd(SCSI *usalp, track_t *trackp, long *ap) +{ + int track=0; + struct track_info track_info; + long next_addr; + int result = -1; + struct disk_info disk_info; + if (trackp){ + track = trackp->trackno; + } + + if (trackp != 0 && track > 0 && is_packet(trackp)) { + usalp->silent++; + result = read_track_info(usalp, (caddr_t)&track_info, TI_TYPE_SESS, track, sizeof(track_info)); + usalp->silent--; + if (scsi_in_progress(usalp)){ + return -1; + } + + } + + if (result < 0) { + /* Get the last rzone*/ + if(read_disk_info(usalp,(caddr_t)&disk_info,8)<0) + return (-1); + + /* if (read_track_info(usalp, (caddr_t)&track_info, TI_TYPE_SESS, 0xFF, sizeof(track_info)) < 0) */ + if (read_rzone_info(usalp, (caddr_t)&track_info, sizeof(track_info)) < 0) + return (-1); + } + if (usalp->verbose) + usal_prbytes("track info:", (Uchar *)&track_info, + sizeof(track_info)-usal_getresid(usalp)); + next_addr = a_to_4_byte(track_info.next_writable_addr); + if (ap) + *ap = next_addr; + return (0); +} + +static int +open_track_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + Uchar mode[0x100]; + int len; + struct cd_mode_page_05 *mp; + + if (!is_tao(trackp) && !is_packet(trackp)) { + if (trackp->pregapsize > 0 && (trackp->flags & TI_PREGAP) == 0) { + if (lverbose) { + printf("Writing pregap for track %d at %ld\n", + (int)trackp->trackno, + trackp->trackstart-trackp->pregapsize); + } + /* + * XXX Do we need to check isecsize too? + */ + pad_track(usalp, dp, trackp, + trackp->trackstart-trackp->pregapsize, + (Llong)trackp->pregapsize*trackp->secsize, + FALSE, 0); + } + return (0); + } + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + if (!get_mode_params(usalp, 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); + + +/* mp->track_mode = ???;*/ + mp->track_mode = st2mode[trackp->sectype & ST_MASK]; +/* mp->copy = ???;*/ + mp->dbtype = trackp->dbtype; + +/*i_to_short(mp->audio_pause_len, 300);*/ +/*i_to_short(mp->audio_pause_len, 150);*/ +/*i_to_short(mp->audio_pause_len, 0);*/ + + if (is_packet(trackp)) { + mp->write_type = WT_PACKET; + mp->track_mode |= TM_INCREMENTAL; + mp->fp = (trackp->pktsize > 0) ? 1 : 0; + i_to_4_byte(mp->packet_size, trackp->pktsize); + } else if (is_tao(trackp)) { + mp->write_type = WT_TAO; + mp->fp = 0; + i_to_4_byte(mp->packet_size, 0); + } else { + errmsgno(EX_BAD, "Unknown write mode.\n"); + return (-1); + } + if (trackp->isrc) { + mp->ISRC[0] = 0x80; /* Set ISRC valid */ + strncpy((char *)&mp->ISRC[1], trackp->isrc, 12); + + } else { + fillbytes(&mp->ISRC[0], sizeof (mp->ISRC), '\0'); + } + +#ifdef DEBUG + usal_prbytes("CD write parameter:", (Uchar *)mode, len); +#endif + if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, trackp->secsize)) + return (-1); + + return (0); +} + +static int +open_track_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + Uchar mode[0x100]; + int len; + struct cd_mode_page_05 *mp; + + if (is_packet(trackp)) { + fillbytes((caddr_t)mode, sizeof(mode), '\0'); + + if (!get_mode_params(usalp, 0x05, "DVD 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); + + mp->write_type = WT_PACKET; + mp->LS_V = 1; + /*For now we set the link size to 0x10(32k) because Pioneer-A03 only support this */ + mp->link_size=0x10; + mp->fp = 1; + i_to_4_byte(mp->packet_size, trackp->pktsize); + } else { + return 0; + } + + if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, trackp->secsize)) + return (-1); + + return (0); +} + +static int +close_track_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + int ret; + + if (!is_tao(trackp) && !is_packet(trackp)) + return (0); + + if (scsi_flush_cache(usalp, (dp->cdr_cmdflags&F_IMMED) != 0) < 0) { + printf("Trouble flushing the cache\n"); + return (-1); + } + wait_unit_ready(usalp, 300); /* XXX Wait for ATAPI */ + if (is_packet(trackp) && !is_noclose(trackp)) { + /* close the incomplete track */ + ret = scsi_close_tr_session(usalp, CL_TYPE_TRACK, 0xFF, + (dp->cdr_cmdflags&F_IMMED) != 0); + wait_unit_ready(usalp, 300); /* XXX Wait for ATAPI */ + return (ret); + } + return (0); +} + +static int +close_track_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + int ret; + if (!is_packet(trackp)) + return (0); + + if (scsi_flush_cache(usalp, (dp->cdr_cmdflags&F_IMMED) != 0) < 0) { + printf("Trouble flushing the cache\n"); + return -1; + } + wait_unit_ready(usalp, 300); /* XXX Wait for ATAPI */ + if (is_packet(trackp) && !is_noclose(trackp)) { + /* close the incomplete track */ + ret = scsi_close_tr_session(usalp, 1, 0xFF, (dp->cdr_cmdflags&F_IMMED) != 0); + wait_unit_ready(usalp, 300); /* XXX Wait for ATAPI */ + return (ret); + } + return (0); +} + +int toc2sess[] = { + SES_DA_ROM, /* CD-DA */ + SES_DA_ROM, /* CD-ROM */ + SES_XA, /* CD-ROM XA mode 1 */ + SES_XA, /* CD-ROM XA MODE 2 */ + SES_CDI, /* CDI */ + SES_DA_ROM, /* Invalid - use default */ + SES_DA_ROM, /* Invalid - use default */ +}; + +static int +open_session_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + Uchar mode[0x100]; + int len; + struct cd_mode_page_05 *mp; + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + if (!get_mode_params(usalp, 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); + + mp->write_type = WT_TAO; /* fix to allow DAO later */ + /* + * We need to set the right dbtype here because Sony drives + * don't like multi session in to be set with DB_ROM_MODE1 + * which is set by us at the beginning as default as some drives + * have illegal default values. + */ + mp->track_mode = st2mode[trackp[0].sectype & ST_MASK]; + mp->dbtype = trackp[0].dbtype; + + if (!is_tao(trackp) && !is_packet(trackp)) { + mp->write_type = WT_SAO; + if (dp->cdr_dstat->ds_cdrflags & RF_AUDIOMASTER) + mp->write_type = 8; + mp->track_mode = 0; + mp->dbtype = DB_RAW; + } + if (is_raw(trackp)) { + mp->write_type = WT_RAW; + mp->track_mode = 0; + + if (is_raw16(trackp)) { + mp->dbtype = DB_RAW_PQ; + } else if (is_raw96r(trackp)) { + mp->dbtype = DB_RAW_PW_R; + } else { + mp->dbtype = DB_RAW_PW; + } + } + + mp->multi_session = (track_base(trackp)->tracktype & TOCF_MULTI) ? + MS_MULTI : MS_NONE; + mp->session_format = toc2sess[track_base(trackp)->tracktype & TOC_MASK]; + + if (trackp->isrc) { + mp->media_cat_number[0] = 0x80; /* Set MCN valid */ + strncpy((char *)&mp->media_cat_number[1], trackp->isrc, 13); + + } else { + fillbytes(&mp->media_cat_number[0], sizeof (mp->media_cat_number), '\0'); + } +#ifdef DEBUG + usal_prbytes("CD write parameter:", (Uchar *)mode, len); +#endif + if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) + return (-1); + + return (0); +} + +static int +open_session_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + Uchar mode[0x100]; + int tracks = trackp->tracks; + + int len; + struct cd_mode_page_05 *mp; + Ulong totalsize; + int i; + int profile; + + fillbytes((caddr_t)mode, sizeof(mode), '\0'); + + if (!get_mode_params(usalp, 0x05, "DVD 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); + if(is_packet(trackp)){ + mp->write_type=WT_PACKET; + mp->fp=0; + mp->BUFE=1; + mp->track_mode=1; + }else{ + mp->write_type = WT_SAO; + } + + mp->multi_session = (track_base(trackp)->tracktype & TOCF_MULTI) ? + MS_MULTI : MS_NONE; + mp->session_format = toc2sess[track_base(trackp)->tracktype & TOC_MASK]; + + /* Enable Burnfree by default, allow to disable. XXX Sucks, duplicated functionality. */ + if (dp->cdr_cdcap->BUF != 0) { + if (lverbose > 2) + fprintf(stderr, + "BURN-Free is %s.\n" + "Turning BURN-Free on\n", + mp->BUFE?"ON":"OFF"); + mp->BUFE = 1; + } + if (driveropts != NULL) { + if ((strcmp(driveropts, "noburnproof") == 0 || + strcmp(driveropts, "noburnfree") == 0)) { + if(lverbose>1) + fprintf(stderr, "Turning BURN-Free off\n"); + mp->BUFE = 0; + } + else if ((strcmp(driveropts, "burnproof") == 0 || + strcmp(driveropts, "burnfree") == 0)) { + /* a NOP, we enable burnfree by default */ + if(lverbose>2) + fprintf(stderr, "Found burnproof/burnfree in driveropts, those options are enabled by default now."); + } + else if (strcmp(driveropts, "help") == 0) { + mmc_opthelp(dp, 0); + } + else { + errmsgno(EX_BAD, "Bad driver opts '%s'.\n", driveropts); + mmc_opthelp(dp, EX_BAD); + } + } + + + if (!set_mode_params(usalp, "DVD write parameter", mode, len, 0, -1)) + return (-1); + + + totalsize=0; + for(i=1;i<=tracks;i++) { + totalsize+=trackp[i].tracksecs; + } + + profile = get_curprofile(usalp); + if(!is_packet(trackp) && profile != 0x1A){ + /* in DAO mode we need to reserve space for the track*/ + if(reserve_track(usalp, totalsize)<0) + return (-1); + } + return (0); +} + +static int +waitfix_mmc(SCSI *usalp, int secs) +{ + char dibuf[16]; + int i; + int key; +#define W_SLEEP 2 + + usalp->silent++; + for (i = 0; i < secs/W_SLEEP; i++) { + if (read_disk_info(usalp, dibuf, sizeof (dibuf)) >= 0) { + usalp->silent--; + return (0); + } + key = usal_sense_key(usalp); + if (key != SC_UNIT_ATTENTION && key != SC_NOT_READY) + break; + sleep(W_SLEEP); + } + usalp->silent--; + return (-1); +#undef W_SLEEP +} + +static int +fixate_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + int ret = 0; + int key = 0; + int code = 0; + struct timeval starttime; + struct timeval stoptime; + int dummy = (track_base(trackp)->tracktype & TOCF_DUMMY) != 0; + + if(debug) + printf("fixate_mmc\n"); + starttime.tv_sec = 0; + starttime.tv_usec = 0; + stoptime = starttime; + gettimeofday(&starttime, (struct timezone *)0); + + if (dummy && lverbose) + printf("WARNING: Some drives don't like fixation in dummy mode.\n"); + + usalp->silent++; + if(debug) + printf("is_tao: %d,is_packet: %d\n", is_tao(trackp), is_packet(trackp)); + if (is_tao(trackp) || is_packet(trackp)) { + ret = scsi_close_tr_session(usalp, CL_TYPE_SESSION, 0, + (dp->cdr_cmdflags&F_IMMED) != 0); + } else { + if (scsi_flush_cache(usalp, (dp->cdr_cmdflags&F_IMMED) != 0) < 0) { + if (!scsi_in_progress(usalp)) + printf("Trouble flushing the cache\n"); + } + } + usalp->silent--; + key = usal_sense_key(usalp); + code = usal_sense_code(usalp); + + usalp->silent++; + if (debug && !unit_ready(usalp)) { + fprintf(stderr, "Early return from fixating. Ret: %d Key: %d, Code: %d\n", ret, key, code); + } + usalp->silent--; + + if (ret >= 0) { + wait_unit_ready(usalp, 420/curspeed); /* XXX Wait for ATAPI */ + waitfix_mmc(usalp, 420/curspeed); /* XXX Wait for ATAPI */ + return (ret); + } + + if ((dummy != 0 && (key != SC_ILLEGAL_REQUEST)) || + /* + * Try to suppress messages from drives that don't like fixation + * in -dummy mode. + */ + ((dummy == 0) && + (((key != SC_UNIT_ATTENTION) && (key != SC_NOT_READY)) || + ((code != 0x2E) && (code != 0x04))))) { + /* + * UNIT ATTENTION/2E seems to be a magic for old Mitsumi ATAPI drives + * NOT READY/ code 4 qual 7 (logical unit not ready, operation in progress) + * seems to be a magic for newer Mitsumi ATAPI drives + * NOT READY/ code 4 qual 8 (logical unit not ready, long write in progress) + * seems to be a magic for SONY drives + * when returning early from fixating. + * Try to supress the error message in this case to make + * simple minded users less confused. + */ + usal_printerr(usalp); + usal_printresult(usalp); /* XXX restore key/code in future */ + } + + if (debug && !unit_ready(usalp)) { + fprintf(stderr, "Early return from fixating. Ret: %d Key: %d, Code: %d\n", ret, key, code); + } + + wait_unit_ready(usalp, 420); /* XXX Wait for ATAPI */ + waitfix_mmc(usalp, 420/curspeed); /* XXX Wait for ATAPI */ + + if (!dummy && + (ret >= 0 || (key == SC_UNIT_ATTENTION && code == 0x2E))) { + /* + * Some ATAPI drives (e.g. Mitsumi) imply the + * IMMED bit in the SCSI cdb. As there seems to be no + * way to properly check for the real end of the + * fixating process we wait for the expected time. + */ + gettimeofday(&stoptime, (struct timezone *)0); + timevaldiff(&starttime, &stoptime); + if (stoptime.tv_sec < (220 / curspeed)) { + unsigned secs; + + if (lverbose) { + printf("Actual fixating time: %ld seconds\n", + (long)stoptime.tv_sec); + } + secs = (280 / curspeed) - stoptime.tv_sec; + if (lverbose) { + printf("ATAPI early return: sleeping %d seconds.\n", + secs); + } + sleep(secs); + } + } + return (ret); +} + +static int +fixate_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + int ret; + if (scsi_flush_cache(usalp, (dp->cdr_cmdflags&F_IMMED) != 0) < 0) { + printf("Trouble flushing the cache\n"); + return -1; + } + wait_unit_ready(usalp, 300); /* XXX Wait for ATAPI */ + /*set a really BIG timeout and call fixate_mmc + The BIG timeout is needed in case there was a very short rzone to write at the + beginning of the disk, because lead-out needs to be at some distance. + */ + if(debug) + printf("fixate_mdvd\n"); + usal_settimeout(usalp, 1000); + if(is_packet(trackp) || dp->profile == 0x1B){ + scsi_close_tr_session(usalp, CL_TYPE_SESSION, 0, FALSE); + } + ret = fixate_mmc(usalp, dp, trackp); + if (dp->profile == 0x2B) { + scsi_close_tr_session(usalp, CL_TYPE_OPEN_SESSION, 0, FALSE); + scsi_close_tr_session(usalp, CL_TYPE_FINALISE_MINRAD, 0, FALSE); + } + usal_settimeout(usalp, 200); + + return ret; +} + +char *blank_types[] = { + "entire disk", + "PMA, TOC, pregap", + "incomplete track", + "reserved track", + "tail of track", + "closing of last session", + "last session", + "reserved blanking type", +}; + +char *format_types[] = { + "full format", + "background format", + "forced format", +}; + +static int +blank_mmc(SCSI *usalp, 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 */ + int ret; + + mmc_check(usalp, &cdrr, &cdwr, &cdrrw, &cdwrw, NULL, NULL); + if (!cdwrw) + return (blank_dummy(usalp, dp, addr, blanktype)); + + if (dp->profile == 0x1A) { + printf("Error: this media does not support blanking, ignoring.\n"); + return (blank_dummy(usalp, dp, addr, blanktype)); + } + if (lverbose) { + printf("Blanking %s\n", blank_types[blanktype & 0x07]); + flush(); + } + + ret = scsi_blank(usalp, addr, blanktype, (dp->cdr_cmdflags&F_IMMED) != 0); + if (ret < 0) + return (ret); + + wait_unit_ready(usalp, 90*60/curspeed); /* XXX Wait for ATAPI */ + waitfix_mmc(usalp, 90*60/curspeed); /* XXX Wait for ATAPI */ + return (ret); +} + +static int format_mdvd(SCSI *usalp, cdr_t *dp, int formattype) +{ +extern char *buf; + BOOL dvdwr = FALSE; /* Write DVD */ + int ret; + int profile; + char addr[12]; + struct disk_info *dip; + + if (debug || lverbose > 2) + printf("format_mdvd\n"); + mmc_check(usalp, NULL, NULL, NULL, NULL, NULL, &dvdwr); + if (!dvdwr) + return (format_dummy(usalp, dp, formattype)); + + if (debug || lverbose > 2) + printf("format_mdvd: drive is a dvd burner.\n"); + profile = get_curprofile(usalp); + if (profile != 0x1A) { + printf("Error: only support DVD+RW formating, ignoring.\n"); + return (format_dummy(usalp, dp, formattype)); + } + dip = (struct disk_info *)buf; + if (get_diskinfo(usalp, dip) < 0) + return -1; + + if (dip->disk_status & 3 && formattype != FORCE_FORMAT) { + printf("Error: disk already formated, ignoring.\n"); + return -1; + } + addr[0] = 0; /* "Reserved" */ + addr[1] = 2; /* "IMMED" flag */ + addr[2] = 0; /* "Descriptor Length" (MSB) */ + addr[3] = 8; /* "Descriptor Length" (LSB) */ + addr[4+0] = 0xff; + addr[4+1] = 0xff; + addr[4+2] = 0xff; + addr[4+3] = 0xff; + addr[4+4] = 0x26<<2; + addr[4+5] = 0; + addr[4+6] = 0; + addr[4+7] = 0; + if (formattype == FORCE_FORMAT) { + printf("format_mdvd: forcing reformat.\n"); + formattype = FULL_FORMAT; + addr[4+0] = 0; + addr[4+1] = 0; + addr[4+2] = 0; + addr[4+3] = 0; + addr[4+7] = 1; + } else { + printf("format_mdvd: media is unformated.\n"); + } + + if (lverbose) { + printf("Formating %s\n", format_types[formattype & 0x07]); + flush(); + } + if (formattype == FULL_FORMAT) { + ret = scsi_format(usalp, (caddr_t)&addr, sizeof(addr), FALSE); + } else { + ret = scsi_format(usalp, (caddr_t)&addr, sizeof(addr), TRUE); + } + if (ret < 0) + return (ret); + + wait_unit_ready(usalp, 90*60/curspeed); /* XXX Wait for ATAPI */ + waitfix_mmc(usalp, 90*60/curspeed); /* XXX Wait for ATAPI */ + return (ret); +} + +static int +send_opc_mmc(SCSI *usalp, caddr_t bp, int cnt, int doopc) +{ + int ret; + + usalp->silent++; + ret = send_opc(usalp, bp, cnt, doopc); + usalp->silent--; + + if (ret >= 0) + return (ret); + + /* BEGIN CSTYLED */ + /* + * Hack for a mysterioys drive .... + * Device type : Removable CD-ROM + * Version : 0 + * Response Format: 1 + * Vendor_info : 'RWD ' + * Identifikation : 'RW2224 ' + * Revision : '2.53' + * Device seems to be: Generic mmc CD-RW. + * + * Performing OPC... + * CDB: 54 01 00 00 00 00 00 00 00 00 + * Sense Bytes: 70 00 06 00 00 00 00 0A 00 00 00 00 5A 03 00 00 + * Sense Key: 0x6 Unit Attention, Segment 0 + * Sense Code: 0x5A Qual 0x03 (operator selected write permit) Fru 0x0 + * Sense flags: Blk 0 (not valid) + */ + /* END CSTYLED */ + if (usal_sense_key(usalp) == SC_UNIT_ATTENTION && + usal_sense_code(usalp) == 0x5A && + usal_sense_qual(usalp) == 0x03) + return (0); + + /* + * Do not make the condition: + * "Power calibration area almost full" a fatal error. + * It just flags that we have a single and last chance to write now. + */ + if ((usal_sense_key(usalp) == SC_RECOVERABLE_ERROR || + usal_sense_key(usalp) == SC_MEDIUM_ERROR) && + usal_sense_code(usalp) == 0x73 && + usal_sense_qual(usalp) == 0x01) + return (0); + + /* + * Send OPC is optional. + */ + if (usal_sense_key(usalp) != SC_ILLEGAL_REQUEST) { + if (usalp->silent <= 0) + usal_printerr(usalp); + return (ret); + } + return (0); +} + +static int +opt1_mmc(SCSI *usalp, cdr_t *dp) +{ + int oflags = dp->cdr_dstat->ds_cdrflags; + + if ((dp->cdr_dstat->ds_cdrflags & RF_AUDIOMASTER) != 0) { + printf("Turning Audio Master Q. R. on\n"); + if (set_audiomaster_yamaha(usalp, dp, TRUE) < 0) + return (-1); + if (!debug && lverbose <= 1) + dp->cdr_dstat->ds_cdrflags &= ~RF_PRATIP; + if (getdisktype_mmc(usalp, dp) < 0) { + dp->cdr_dstat->ds_cdrflags = oflags; + return (-1); + } + dp->cdr_dstat->ds_cdrflags = oflags; + if (oflags & RF_PRATIP) { + msf_t msf; + lba_to_msf(dp->cdr_dstat->ds_first_leadin, &msf); + printf("New start of lead in: %ld (%02d:%02d/%02d)\n", + (long)dp->cdr_dstat->ds_first_leadin, + msf.msf_min, + msf.msf_sec, + msf.msf_frame); + lba_to_msf(dp->cdr_dstat->ds_maxblocks, &msf); + printf("New start of lead out: %ld (%02d:%02d/%02d)\n", + (long)dp->cdr_dstat->ds_maxblocks, + msf.msf_min, + msf.msf_sec, + msf.msf_frame); + } + } + if (mmc_isplextor(usalp)) { + int gcode; + + if ((dp->cdr_flags & (CDR_SINGLESESS|CDR_HIDE_CDR)) != 0) { + if (ss_hide_plextor(usalp, + (dp->cdr_dstat->ds_cdrflags & RF_SINGLESESS) != 0, + (dp->cdr_dstat->ds_cdrflags & RF_HIDE_CDR) != 0) < 0) + return (-1); + } + + if ((dp->cdr_flags & CDR_SPEEDREAD) != 0) { + if (speed_rd_plextor(usalp, + (dp->cdr_dstat->ds_cdrflags & RF_SPEEDREAD) != 0) < 0) + return (-1); + } + + if ((dp->cdr_cmdflags & F_SETDROPTS) || + (wm_base(dp->cdr_dstat->ds_wrmode) == WM_SAO) || + (wm_base(dp->cdr_dstat->ds_wrmode) == WM_RAW)) + gcode = do_gigarec_plextor(usalp); + else + gcode = gigarec_plextor(usalp, 0); + if (gcode != 0) { + msf_t msf; + + dp->cdr_dstat->ds_first_leadin = + gigarec_mult(gcode, dp->cdr_dstat->ds_first_leadin); + dp->cdr_dstat->ds_maxblocks = + gigarec_mult(gcode, dp->cdr_dstat->ds_maxblocks); + + if (oflags & RF_PRATIP) { + lba_to_msf(dp->cdr_dstat->ds_first_leadin, &msf); + printf("New start of lead in: %ld (%02d:%02d/%02d)\n", + (long)dp->cdr_dstat->ds_first_leadin, + msf.msf_min, + msf.msf_sec, + msf.msf_frame); + lba_to_msf(dp->cdr_dstat->ds_maxblocks, &msf); + printf("New start of lead out: %ld (%02d:%02d/%02d)\n", + (long)dp->cdr_dstat->ds_maxblocks, + msf.msf_min, + msf.msf_sec, + msf.msf_frame); + } + } + } + return (0); +} + +static int +opt2_mmc(SCSI *usalp, cdr_t *dp) +{ + 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(usalp, 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); + + + rp = get_justlink_ricoh(usalp, 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>2 && (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) { + if(lverbose>2) + printf("Turning BURN-Free on\n"); + burnfree = TRUE; + } + if (burnfree && (dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0) { + if(lverbose>2) + 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) { + /* + * Clear Just-Link counter + */ + i_to_2_byte(rp->link_counter, 0); + if (xdebug) + usal_prbytes("Mode Select Data ", moder, moder[0]+1); + + if (!set_mode_params(usalp, "Ricoh Vendor Page", moder, moder[0]+1, 0, -1)) + return (-1); + rp = get_justlink_ricoh(usalp, moder); + } + +#ifdef DEBUG + usal_prbytes("CD write parameter:", (Uchar *)mode, len); +#endif + if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) + return (-1); + + if (mmc_isplextor(usalp)) { + /* + * Clear Burn-Proof counter + */ + usalp->silent++; + bpc_plextor(usalp, 1, NULL); + usalp->silent--; + + do_varirec_plextor(usalp); + } + + return (0); +} + +static int +opt1_mdvd(SCSI *usalp, cdr_t *dp) +{ + int oflags = dp->cdr_dstat->ds_cdrflags; + + if ((dp->cdr_dstat->ds_cdrflags & RF_AUDIOMASTER) != 0) { + printf("Turning Audio Master Q. R. on\n"); + if (set_audiomaster_yamaha(usalp, dp, TRUE) < 0) + return (-1); + if (!debug && lverbose <= 1) + dp->cdr_dstat->ds_cdrflags &= ~RF_PRATIP; + if (getdisktype_mdvd(usalp, dp) < 0) { + dp->cdr_dstat->ds_cdrflags = oflags; + return (-1); + } + dp->cdr_dstat->ds_cdrflags = oflags; + if (oflags & RF_PRATIP) { + msf_t msf; + lba_to_msf(dp->cdr_dstat->ds_first_leadin, &msf); + printf("New start of lead in: %ld (%02d:%02d/%02d)\n", + (long)dp->cdr_dstat->ds_first_leadin, + msf.msf_min, + msf.msf_sec, + msf.msf_frame); + lba_to_msf(dp->cdr_dstat->ds_maxblocks, &msf); + printf("New start of lead out: %ld (%02d:%02d/%02d)\n", + (long)dp->cdr_dstat->ds_maxblocks, + msf.msf_min, + msf.msf_sec, + msf.msf_frame); + } + } + return (0); +} + +static int +scsi_sony_write(SCSI *usalp, + 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 */) +{ + return (write_xg5(usalp, bp, sectaddr, size, blocks)); +} + +Uchar db2df[] = { + 0x00, /* 0 2352 bytes of raw data */ + 0xFF, /* 1 2368 bytes (raw data + P/Q Subchannel) */ + 0xFF, /* 2 2448 bytes (raw data + P-W Subchannel) */ + 0xFF, /* 3 2448 bytes (raw data + P-W raw Subchannel)*/ + 0xFF, /* 4 - Reserved */ + 0xFF, /* 5 - Reserved */ + 0xFF, /* 6 - Reserved */ + 0xFF, /* 7 - Vendor specific */ + 0x10, /* 8 2048 bytes Mode 1 (ISO/IEC 10149) */ + 0x30, /* 9 2336 bytes Mode 2 (ISO/IEC 10149) */ + 0xFF, /* 10 2048 bytes Mode 2! (CD-ROM XA form 1) */ + 0xFF, /* 11 2056 bytes Mode 2 (CD-ROM XA form 1) */ + 0xFF, /* 12 2324 bytes Mode 2 (CD-ROM XA form 2) */ + 0xFF, /* 13 2332 bytes Mode 2 (CD-ROM XA 1/2+subhdr) */ + 0xFF, /* 14 - Reserved */ + 0xFF, /* 15 - Vendor specific */ +}; + +static int +gen_cue_mmc(track_t *trackp, void *vcuep, BOOL needgap) +{ + int tracks = trackp->tracks; + int i; + struct mmc_cue **cuep = vcuep; + struct mmc_cue *cue; + struct mmc_cue *cp; + int ncue = 0; + int icue = 0; + int pgsize; + msf_t m; + int ctl; + int df; + int scms; + + cue = malloc(1); + + for (i = 0; i <= tracks; i++) { + ctl = (st2mode[trackp[i].sectype & ST_MASK]) << 4; + if (is_copy(&trackp[i])) + ctl |= TM_ALLOW_COPY << 4; + if (is_quadro(&trackp[i])) + ctl |= TM_QUADRO << 4; + df = db2df[trackp[i].dbtype & 0x0F]; + if (trackp[i].tracktype == TOC_XA2 && + trackp[i].sectype == (SECT_MODE_2_MIX|ST_MODE_RAW)) { + /* + * Hack for CUE with MODE2/CDI and + * trackp[i].dbtype == DB_RAW + */ + df = 0x21; + } + + if (trackp[i].isrc) { /* MCN or ISRC */ + ncue += 2; + cue = realloc(cue, ncue * sizeof (*cue)); + cp = &cue[icue++]; + if (i == 0) { + cp->cs_ctladr = 0x02; + movebytes(&trackp[i].isrc[0], &cp->cs_tno, 7); + cp = &cue[icue++]; + cp->cs_ctladr = 0x02; + movebytes(&trackp[i].isrc[7], &cp->cs_tno, 7); + } else { + cp->cs_ctladr = 0x03; + cp->cs_tno = i; + movebytes(&trackp[i].isrc[0], &cp->cs_index, 6); + cp = &cue[icue++]; + cp->cs_ctladr = 0x03; + cp->cs_tno = i; + movebytes(&trackp[i].isrc[6], &cp->cs_index, 6); + } + } + if (i == 0) { /* Lead in */ + df &= ~7; /* Mask off data size & nonRAW subch */ + if (df < 0x10) + df |= 1; + else + df |= 4; + if (trackp[0].flags & TI_TEXT) /* CD-Text in Lead-in*/ + df |= 0x40; + lba_to_msf(-150, &m); + cue = realloc(cue, ++ncue * sizeof (*cue)); + cp = &cue[icue++]; + fillcue(cp, ctl|0x01, i, 0, df, 0, &m); + } else { + scms = 0; + + if (is_scms(&trackp[i])) + scms = 0x80; + pgsize = trackp[i].pregapsize; + if (pgsize == 0 && needgap) + pgsize++; + lba_to_msf(trackp[i].trackstart-pgsize, &m); + cue = realloc(cue, ++ncue * sizeof (*cue)); + cp = &cue[icue++]; + fillcue(cp, ctl|0x01, i, 0, df, scms, &m); + + if (trackp[i].nindex == 1) { + lba_to_msf(trackp[i].trackstart, &m); + cue = realloc(cue, ++ncue * sizeof (*cue)); + cp = &cue[icue++]; + fillcue(cp, ctl|0x01, i, 1, df, scms, &m); + } else { + int idx; + long *idxlist; + + ncue += trackp[i].nindex; + idxlist = trackp[i].tindex; + cue = realloc(cue, ncue * sizeof (*cue)); + + for (idx = 1; idx <= trackp[i].nindex; idx++) { + lba_to_msf(trackp[i].trackstart + idxlist[idx], &m); + cp = &cue[icue++]; + fillcue(cp, ctl|0x01, i, idx, df, scms, &m); + } + } + } + } + /* Lead out */ + ctl = (st2mode[trackp[tracks+1].sectype & ST_MASK]) << 4; + if (is_copy(&trackp[i])) + ctl |= TM_ALLOW_COPY << 4; + if (is_quadro(&trackp[i])) + ctl |= TM_QUADRO << 4; + df = db2df[trackp[tracks+1].dbtype & 0x0F]; + if (trackp[i].tracktype == TOC_XA2 && + trackp[i].sectype == (SECT_MODE_2_MIX|ST_MODE_RAW)) { + /* + * Hack for CUE with MODE2/CDI and + * trackp[i].dbtype == DB_RAW + */ + df = 0x21; + } + df &= ~7; /* Mask off data size & nonRAW subch */ + if (df < 0x10) + df |= 1; + else + df |= 4; + lba_to_msf(trackp[tracks+1].trackstart, &m); + cue = realloc(cue, ++ncue * sizeof (*cue)); + cp = &cue[icue++]; + fillcue(cp, ctl|0x01, 0xAA, 1, df, 0, &m); + + if (lverbose > 1) { + for (i = 0; i < ncue; i++) { + usal_prbytes("", (Uchar *)&cue[i], 8); + } + } + if (cuep) + *cuep = cue; + else + free(cue); + return (ncue); +} + +static void +fillcue(struct mmc_cue *cp /* The target cue entry */, + int ca /* Control/adr for this entry */, + int tno /* Track number for this entry */, + int idx /* Index for this entry */, + int dataform /* Data format for this entry */, + int scms /* Serial copy management */, + msf_t *mp /* MSF value for this entry */) +{ + cp->cs_ctladr = ca; /* XXX wie lead in */ + cp->cs_tno = tno; + cp->cs_index = idx; + cp->cs_dataform = dataform; /* XXX wie lead in */ + cp->cs_scms = scms; + cp->cs_min = mp->msf_min; + cp->cs_sec = mp->msf_sec; + cp->cs_frame = mp->msf_frame; +} + +static int +send_cue_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + struct mmc_cue *cp; + int ncue; + int ret; + Uint i; + + for (i = 1; i <= trackp->tracks; i++) { + if (trackp[i].tracksize < (tsize_t)0) { + errmsgno(EX_BAD, "Track %d has unknown length.\n", i); + return (-1); + } + } + ncue = (*dp->cdr_gen_cue)(trackp, &cp, FALSE); + + usalp->silent++; + ret = send_cue_sheet(usalp, (caddr_t)cp, ncue*8); + usalp->silent--; + free(cp); + if (ret < 0) { + errmsgno(EX_BAD, "CUE sheet not accepted. Retrying with minimum pregapsize = 1.\n"); + ncue = (*dp->cdr_gen_cue)(trackp, &cp, TRUE); + ret = send_cue_sheet(usalp, (caddr_t)cp, ncue*8); + if (ret < 0) { + errmsgno(EX_BAD, + "CUE sheet still not accepted. Please try to write in RAW (-raw96r) mode.\n"); + } + free(cp); + } + return (ret); +} + +static int +stats_mmc(SCSI *usalp, cdr_t *dp) +{ + Uchar mode[256]; + struct ricoh_mode_page_30 *rp; + UInt32_t count; + + if (mmc_isplextor(usalp) && lverbose) { + int sels; + int maxs; + int lasts; + + /* + * Run it in silent mode as old drives do not support it. + * As this function looks to be a part of the PowerRec + * features, we may want to check + * dp->cdr_flags & CDR_FORCESPEED + */ + usalp->silent++; + if (get_speeds_plextor(usalp, &sels, &maxs, &lasts) >= 0) { + printf("Last selected write speed: %dx\n", + sels / 176); + printf("Max media write speed: %dx\n", + maxs / 176); + printf("Last actual write speed: %dx\n", + lasts / 176); + } + usalp->silent--; + } + + if ((dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0) + return (0); + + if (mmc_isplextor(usalp)) { + int i = 0; + int ret; + + /* + * Read Burn-Proof counter + */ + usalp->silent++; + ret = bpc_plextor(usalp, 2, &i); + usalp->silent--; + if (ret < 0) + return (-1); + count = i; + /* + * Clear Burn-Proof counter + */ + bpc_plextor(usalp, 1, NULL); + } else { + rp = get_justlink_ricoh(usalp, mode); + if (rp) + count = a_to_u_2_byte(rp->link_counter); + else + return (-1); + } + if (lverbose) { + if (count == 0) + printf("BURN-Free was never needed.\n"); + else + printf("BURN-Free was %d times used.\n", + (int)count); + } + return (0); +} +/*--------------------------------------------------------------------------*/ +static BOOL +mmc_isplextor(SCSI *usalp) +{ + if (usalp->inq != NULL && + strncmp(usalp->inq->vendor_info, "PLEXTOR", 7) == 0) { + return (TRUE); + } + return (FALSE); +} + +static BOOL +mmc_isyamaha(SCSI *usalp) +{ + if (usalp->inq != NULL && + strncmp(usalp->inq->vendor_info, "YAMAHA", 6) == 0) { + return (TRUE); + } + return (FALSE); +} + +static void +do_varirec_plextor(SCSI *usalp) +{ + char *p; + int voff; + + p = hasdrvopt(driveropts, "varirec="); + if (p == NULL || curspeed != 4) { + if (check_varirec_plextor(usalp) >= 0) + varirec_plextor(usalp, FALSE, 0); + } else { + if (*astoi(p, &voff) != '\0') + comerrno(EX_BAD, + "Bad varirec value '%s'.\n", p); + if (check_varirec_plextor(usalp) < 0) + comerrno(EX_BAD, "Drive does not support VariRec.\n"); + varirec_plextor(usalp, TRUE, voff); + } +} + +/* + * GigaRec value table + */ +struct gr { + Uchar val; + char vadd; + char *name; +} gr[] = { + { 0x00, 0, "off", }, + { 0x00, 0, "1.0", }, + { 0x01, 2, "1.2", }, + { 0x02, 3, "1.3", }, + { 0x03, 4, "1.4", }, + { 0x81, -2, "0.8", }, + { 0x82, -3, "0.7", }, + { 0x83, -4, "0.6", }, + { 0x00, 0, NULL, }, +}; + +static int +do_gigarec_plextor(SCSI *usalp) +{ + char *p; + int val = 0; /* Make silly GCC happy */ + + p = hasdrvopt(driveropts, "gigarec="); + if (p == NULL) { + if (check_gigarec_plextor(usalp) >= 0) + gigarec_plextor(usalp, 0); + } else { + struct gr *gp = gr; + + for (; gp->name != NULL; gp++) { + if (streql(p, gp->name)) { + val = gp->val; + break; + } + } + if (gp->name == NULL) + comerrno(EX_BAD, + "Bad gigarec value '%s'.\n", p); + if (check_gigarec_plextor(usalp) < 0) + comerrno(EX_BAD, "Drive does not support GigaRec.\n"); + return (gigarec_plextor(usalp, val)); + } + return (0); +} + +static int +drivemode_plextor(SCSI *usalp, caddr_t bp, int cnt, int modecode, void *modeval) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + if (modeval == NULL) { + scmd->flags |= SCG_RECV_DATA; + scmd->addr = bp; + scmd->size = cnt; + } else { + scmd->cdb.g5_cdb.res = 0x08; + } + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xE9; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + scmd->cdb.g1_cdb.addr[0] = modecode; + if (modeval) + movebytes(modeval, &scmd->cdb.g1_cdb.addr[1], 6); + else + i_to_2_byte(&scmd->cdb.g1_cdb.count[2], cnt); + + usalp->cmdname = "plextor drive mode"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +/* + * #defines for drivemode_plextor()... + */ +#define MODE_CODE_SH 0x01 /* Mode code for Single Session & Hide-CDR */ +#define MB1_SS 0x01 /* Single Session Mode */ +#define MB1_HIDE_CDR 0x02 /* Hide CDR Media */ + +#define MODE_CODE_VREC 0x02 /* Mode code for Vari Rec */ + +#define MODE_CODE_GREC 0x04 /* Mode code for Giga Rec */ + +#define MODE_CODE_SPEED 0xbb /* Mode code for Speed Read */ +#define MBbb_SPEAD_READ 0x01 /* Spead Read */ + /* Danach Speed auf 0xFFFF 0xFFFF setzen */ + +static int +drivemode2_plextor(SCSI *usalp, caddr_t bp, int cnt, int modecode, void *modeval) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + if (modeval == NULL) { + scmd->flags |= SCG_RECV_DATA; + scmd->addr = bp; + scmd->size = cnt; + } else { + scmd->cdb.g5_cdb.res = 0x08; + } + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xED; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + scmd->cdb.g1_cdb.addr[0] = modecode; + if (modeval) + scmd->cdb.g5_cdb.reladr = *(char *)modeval != 0 ? 1 : 0; + else + i_to_2_byte(&scmd->cdb.g1_cdb.count[1], cnt); + + usalp->cmdname = "plextor drive mode2"; + + if (usal_cmd(usalp) < 0) + return (-1); + + return (0); +} + +static int +check_varirec_plextor(SCSI *usalp) +{ + int modecode = 2; + Uchar getmode[8]; + + fillbytes(getmode, sizeof (getmode), '\0'); + usalp->silent++; + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + + return (0); +} + +static int +check_gigarec_plextor(SCSI *usalp) +{ + int modecode = 4; + Uchar getmode[8]; + + fillbytes(getmode, sizeof (getmode), '\0'); + usalp->silent++; + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + + return (0); +} + +static int +varirec_plextor(SCSI *usalp, BOOL on, int val) +{ + int modecode = 2; + Uchar setmode[8]; + Uchar getmode[8]; + + fillbytes(getmode, sizeof (getmode), '\0'); + usalp->silent++; + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + + if (lverbose > 1) + usal_prbytes("Modes", getmode, sizeof (getmode)); + + + fillbytes(setmode, sizeof (setmode), '\0'); + setmode[0] = on?1:0; + if (on) { + if (val < -2 || val > 2) + comerrno(EX_BAD, "Bad VariRec offset %d\n", val); + printf("Turning Varirec on.\n"); + printf("Varirec offset is %d.\n", val); + + if (val > 0) { + setmode[1] = val & 0x7F; + } else { + setmode[1] = (-val) & 0x7F; + setmode[1] |= 0x80; + } + } + + if (drivemode_plextor(usalp, NULL, 0, modecode, setmode) < 0) + return (-1); + + fillbytes(getmode, sizeof (getmode), '\0'); + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) + return (-1); + + if (lverbose > 1) + usal_prbytes("Modes", getmode, sizeof (getmode)); + + return (0); +} + +static int +gigarec_plextor(SCSI *usalp, int val) +{ + int modecode = 4; + Uchar setmode[8]; + Uchar getmode[8]; + + fillbytes(getmode, sizeof (getmode), '\0'); + usalp->silent++; + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + + if (lverbose > 1) + usal_prbytes("Modes", getmode, sizeof (getmode)); + + + fillbytes(setmode, sizeof (setmode), '\0'); + setmode[1] = val; + + if (drivemode_plextor(usalp, NULL, 0, modecode, setmode) < 0) + return (-1); + + fillbytes(getmode, sizeof (getmode), '\0'); + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) + return (-1); + + if (lverbose > 1) + usal_prbytes("Modes", getmode, sizeof (getmode)); + + { + struct gr *gp = gr; + + for (; gp->name != NULL; gp++) { + if (getmode[3] == gp->val) + break; + } + if (gp->name == NULL) + printf("Unknown GigaRec value 0x%X.\n", getmode[3]); + else + printf("GigaRec %sis %s.\n", gp->val?"value ":"", gp->name); + } + return (getmode[3]); +} + +static Int32_t +gigarec_mult(int code, Int32_t val) +{ + Int32_t add; + struct gr *gp = gr; + + for (; gp->name != NULL; gp++) { + if (code == gp->val) + break; + } + if (gp->vadd == 0) + return (val); + + add = val * gp->vadd / 10; + return (val + add); +} + +static int +check_ss_hide_plextor(SCSI *usalp) +{ + int modecode = 1; + Uchar getmode[8]; + + fillbytes(getmode, sizeof (getmode), '\0'); + usalp->silent++; + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + + return (getmode[2] & 0x03); +} + +static int +check_speed_rd_plextor(SCSI *usalp) +{ + int modecode = 0xBB; + Uchar getmode[8]; + + fillbytes(getmode, sizeof (getmode), '\0'); + usalp->silent++; + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + + return (getmode[2] & 0x01); +} + +static int +check_powerrec_plextor(SCSI *usalp) +{ + int modecode = 0; + Uchar getmode[8]; + + fillbytes(getmode, sizeof (getmode), '\0'); + usalp->silent++; + if (drivemode2_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + + if (getmode[2] & 1) + return (1); + + return (0); +} + +static int +ss_hide_plextor(SCSI *usalp, BOOL do_ss, BOOL do_hide) +{ + int modecode = 1; + Uchar setmode[8]; + Uchar getmode[8]; + BOOL is_ss; + BOOL is_hide; + + fillbytes(getmode, sizeof (getmode), '\0'); + usalp->silent++; + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + + if (lverbose > 1) + usal_prbytes("Modes", getmode, sizeof (getmode)); + + + is_ss = (getmode[2] & MB1_SS) != 0; + is_hide = (getmode[2] & MB1_HIDE_CDR) != 0; + + if (lverbose > 0) { + printf("Single session is %s.\n", is_ss ? "ON":"OFF"); + printf("Hide CDR is %s.\n", is_hide ? "ON":"OFF"); + } + + fillbytes(setmode, sizeof (setmode), '\0'); + setmode[0] = getmode[2]; /* Copy over old values */ + if (do_ss >= 0) { + if (do_ss) + setmode[0] |= MB1_SS; + else + setmode[0] &= ~MB1_SS; + } + if (do_hide >= 0) { + if (do_hide) + setmode[0] |= MB1_HIDE_CDR; + else + setmode[0] &= ~MB1_HIDE_CDR; + } + + if (do_ss >= 0 && do_ss != is_ss) + printf("Turning single session %s.\n", do_ss?"on":"off"); + if (do_hide >= 0 && do_hide != is_hide) + printf("Turning hide CDR %s.\n", do_hide?"on":"off"); + + if (drivemode_plextor(usalp, NULL, 0, modecode, setmode) < 0) + return (-1); + + fillbytes(getmode, sizeof (getmode), '\0'); + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) + return (-1); + + if (lverbose > 1) + usal_prbytes("Modes", getmode, sizeof (getmode)); + + return (0); +} + +static int +speed_rd_plextor(SCSI *usalp, BOOL do_speedrd) +{ + int modecode = 0xBB; + Uchar setmode[8]; + Uchar getmode[8]; + BOOL is_speedrd; + + fillbytes(getmode, sizeof (getmode), '\0'); + usalp->silent++; + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + + if (lverbose > 1) + usal_prbytes("Modes", getmode, sizeof (getmode)); + + + is_speedrd = (getmode[2] & MBbb_SPEAD_READ) != 0; + + if (lverbose > 0) + printf("Speed-Read is %s.\n", is_speedrd ? "ON":"OFF"); + + fillbytes(setmode, sizeof (setmode), '\0'); + setmode[0] = getmode[2]; /* Copy over old values */ + if (do_speedrd >= 0) { + if (do_speedrd) + setmode[0] |= MBbb_SPEAD_READ; + else + setmode[0] &= ~MBbb_SPEAD_READ; + } + + if (do_speedrd >= 0 && do_speedrd != is_speedrd) + printf("Turning Speed-Read %s.\n", do_speedrd?"on":"off"); + + if (drivemode_plextor(usalp, NULL, 0, modecode, setmode) < 0) + return (-1); + + fillbytes(getmode, sizeof (getmode), '\0'); + if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) + return (-1); + + if (lverbose > 1) + usal_prbytes("Modes", getmode, sizeof (getmode)); + + /* + * Set current read speed to new max value. + */ + if (do_speedrd >= 0 && do_speedrd != is_speedrd) + scsi_set_speed(usalp, 0xFFFF, -1, ROTCTL_CAV); + + return (0); +} + +static int +powerrec_plextor(SCSI *usalp, BOOL do_powerrec) +{ + int modecode = 0; + Uchar setmode[8]; + Uchar getmode[8]; + BOOL is_powerrec; + int speed; + + fillbytes(getmode, sizeof (getmode), '\0'); + usalp->silent++; + if (drivemode2_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) { + usalp->silent--; + return (-1); + } + usalp->silent--; + + if (lverbose > 1) + usal_prbytes("Modes", getmode, sizeof (getmode)); + + + is_powerrec = (getmode[2] & 1) != 0; + + speed = a_to_u_2_byte(&getmode[4]); + + if (lverbose > 0) { + printf("Power-Rec is %s.\n", is_powerrec ? "ON":"OFF"); + printf("Power-Rec write speed: %dx (recommended)\n", speed / 176); + } + + fillbytes(setmode, sizeof (setmode), '\0'); + setmode[0] = getmode[2]; /* Copy over old values */ + if (do_powerrec >= 0) { + if (do_powerrec) + setmode[0] |= 1; + else + setmode[0] &= ~1; + } + + if (do_powerrec >= 0 && do_powerrec != is_powerrec) + printf("Turning Power-Rec %s.\n", do_powerrec?"on":"off"); + + if (drivemode2_plextor(usalp, NULL, 0, modecode, setmode) < 0) + return (-1); + + fillbytes(getmode, sizeof (getmode), '\0'); + if (drivemode2_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) + return (-1); + + if (lverbose > 1) + usal_prbytes("Modes", getmode, sizeof (getmode)); + + return (0); +} + +static int +get_speeds_plextor(SCSI *usalp, int *selp, int *maxp, int *lastp) +{ + register struct usal_cmd *scmd = usalp->scmd; + char buf[10]; + int i; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + fillbytes((caddr_t)buf, sizeof (buf), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->flags |= SCG_RECV_DATA; + scmd->addr = buf; + scmd->size = sizeof (buf); + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xEB; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + + i_to_2_byte(&scmd->cdb.g1_cdb.count[1], sizeof (buf)); + + usalp->cmdname = "plextor get speedlist"; + + if (usal_cmd(usalp) < 0) + return (-1); + + i = a_to_u_2_byte(&buf[4]); + if (selp) + *selp = i; + + i = a_to_u_2_byte(&buf[6]); + if (maxp) + *maxp = i; + + i = a_to_u_2_byte(&buf[8]); + if (lastp) + *lastp = i; + + return (0); +} + +static int +bpc_plextor(SCSI *usalp, int mode, int *bpp) +{ + register struct usal_cmd *scmd = usalp->scmd; + char buf[4]; + int i; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + fillbytes((caddr_t)buf, sizeof (buf), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->flags |= SCG_RECV_DATA; + scmd->addr = buf; + scmd->size = sizeof (buf); + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xF5; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + + scmd->cdb.g5_cdb.addr[1] = 0x08; + scmd->cdb.g5_cdb.addr[2] = mode; + + i_to_2_byte(&scmd->cdb.g1_cdb.count[1], sizeof (buf)); + + usalp->cmdname = "plextor read bpc"; + + if (usal_cmd(usalp) < 0) + return (-1); + + if (usal_getresid(usalp) > 2) + return (0); + + i = a_to_u_2_byte(buf); + if (bpp) + *bpp = i; + + return (0); +} + +static int +set_audiomaster_yamaha(SCSI *usalp, cdr_t *dp, BOOL keep_mode) +{ + Uchar mode[0x100]; + int len; + int ret = 0; + struct cd_mode_page_05 *mp; + + if (xdebug && !keep_mode) + printf("Checking for Yamaha Audio Master feature: "); + + /* + * Do not reset mp->test_write (-dummy) here. + */ + deflt_writemodes_mmc(usalp, FALSE); + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + usalp->silent++; + if (!get_mode_params(usalp, 0x05, "CD write parameter", + mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) { + usalp->silent--; + return (-1); + } + if (len == 0) { + usalp->silent--; + return (-1); + } + + mp = (struct cd_mode_page_05 *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); +#ifdef DEBUG + usal_prbytes("CD write parameter:", (Uchar *)mode, len); +#endif + + /* + * Do not set mp->test_write (-dummy) here. It should be set + * only at one place and only one time. + */ + mp->BUFE = 0; + + mp->write_type = 8; + mp->track_mode = 0; + mp->dbtype = DB_RAW; + + if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) + ret = -1; + + /* + * Do not reset mp->test_write (-dummy) here. + */ + if (!keep_mode || ret < 0) + deflt_writemodes_mmc(usalp, FALSE); + usalp->silent--; + + return (ret); +} + +struct +ricoh_mode_page_30 *get_justlink_ricoh(SCSI *usalp, Uchar *mode) +{ + Uchar modec[0x100]; + int len; + struct ricoh_mode_page_30 *mp; + + usalp->silent++; + if (!get_mode_params(usalp, 0x30, "Ricoh Vendor Page", mode, modec, NULL, NULL, &len)) { + usalp->silent--; + return ((struct ricoh_mode_page_30 *)0); + } + usalp->silent--; + + /* + * SCSI mode header + 6 bytes mode page 30. + * This is including the Burn-Free counter. + */ + if (len < 10) + return ((struct ricoh_mode_page_30 *)0); + + if (xdebug) { + fprintf(stderr, "Mode len: %d\n", len); + usal_prbytes("Mode Sense Data ", mode, len); + usal_prbytes("Mode Sence CData", modec, len); + } + + mp = (struct ricoh_mode_page_30 *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); + + /* + * 6 bytes mode page 30. + * This is including the Burn-Free counter. + */ + if ((len - ((Uchar *)mp - mode) -1) < 5) + return ((struct ricoh_mode_page_30 *)0); + + if (xdebug) { + fprintf(stderr, "Burnfree counter: %d\n", a_to_u_2_byte(mp->link_counter)); + } + return (mp); +} + +static int +force_speed_yamaha(SCSI *usalp, int readspeed, int writespeed) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xBB; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + + if (readspeed < 0) + i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], 0xFFFF); + else + i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], readspeed); + if (writespeed < 0) + i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], 0xFFFF); + else + i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], writespeed); + + scmd->cdb.cmd_cdb[11] = 0x80; + + usalp->cmdname = "yamaha force cd speed"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +static BOOL +get_tattoo_yamaha(SCSI *usalp, BOOL print, Int32_t *irp, Int32_t *orp) +{ + Uchar mode[0x100]; + int len; + UInt32_t ival; + UInt32_t oval; + Uchar *mp; + + usalp->silent++; + if (!get_mode_params(usalp, 0x31, "Yamaha Tattoo Page", mode, NULL, NULL, NULL, &len)) { + usalp->silent--; + return (FALSE); + } + usalp->silent--; + + /* + * SCSI mode header + 16 bytes mode page 31. + * This is including the Burn-Free counter. + */ + if (len < 20) + return (FALSE); + + mp = (Uchar *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); + + /* + * 10 bytes mode page 31. + * This is including the Burn-Free counter. + */ + if ((len - ((Uchar *)mp - mode) -1) < 10) + return (FALSE); + + ival = a_to_u_3_byte(&mp[4]); + oval = a_to_u_3_byte(&mp[7]); + + if (irp) + *irp = ival; + if (orp) + *orp = oval; + + if (print && ival > 0 && oval > 0) { + printf("DiskT@2 inner r: %d\n", (int)ival); + printf("DiskT@2 outer r: %d\n", (int)oval); + printf("DiskT@2 image size: 3744 x %d pixel.\n", + (int)(oval-ival)+1); + } + + return (TRUE); +} + +static int +do_tattoo_yamaha(SCSI *usalp, FILE *f) +{ + Int32_t ival = 0; + Int32_t oval = 0; + Int32_t lines; + off_t fsize; + char *buf = usalp->bufptr; + long bufsize = usalp->maxbuf; + long nsecs; + long amt; + + nsecs = bufsize / 2048; + bufsize = nsecs * 2048; + + if (!get_tattoo_yamaha(usalp, FALSE, &ival, &oval)) { + errmsgno(EX_BAD, "Cannot get DiskT@2 info.\n"); + return (-1); + } + + if (ival == 0 || oval == 0) { + errmsgno(EX_BAD, "DiskT@2 info not valid.\n"); + return (-1); + } + + lines = oval - ival + 1; + fsize = filesize(f); + if ((fsize % 3744) != 0 || fsize < (lines*3744)) { + errmsgno(EX_BAD, "Illegal DiskT@2 file size.\n"); + return (-1); + } + if (fsize > (lines*3744)) + fsize = lines*3744; + + if (lverbose) + printf("Starting to write DiskT@2 data.\n"); + fillbytes(buf, bufsize, '\0'); + if ((amt = fileread(f, buf, bufsize)) <= 0) { + errmsg("DiskT@2 file read error.\n"); + return (-1); + } + + if (yamaha_write_buffer(usalp, 1, 0, ival, amt/2048, buf, amt) < 0) { + errmsgno(EX_BAD, "DiskT@2 1st write error.\n"); + return (-1); + } + amt = (amt+2047) / 2048 * 2048; + fsize -= amt; + + while (fsize > 0) { + fillbytes(buf, bufsize, '\0'); + if ((amt = fileread(f, buf, bufsize)) <= 0) { + errmsg("DiskT@2 file read error.\n"); + return (-1); + } + amt = (amt+2047) / 2048 * 2048; + fsize -= amt; + if (yamaha_write_buffer(usalp, 1, 0, 0, amt/2048, buf, amt) < 0) { + errmsgno(EX_BAD, "DiskT@2 write error.\n"); + return (-1); + } + } + + if (yamaha_write_buffer(usalp, 1, 0, oval, 0, buf, 0) < 0) { + errmsgno(EX_BAD, "DiskT@2 final error.\n"); + return (-1); + } + + wait_unit_ready(usalp, 1000); /* Wait for DiskT@2 */ + waitfix_mmc(usalp, 1000); /* Wait for DiskT@2 */ + + return (0); +} + +/* + * Yamaha specific version of 'write buffer' that offers an additional + * Parameter Length 'parlen' parameter. + */ +static int +yamaha_write_buffer(SCSI *usalp, int mode, int bufferid, long offset, + long parlen, void *buffer, long buflen) +{ + register struct usal_cmd *scmd = usalp->scmd; + Uchar *CDB; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = buffer; + scmd->size = buflen; + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = 0x3B; + + CDB = (Uchar *)scmd->cdb.cmd_cdb; + CDB[1] = mode & 7; + CDB[2] = bufferid; + i_to_3_byte(&CDB[3], offset); + i_to_3_byte(&CDB[6], parlen); + + usalp->cmdname = "write_buffer"; + + if (usal_cmd(usalp) >= 0) + return (1); + return (0); +} + +static int +dvd_dual_layer_split(SCSI *usalp, cdr_t *dp, long tsize) +{ + unsigned char xb[12]; + long l0_size; + + /* Get the Layer 0 defined data zone*/ + if (read_dvd_structure(usalp, (caddr_t)xb, 12, 0, 0, 0x20) >= 0) { + if ((xb[1] | xb[0] << 8) < 13) { + fprintf(stderr, "dvd_dual_layer_split: read_dvd_structure returns invalid data\n"); + return 1; + } + if (xb[4] & 0x80) { + printf("L0 zone size already set\n"); + return 1; + } + l0_size = xb[11] | xb[10] << 8 | xb[9] << 16 | xb[8] << 24; + if (tsize < l0_size) { + fprintf(stderr, "track size smaller than one layer, use --force to force burning."); + return 0; + } + printf("L0 size: %ld (track size %ld)\n", l0_size, tsize); + l0_size = tsize / 2; + l0_size = l0_size - 1 + 16 - (l0_size - 1) % 16; + printf("New L0 size: %ld\n", l0_size); + + memset (xb, 0, sizeof(xb)); + xb[1] = sizeof(xb) - 2; + xb[8] = l0_size >> 24; + xb[9] = l0_size >> 16; + xb[10] = l0_size >> 8; + xb[11] = l0_size; + if (send_dvd_structure(usalp, (caddr_t)xb, 12, 0, 0x20)) { + fprintf(stderr, "dvd_dual_layer_split: send_dvd_structure failed, could not set middle zone location.\n"); + return 0; + } + } + return 1; +} diff --git a/wodim/scsi_cdr.c b/wodim/scsi_cdr.c index 2d5e55c..29b21a4 100644 --- a/wodim/scsi_cdr.c +++ b/wodim/scsi_cdr.c @@ -1328,7 +1328,7 @@ scsi_format(SCSI *usalp, caddr_t addr, int size, BOOL background) } sleep(10); printf("\rFormating in progress: 100.00 %% done. \n"); - if (pid) exit (0); + if (pid > 0) exit (0); return ret; } diff --git a/wodim/scsi_cdr.c.format b/wodim/scsi_cdr.c.format new file mode 100644 index 0000000..2d5e55c --- /dev/null +++ b/wodim/scsi_cdr.c.format @@ -0,0 +1,2924 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)scsi_cdr.c 1.137 04/05/25 Copyright 1995-2004 J. Schilling */ +/* + * SCSI command functions for cdrecord + * covering pre-MMC standard functions up to MMC-2 + * + * Copyright (c) 1995-2004 J. Schilling + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * NOTICE: The Philips CDD 521 has several firmware bugs. + * One of them is not to respond to a SCSI selection + * within 200ms if the general load on the + * SCSI bus is high. To deal with this problem + * most of the SCSI commands are send with the + * SCG_CMD_RETRY flag enabled. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "scsimmc.h" +#include "wodim.h" +#include "scsi_scan.h" + +#define strbeg(s1, s2) (strstr((s2), (s1)) == (s2)) + +BOOL unit_ready(SCSI *usalp); +BOOL wait_unit_ready(SCSI *usalp, int secs); +BOOL scsi_in_progress(SCSI *usalp); +BOOL cdr_underrun(SCSI *usalp); +int test_unit_ready(SCSI *usalp); +int rezero_unit(SCSI *usalp); +int request_sense(SCSI *usalp); +int request_sense_b(SCSI *usalp, caddr_t bp, int cnt); +int inquiry(SCSI *usalp, caddr_t, int); +int read_capacity(SCSI *usalp); +void print_capacity(SCSI *usalp, FILE *f); +int scsi_load_unload(SCSI *usalp, int); +int scsi_prevent_removal(SCSI *usalp, int); +int scsi_start_stop_unit(SCSI *usalp, int, int, BOOL immed); +int scsi_set_speed(SCSI *usalp, int readspeed, int writespeed, int rotctl); +int scsi_get_speed(SCSI *usalp, int *readspeedp, int *writespeedp); +int qic02(SCSI *usalp, int); +int write_xscsi(SCSI *usalp, caddr_t, long, long, int); +int write_xg0(SCSI *usalp, caddr_t, long, long, int); +int write_xg1(SCSI *usalp, caddr_t, long, long, int); +int write_xg5(SCSI *usalp, caddr_t, long, long, int); +int seek_scsi(SCSI *usalp, long addr); +int seek_g0(SCSI *usalp, long addr); +int seek_g1(SCSI *usalp, long addr); +int scsi_flush_cache(SCSI *usalp, BOOL immed); +int read_buffer(SCSI *usalp, caddr_t bp, int cnt, int mode); +int write_buffer(SCSI *usalp, char *buffer, long length, int mode, + int bufferid, long offset); +int read_subchannel(SCSI *usalp, caddr_t bp, int track, int cnt, int msf, + int subq, int fmt); +int read_toc(SCSI *usalp, caddr_t, int, int, int, int); +int read_toc_philips(SCSI *usalp, caddr_t, int, int, int, int); +int read_header(SCSI *usalp, caddr_t, long, int, int); +int read_disk_info(SCSI *usalp, caddr_t, int); +int read_track_info(SCSI *usalp, caddr_t, int type, int addr, int cnt); +int read_rzone_info(SCSI *usalp, caddr_t bp, int cnt); +int reserve_tr_rzone(SCSI *usalp, long size); +int read_dvd_structure(SCSI *usalp, caddr_t bp, int cnt, int addr, int layer, + int fmt); +int send_dvd_structure(SCSI *usalp, caddr_t bp, int cnt, int layer, int fmt); +int send_opc(SCSI *usalp, caddr_t, int cnt, int doopc); +int read_track_info_philips(SCSI *usalp, caddr_t, int, int); +int scsi_close_tr_session(SCSI *usalp, int type, int track, BOOL immed); +int read_master_cue(SCSI *usalp, caddr_t bp, int sheet, int cnt); +int send_cue_sheet(SCSI *usalp, caddr_t bp, long size); +int read_buff_cap(SCSI *usalp, long *, long *); +int scsi_blank(SCSI *usalp, long addr, int blanktype, BOOL immed); +int scsi_format(SCSI *usalp, caddr_t addr, int size, BOOL background); +int scsi_set_streaming(SCSI *usalp, caddr_t addr, int size); +BOOL allow_atapi(SCSI *usalp, BOOL new); +int mode_select(SCSI *usalp, Uchar *, int, int, int); +int mode_sense(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf); +int mode_select_sg0(SCSI *usalp, Uchar *, int, int, int); +int mode_sense_sg0(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf); +int mode_select_g0(SCSI *usalp, Uchar *, int, int, int); +int mode_select_g1(SCSI *usalp, Uchar *, int, int, int); +int mode_sense_g0(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf); +int mode_sense_g1(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf); +int read_tochdr(SCSI *usalp, cdr_t *, int *, int *); +int read_cdtext(SCSI *usalp); +int read_trackinfo(SCSI *usalp, int, long *, struct msf *, int *, int *, + int *); +int read_B0(SCSI *usalp, BOOL isbcd, long *b0p, long *lop); +int read_session_offset(SCSI *usalp, long *); +int read_session_offset_philips(SCSI *usalp, long *); +int sense_secsize(SCSI *usalp, int current); +int select_secsize(SCSI *usalp, int); +BOOL is_cddrive(SCSI *usalp); +BOOL is_unknown_dev(SCSI *usalp); +int read_scsi(SCSI *usalp, caddr_t, long, int); +int read_g0(SCSI *usalp, caddr_t, long, int); +int read_g1(SCSI *usalp, caddr_t, long, int); +BOOL getdev(SCSI *usalp, BOOL); +void printinq(SCSI *usalp, FILE *f); +void printdev(SCSI *usalp); +BOOL do_inquiry(SCSI *usalp, BOOL); +BOOL recovery_needed(SCSI *usalp, cdr_t *); +int scsi_load(SCSI *usalp, cdr_t *); +int scsi_unload(SCSI *usalp, cdr_t *); +int scsi_cdr_write(SCSI *usalp, caddr_t bp, long sectaddr, long size, + int blocks, BOOL islast); +struct cd_mode_page_2A * mmc_cap(SCSI *usalp, Uchar *modep); +void mmc_getval(struct cd_mode_page_2A *mp, BOOL *cdrrp, BOOL *cdwrp, + BOOL *cdrrwp, BOOL *cdwrwp, BOOL *dvdp, BOOL *dvdwp); +BOOL is_mmc(SCSI *usalp, BOOL *cdwp, BOOL *dvdwp); +BOOL mmc_check(SCSI *usalp, BOOL *cdrrp, BOOL *cdwrp, BOOL *cdrrwp, + BOOL *cdwrwp, BOOL *dvdp, BOOL *dvdwp); +static void print_speed(char *fmt, int val); +void print_capabilities(SCSI *usalp); + +BOOL +unit_ready(SCSI *usalp) +{ + register struct usal_cmd *scmd = usalp->scmd; + + if (test_unit_ready(usalp) >= 0) /* alles OK */ + return (TRUE); + else if (scmd->error >= SCG_FATAL) /* nicht selektierbar */ + return (FALSE); + + if (usal_sense_key(usalp) == SC_UNIT_ATTENTION) { + if (test_unit_ready(usalp) >= 0) /* alles OK */ + return (TRUE); + } + if ((usal_cmd_status(usalp) & ST_BUSY) != 0) { + /* + * Busy/reservation_conflict + */ + usleep(500000); + if (test_unit_ready(usalp) >= 0) /* alles OK */ + return (TRUE); + } + if (usal_sense_key(usalp) == -1) { /* non extended Sense */ + if (usal_sense_code(usalp) == 4) /* NOT_READY */ + return (FALSE); + return (TRUE); + } + /* FALSE wenn NOT_READY */ + return (usal_sense_key(usalp) != SC_NOT_READY); +} + +BOOL +wait_unit_ready(SCSI *usalp, int secs) +{ + int i; + int c; + int k; + int ret; + + usalp->silent++; + ret = test_unit_ready(usalp); /* eat up unit attention */ + if (ret < 0) + ret = test_unit_ready(usalp); /* got power on condition? */ + usalp->silent--; + + if (ret >= 0) /* success that's enough */ + return (TRUE); + + usalp->silent++; + for (i = 0; i < secs && (ret = test_unit_ready(usalp)) < 0; i++) { + if (usalp->scmd->scb.busy != 0) { + sleep(1); + continue; + } + c = usal_sense_code(usalp); + k = usal_sense_key(usalp); + /* + * Abort quickly if it does not make sense to wait. + * 0x30 == Cannot read medium + * 0x3A == Medium not present + */ + if ((k == SC_NOT_READY && (c == 0x3A || c == 0x30)) || + (k == SC_MEDIUM_ERROR)) { + if (usalp->silent <= 1) + usal_printerr(usalp); + usalp->silent--; + return (FALSE); + } + sleep(1); + } + usalp->silent--; + if (ret < 0) + return (FALSE); + return (TRUE); +} + +BOOL +scsi_in_progress(SCSI *usalp) +{ + if (usal_sense_key(usalp) == SC_NOT_READY && + /* + * Logigal unit not ready operation/long_write in progress + */ + usal_sense_code(usalp) == 0x04 && + (usal_sense_qual(usalp) == 0x04 || /* CyberDr. "format in progress"*/ + usal_sense_qual(usalp) == 0x07 || /* "operation in progress" */ + usal_sense_qual(usalp) == 0x08)) { /* "long write in progress" */ + return (TRUE); + } else { + if (usalp->silent <= 1) + usal_printerr(usalp); + } + return (FALSE); +} + +BOOL +cdr_underrun(SCSI *usalp) +{ + if ((usal_sense_key(usalp) != SC_ILLEGAL_REQUEST && + usal_sense_key(usalp) != SC_MEDIUM_ERROR)) + return (FALSE); + + if ((usal_sense_code(usalp) == 0x21 && + (usal_sense_qual(usalp) == 0x00 || /* logical block address out of range */ + usal_sense_qual(usalp) == 0x02)) || /* invalid address for write */ + + (usal_sense_code(usalp) == 0x0C && + usal_sense_qual(usalp) == 0x09)) { /* write error - loss of streaming */ + return (TRUE); + } + /* + * XXX Bei manchen Brennern kommt mach dem der Brennvorgang bereits + * XXX eine Weile gelaufen ist ein 5/24/0 Invalid field in CDB. + * XXX Daher sollte man testen ob schon geschrieben wurde... + */ + return (FALSE); +} + +int +test_unit_ready(SCSI *usalp) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = (caddr_t)0; + scmd->size = 0; + scmd->flags = SCG_DISRE_ENA | (usalp->silent ? SCG_SILENT:0); + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = SC_TEST_UNIT_READY; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + + usalp->cmdname = "test unit ready"; + + return (usal_cmd(usalp)); +} + +int +rezero_unit(SCSI *usalp) +{ + register struct usal_cmd *scmd = usalp->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_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = SC_REZERO_UNIT; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + + usalp->cmdname = "rezero unit"; + + return (usal_cmd(usalp)); +} + +int +request_sense(SCSI *usalp) +{ + char sensebuf[CCS_SENSE_LEN]; + register struct usal_cmd *scmd = usalp->scmd; + + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = sensebuf; + scmd->size = sizeof (sensebuf); + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = SC_REQUEST_SENSE; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + scmd->cdb.g0_cdb.count = CCS_SENSE_LEN; + + usalp->cmdname = "request_sense"; + + if (usal_cmd(usalp) < 0) + return (-1); + usal_prsense((Uchar *)sensebuf, CCS_SENSE_LEN - usal_getresid(usalp)); + return (0); +} + +int +request_sense_b(SCSI *usalp, caddr_t bp, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = SC_REQUEST_SENSE; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + scmd->cdb.g0_cdb.count = cnt; + + usalp->cmdname = "request_sense"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +inquiry(SCSI *usalp, caddr_t bp, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes(bp, cnt, '\0'); + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = SC_INQUIRY; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + scmd->cdb.g0_cdb.count = cnt; + + usalp->cmdname = "inquiry"; + + if (usal_cmd(usalp) < 0) + return (-1); + if (usalp->verbose) + usal_prbytes("Inquiry Data :", (Uchar *)bp, cnt - usal_getresid(usalp)); + return (0); +} + +int +read_capacity(SCSI *usalp) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = (caddr_t)usalp->cap; + scmd->size = sizeof (struct scsi_capacity); + 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 = 0x25; /* Read Capacity */ + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdblen(&scmd->cdb.g1_cdb, 0); /* Full Media */ + + usalp->cmdname = "read capacity"; + + if (usal_cmd(usalp) < 0) { + return (-1); + } else { + long cbsize; + long cbaddr; + + /* + * c_bsize & c_baddr are signed Int32_t + * so we use signed int conversion here. + */ + cbsize = a_to_4_byte(&usalp->cap->c_bsize); + cbaddr = a_to_4_byte(&usalp->cap->c_baddr); + usalp->cap->c_bsize = cbsize; + usalp->cap->c_baddr = cbaddr; + } + return (0); +} + +void +print_capacity(SCSI *usalp, FILE *f) +{ + long kb; + long mb; + long prmb; + double dkb; + + dkb = (usalp->cap->c_baddr+1.0) * (usalp->cap->c_bsize/1024.0); + kb = dkb; + mb = dkb / 1024.0; + prmb = dkb / 1000.0 * 1.024; + fprintf(f, "Capacity: %ld Blocks = %ld kBytes = %ld MBytes = %ld prMB\n", + (long)usalp->cap->c_baddr+1, kb, mb, prmb); + fprintf(f, "Sectorsize: %ld Bytes\n", (long)usalp->cap->c_bsize); +} + +int +scsi_load_unload(SCSI *usalp, int load) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xA6; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + scmd->cdb.g5_cdb.addr[1] = load?3:2; + scmd->cdb.g5_cdb.count[2] = 0; /* slot # */ + + usalp->cmdname = "medium load/unload"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +scsi_prevent_removal(SCSI *usalp, int prevent) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = 0x1E; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + scmd->cdb.g0_cdb.count = prevent & 1; + + usalp->cmdname = "prevent/allow medium removal"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + + +int +scsi_start_stop_unit(SCSI *usalp, int flg, int loej, BOOL immed) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = 0x1B; /* Start Stop Unit */ + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + scmd->cdb.g0_cdb.count = (flg ? 1:0) | (loej ? 2:0); + + if (immed) + scmd->cdb.cmd_cdb[1] |= 0x01; + + usalp->cmdname = "start/stop unit"; + + return (usal_cmd(usalp)); +} + +int +scsi_set_streaming(SCSI *usalp, caddr_t perf_desc, int size) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = perf_desc; + scmd->size = size; + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xB6; + scmd->cdb.cmd_cdb[11] = 0; + scmd->cdb.cmd_cdb[10] = size; + + usalp->cmdname = "set streaming"; + + if(usalp->verbose) + fprintf(stderr, "scsi_set_streaming\n"); + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +scsi_set_speed(SCSI *usalp, int readspeed, int writespeed, int rotctl) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xBB; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + + if (readspeed < 0) + i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], 0xFFFF); + else + i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], readspeed); + if (writespeed < 0) + i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], 0xFFFF); + else + i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], writespeed); + + scmd->cdb.cmd_cdb[1] |= rotctl & 0x03; + + usalp->cmdname = "set cd speed"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +scsi_get_speed(SCSI *usalp, int *readspeedp, int *writespeedp) +{ + struct cd_mode_page_2A *mp; + Uchar m[256]; + int val; + + usalp->silent++; + mp = mmc_cap(usalp, m); /* Get MMC capabilities in allocated mp */ + usalp->silent--; + if (mp == NULL) + return (-1); /* Pre SCSI-3/mmc drive */ + + val = a_to_u_2_byte(mp->cur_read_speed); + if (readspeedp) + *readspeedp = val; + + if (mp->p_len >= 28) + val = a_to_u_2_byte(mp->v3_cur_write_speed); + else + val = a_to_u_2_byte(mp->cur_write_speed); + if (writespeedp) + *writespeedp = val; + + return (0); +} + + +int +qic02(SCSI *usalp, int cmd) +{ + register struct usal_cmd *scmd = usalp->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_G0_CDBLEN; + scmd->sense_len = DEF_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = 0x0D; /* qic02 Sysgen SC4000 */ + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + scmd->cdb.g0_cdb.mid_addr = cmd; + + usalp->cmdname = "qic 02"; + return (usal_cmd(usalp)); +} + +#define G0_MAXADDR 0x1FFFFFL + +int +write_xscsi(SCSI *usalp, caddr_t bp, long addr, long size, int cnt) +{ + if (addr <= G0_MAXADDR) + return (write_xg0(usalp, bp, addr, size, cnt)); + else + return (write_xg1(usalp, bp, addr, size, cnt)); +} + +int +write_xg0(SCSI *usalp, + caddr_t bp /* address of buffer */, + long addr /* disk address (sector) to put */, + long size /* number of bytes to transfer */, + int cnt /* sectorcount */) +{ + register struct usal_cmd *scmd = usalp->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_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = SC_WRITE; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + g0_cdbaddr(&scmd->cdb.g0_cdb, addr); + scmd->cdb.g0_cdb.count = cnt; + + usalp->cmdname = "write_g0"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (size - usal_getresid(usalp)); +} + +int +write_xg1(SCSI *usalp, + caddr_t bp /* address of buffer */, + long addr /* disk address (sector) to put */, + long size /* number of bytes to transfer */, + int cnt /* sectorcount */) +{ + register struct usal_cmd *scmd = usalp->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 = usal_lun(usalp); + g1_cdbaddr(&scmd->cdb.g1_cdb, addr); + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "write_g1"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (size - usal_getresid(usalp)); +} + +int +write_xg5(SCSI *usalp, + caddr_t bp /* address of buffer */, + long addr /* disk address (sector) to put */, + long size /* number of bytes to transfer */, + int cnt /* sectorcount */) +{ + register struct usal_cmd *scmd = usalp->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_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g5_cdb.cmd = 0xAA; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + g5_cdbaddr(&scmd->cdb.g5_cdb, addr); + g5_cdblen(&scmd->cdb.g5_cdb, cnt); + + usalp->cmdname = "write_g5"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (size - usal_getresid(usalp)); +} + +int +seek_scsi(SCSI *usalp, long addr) +{ + if (addr <= G0_MAXADDR) + return (seek_g0(usalp, addr)); + else + return (seek_g1(usalp, addr)); +} + +int +seek_g0(SCSI *usalp, long addr) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = 0x0B; /* Seek */ + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + g0_cdbaddr(&scmd->cdb.g0_cdb, addr); + + usalp->cmdname = "seek_g0"; + + return (usal_cmd(usalp)); +} + +int +seek_g1(SCSI *usalp, long addr) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = 0x2B; /* Seek G1 */ + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdbaddr(&scmd->cdb.g1_cdb, addr); + + usalp->cmdname = "seek_g1"; + + return (usal_cmd(usalp)); +} + +int +scsi_flush_cache(SCSI *usalp, BOOL immed) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->timeout = 2 * 60; /* Max: sizeof (CDR-cache)/150KB/s */ + scmd->cdb.g1_cdb.cmd = 0x35; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + + if (immed) + scmd->cdb.cmd_cdb[1] |= 0x02; + + usalp->cmdname = "flush cache"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +read_buffer(SCSI *usalp, caddr_t bp, int cnt, int mode) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + scmd->dma_read = 1; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = 0x3C; /* Read Buffer */ + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + scmd->cdb.cmd_cdb[1] |= (mode & 7); + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "read buffer"; + + return (usal_cmd(usalp)); +} + +int +write_buffer(SCSI *usalp, char *buffer, long length, int mode, int bufferid, + long offset) +{ + register struct usal_cmd *scmd = usalp->scmd; + char *cdb; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = buffer; + scmd->size = length; + scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + + cdb = (char *)scmd->cdb.cmd_cdb; + + cdb[0] = 0x3B; + cdb[1] = mode & 7; + cdb[2] = bufferid; + cdb[3] = offset >> 16; + cdb[4] = (offset >> 8) & 0xff; + cdb[5] = offset & 0xff; + cdb[6] = length >> 16; + cdb[7] = (length >> 8) & 0xff; + cdb[8] = length & 0xff; + + usalp->cmdname = "write_buffer"; + + if (usal_cmd(usalp) >= 0) + return (1); + return (0); +} + +int +read_subchannel(SCSI *usalp, caddr_t bp, int track, int cnt, int msf, int subq, + int fmt) + +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + 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 = 0x42; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + if (msf) + scmd->cdb.g1_cdb.res = 1; + if (subq) + scmd->cdb.g1_cdb.addr[0] = 0x40; + scmd->cdb.g1_cdb.addr[1] = fmt; + scmd->cdb.g1_cdb.res6 = track; + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "read subchannel"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +read_toc(SCSI *usalp, caddr_t bp, int track, int cnt, int msf, int fmt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + 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 = 0x43; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + if (msf) + scmd->cdb.g1_cdb.res = 1; + scmd->cdb.g1_cdb.addr[0] = fmt & 0x0F; + scmd->cdb.g1_cdb.res6 = track; + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "read toc"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +read_toc_philips(SCSI *usalp, caddr_t bp, int track, int cnt, int msf, int fmt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->timeout = 4 * 60; /* May last 174s on a TEAC CD-R55S */ + scmd->cdb.g1_cdb.cmd = 0x43; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + if (msf) + scmd->cdb.g1_cdb.res = 1; + scmd->cdb.g1_cdb.res6 = track; + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + if (fmt & 1) + scmd->cdb.g1_cdb.vu_96 = 1; + if (fmt & 2) + scmd->cdb.g1_cdb.vu_97 = 1; + + usalp->cmdname = "read toc"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +read_header(SCSI *usalp, caddr_t bp, long addr, int cnt, int msf) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + 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 = 0x44; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + if (msf) + scmd->cdb.g1_cdb.res = 1; + g1_cdbaddr(&scmd->cdb.g1_cdb, addr); + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "read header"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +read_disk_info(SCSI *usalp, caddr_t bp, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->timeout = 4 * 60; /* Needs up to 2 minutes */ + scmd->cdb.g1_cdb.cmd = 0x51; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "read disk info"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +read_track_info(SCSI *usalp, caddr_t bp, int type, int addr, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->timeout = 4 * 60; /* Needs up to 2 minutes */ + scmd->cdb.g1_cdb.cmd = 0x52; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); +/* scmd->cdb.cmd_cdb[1] = type & 0x03;*/ + scmd->cdb.cmd_cdb[1] = type; + g1_cdbaddr(&scmd->cdb.g1_cdb, addr); /* LBA/Track/Session */ + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "read track info"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +reserve_track(SCSI *usalp, Ulong size) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof(*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = 0x53; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + i_to_4_byte(&scmd->cdb.g1_cdb.addr[3], size); + + usalp->cmdname = "reserve track"; + + if (usal_cmd(usalp) < 0) + return (-1); + + return (0); + +} + +int +read_rzone_info(SCSI *usalp, caddr_t bp, int cnt) +{ + return (read_track_info(usalp, bp, TI_TYPE_LBA, 0, cnt)); +} + +int +reserve_tr_rzone(SCSI *usalp, long size /* number of blocks */) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = (caddr_t)0; + scmd->size = 0; + scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = 0x53; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + + i_to_4_byte(&scmd->cdb.g1_cdb.addr[3], size); + + usalp->cmdname = "reserve_track_rzone"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +read_dvd_structure(SCSI *usalp, caddr_t bp, int cnt, int addr, int layer, + int fmt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->timeout = 4 * 60; /* Needs up to 2 minutes ??? */ + scmd->cdb.g5_cdb.cmd = 0xAD; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + g5_cdbaddr(&scmd->cdb.g5_cdb, addr); + g5_cdblen(&scmd->cdb.g5_cdb, cnt); + scmd->cdb.g5_cdb.count[0] = layer; + scmd->cdb.g5_cdb.count[1] = fmt; + + usalp->cmdname = "read dvd structure"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +send_dvd_structure(SCSI *usalp, caddr_t bp, int cnt, int layer, int fmt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->timeout = 4 * 60; /* Needs up to 2 minutes ??? */ + scmd->cdb.g5_cdb.cmd = 0xBF; + scmd->cdb.g5_cdb.lun = usal_lun(usalp); + g5_cdblen(&scmd->cdb.g5_cdb, cnt); + + scmd->cdb.cmd_cdb[7] = fmt; + + usalp->cmdname = "send dvd structure"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +send_opc(SCSI *usalp, caddr_t bp, int cnt, int doopc) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->timeout = 60; + scmd->cdb.g1_cdb.cmd = 0x54; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + scmd->cdb.g1_cdb.reladr = doopc?1:0; + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "send opc"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +read_track_info_philips(SCSI *usalp, caddr_t bp, int track, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + 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 = 0xE5; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdbaddr(&scmd->cdb.g1_cdb, track); + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "read track info"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +scsi_close_tr_session(SCSI *usalp, int type, int track, BOOL immed) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->timeout = 8 * 60; /* Needs up to 4 minutes */ + scmd->cdb.g1_cdb.cmd = 0x5B; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + scmd->cdb.g1_cdb.addr[0] = type; + scmd->cdb.g1_cdb.addr[3] = track; + + if (immed) + scmd->cdb.g1_cdb.reladr = 1; +/* scmd->cdb.cmd_cdb[1] |= 0x01;*/ +#ifdef nono + scmd->cdb.g1_cdb.reladr = 1; /* IMM hack to test Mitsumi behaviour*/ +#endif + + usalp->cmdname = "close track/session"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +read_master_cue(SCSI *usalp, caddr_t bp, int sheet, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt; + 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 = 0x59; /* Read master cue */ + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + scmd->cdb.g1_cdb.addr[2] = sheet; + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "read master cue"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +int +send_cue_sheet(SCSI *usalp, caddr_t bp, long size) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = size; + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = 0x5D; /* Send CUE sheet */ + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdblen(&scmd->cdb.g1_cdb, size); + + usalp->cmdname = "send_cue_sheet"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (size - scmd->resid); +} + +int +read_buff_cap(SCSI *usalp, long *sp, long *fp) +{ + char resp[12]; + Ulong freespace; + Ulong bufsize; + int per; + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = (caddr_t)resp; + scmd->size = sizeof (resp); + 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 = 0x5C; /* Read buffer cap */ + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdblen(&scmd->cdb.g1_cdb, sizeof (resp)); + + usalp->cmdname = "read buffer cap"; + + if (usal_cmd(usalp) < 0) + return (-1); + + bufsize = a_to_u_4_byte(&resp[4]); + freespace = a_to_u_4_byte(&resp[8]); + if (sp) + *sp = bufsize; + if (fp) + *fp = freespace; + + if (usalp->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); +} + +int +scsi_blank(SCSI *usalp, long addr, int blanktype, BOOL immed) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->timeout = 160 * 60; /* full blank at 1x could take 80 minutes */ + scmd->cdb.g5_cdb.cmd = 0xA1; /* Blank */ + scmd->cdb.g0_cdb.high_addr = blanktype; + g1_cdbaddr(&scmd->cdb.g5_cdb, addr); + + if (immed) + scmd->cdb.g5_cdb.res |= 8; +/* scmd->cdb.cmd_cdb[1] |= 0x10;*/ + + usalp->cmdname = "blank unit"; + + return (usal_cmd(usalp)); +} + +int +scsi_format(SCSI *usalp, caddr_t addr, int size, BOOL background) +{ + register struct usal_cmd *scmd = usalp->scmd; + int progress=0, ret=-1, pid=-1; + unsigned char sense_table[18]; + int i; + + printf("scsi_format: preparing\n"); + + fillbytes((caddr_t)scmd, sizeof(*scmd), '\0'); + scmd->addr = addr; + scmd->size = size; + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->timeout = 160 * 60; /* Do not know what to set */ + scmd->cdb.g5_cdb.cmd = 0x04; /* Format Unit */ + scmd->cdb.cmd_cdb[1] = 0x11; /* "FmtData" and "Format Code" */ + scmd->cdb.cmd_cdb[5] = 0; + + usalp->cmdname = "format unit"; + + printf("scsi_format: running\n"); + ret = (usal_cmd(usalp)); + printf("scsi_format: post processing %d\n", ret); + if (ret == -1) return ret; + if (background) { + if ((pid=fork()) == (pid_t)-1) + perror ("- [unable to fork()]"); + else { + if (!pid) { + while (1) { + if (test_unit_ready(usalp) >= 0) + break; + sleep(1); + } + return ret; + } + } + } + printf("Formating in progress: 0.00 %% done."); + sleep(20); + i = 0; + while (progress < 0xfff0 && !(progress == 0 && i > 50)) { + test_unit_ready(usalp); + request_sense_b(usalp, (caddr_t)sense_table, 18); + progress = sense_table[16]<<8|sense_table[17]; + printf("\rFormating in progress: %.2f %% done [%d]. ", (float)(progress*100)/0x10000,progress); + usleep(100000); + i++; + /*for (i=0; i < 18; i++) { + printf("%d ", sense_table[i]); + }*/ + } + sleep(10); + printf("\rFormating in progress: 100.00 %% done. \n"); + if (pid) exit (0); + return ret; +} + +/* + * XXX First try to handle ATAPI: + * XXX ATAPI cannot handle SCSI 6 byte commands. + * XXX We try to simulate 6 byte mode sense/select. + */ +static BOOL is_atapi; + +BOOL +allow_atapi(SCSI *usalp, BOOL new) +{ + BOOL old = is_atapi; + Uchar mode[256]; + + if (new == old) + return (old); + + usalp->silent++; + /* + * If a bad drive has been reset before, we may need to fire up two + * test unit ready commands to clear status. + */ + (void) unit_ready(usalp); + if (new && + mode_sense_g1(usalp, mode, 8, 0x3F, 0) < 0) { /* All pages current */ + new = FALSE; + } + usalp->silent--; + + is_atapi = new; + return (old); +} + +int +mode_select(SCSI *usalp, Uchar *dp, int cnt, int smp, int pf) +{ + if (is_atapi) + return (mode_select_sg0(usalp, dp, cnt, smp, pf)); + return (mode_select_g0(usalp, dp, cnt, smp, pf)); +} + +int +mode_sense(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf) +{ + if (is_atapi) + return (mode_sense_sg0(usalp, dp, cnt, page, pcf)); + return (mode_sense_g0(usalp, dp, cnt, page, pcf)); +} + +/* + * Simulate mode select g0 with mode select g1. + */ +int +mode_select_sg0(SCSI *usalp, Uchar *dp, int cnt, int smp, int pf) +{ + Uchar xmode[256+4]; + int amt = cnt; + + if (amt < 1 || amt > 255) { + /* XXX clear SCSI error codes ??? */ + return (-1); + } + + if (amt < 4) { /* Data length. medium type & VU */ + amt += 1; + } else { + amt += 4; + movebytes(&dp[4], &xmode[8], cnt-4); + } + xmode[0] = 0; + xmode[1] = 0; + xmode[2] = dp[1]; + xmode[3] = dp[2]; + xmode[4] = 0; + xmode[5] = 0; + i_to_2_byte(&xmode[6], (unsigned int)dp[3]); + + if (usalp->verbose) usal_prbytes("Mode Parameters (un-converted)", dp, cnt); + + return (mode_select_g1(usalp, xmode, amt, smp, pf)); +} + +/* + * Simulate mode sense g0 with mode sense g1. + */ +int +mode_sense_sg0(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf) +{ + Uchar xmode[256+4]; + int amt = cnt; + int len; + + if (amt < 1 || amt > 255) { + /* XXX clear SCSI error codes ??? */ + return (-1); + } + + fillbytes((caddr_t)xmode, sizeof (xmode), '\0'); + if (amt < 4) { /* Data length. medium type & VU */ + amt += 1; + } else { + amt += 4; + } + if (mode_sense_g1(usalp, xmode, amt, page, pcf) < 0) + return (-1); + + amt = cnt - usal_getresid(usalp); +/* + * For tests: Solaris 8 & LG CD-ROM always returns resid == amt + */ +/* amt = cnt;*/ + if (amt > 4) + movebytes(&xmode[8], &dp[4], amt-4); + len = a_to_u_2_byte(xmode); + if (len == 0) { + dp[0] = 0; + } else if (len < 6) { + if (len > 2) + len = 2; + dp[0] = len; + } else { + dp[0] = len - 3; + } + dp[1] = xmode[2]; + dp[2] = xmode[3]; + len = a_to_u_2_byte(&xmode[6]); + dp[3] = len; + + if (usalp->verbose) usal_prbytes("Mode Sense Data (converted)", dp, amt); + return (0); +} + +int +mode_select_g0(SCSI *usalp, Uchar *dp, int cnt, int smp, int pf) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = (caddr_t)dp; + scmd->size = cnt; + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = SC_MODE_SELECT; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + scmd->cdb.g0_cdb.high_addr = smp ? 1 : 0 | pf ? 0x10 : 0; + scmd->cdb.g0_cdb.count = cnt; + + if (usalp->verbose) { + fprintf(stderr, "%s ", smp?"Save":"Set "); + usal_prbytes("Mode Parameters", dp, cnt); + } + + usalp->cmdname = "mode select g0"; + + return (usal_cmd(usalp)); +} + +int +mode_select_g1(SCSI *usalp, Uchar *dp, int cnt, int smp, int pf) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = (caddr_t)dp; + scmd->size = cnt; + scmd->flags = SCG_DISRE_ENA; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = 0x55; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + scmd->cdb.g0_cdb.high_addr = smp ? 1 : 0 | pf ? 0x10 : 0; + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + if (usalp->verbose) { + printf("%s ", smp?"Save":"Set "); + usal_prbytes("Mode Parameters", dp, cnt); + } + + usalp->cmdname = "mode select g1"; + + return (usal_cmd(usalp)); +} + +int +mode_sense_g0(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = (caddr_t)dp; + scmd->size = 0xFF; + scmd->size = cnt; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = SC_MODE_SENSE; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); +#ifdef nonono + scmd->cdb.g0_cdb.high_addr = 1<<4; /* DBD Disable Block desc. */ +#endif + scmd->cdb.g0_cdb.mid_addr = (page&0x3F) | ((pcf<<6)&0xC0); + scmd->cdb.g0_cdb.count = page ? 0xFF : 24; + scmd->cdb.g0_cdb.count = cnt; + + usalp->cmdname = "mode sense g0"; + + if (usal_cmd(usalp) < 0) + return (-1); + if (usalp->verbose) usal_prbytes("Mode Sense Data", dp, cnt - usal_getresid(usalp)); + return (0); +} + +int +mode_sense_g1(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = (caddr_t)dp; + scmd->size = cnt; + 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 = 0x5A; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); +#ifdef nonono + scmd->cdb.g0_cdb.high_addr = 1<<4; /* DBD Disable Block desc. */ +#endif + scmd->cdb.g1_cdb.addr[0] = (page&0x3F) | ((pcf<<6)&0xC0); + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "mode sense g1"; + + if (usal_cmd(usalp) < 0) + return (-1); + if (usalp->verbose) usal_prbytes("Mode Sense Data", dp, cnt - usal_getresid(usalp)); + return (0); +} + +struct trackdesc { + Uchar res0; + +#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */ + Ucbit control : 4; + Ucbit adr : 4; +#else /* Motorola byteorder */ + Ucbit adr : 4; + Ucbit control : 4; +#endif + + Uchar track; + Uchar res3; + Uchar addr[4]; +}; + +struct diskinfo { + struct tocheader hd; + struct trackdesc desc[1]; +}; + +struct siheader { + Uchar len[2]; + Uchar finished; + Uchar unfinished; +}; + +struct sidesc { + Uchar sess_number; + Uchar res1; + Uchar track; + Uchar res3; + Uchar addr[4]; +}; + +struct sinfo { + struct siheader hd; + struct sidesc desc[1]; +}; + +struct trackheader { + Uchar mode; + Uchar res[3]; + Uchar addr[4]; +}; +#define TRM_ZERO 0 +#define TRM_USER_ECC 1 /* 2048 bytes user data + 288 Bytes ECC/EDC */ +#define TRM_USER 2 /* All user data (2336 bytes) */ + + +int +read_tochdr(SCSI *usalp, cdr_t *dp, int *fp, int *lp) +{ + struct tocheader *tp; + char xb[256]; + int len; + + tp = (struct tocheader *)xb; + + fillbytes((caddr_t)xb, sizeof (xb), '\0'); + if (read_toc(usalp, xb, 0, sizeof (struct tocheader), 0, FMT_TOC) < 0) { + if (usalp->silent == 0) + errmsgno(EX_BAD, "Cannot read TOC header\n"); + return (-1); + } + len = a_to_u_2_byte(tp->len) + sizeof (struct tocheader)-2; + if (len >= 4) { + if (fp) + *fp = tp->first; + if (lp) + *lp = tp->last; + return (0); + } + return (-1); +} + +int +read_cdtext(SCSI *usalp) +{ + struct tocheader *tp; + char xb[256]; + int len; + char xxb[10000]; + + tp = (struct tocheader *)xb; + + fillbytes((caddr_t)xb, sizeof (xb), '\0'); + if (read_toc(usalp, xb, 0, sizeof (struct tocheader), 0, FMT_CDTEXT) < 0) { + if (usalp->silent == 0 || usalp->verbose > 0) + errmsgno(EX_BAD, "Cannot read CD-Text header\n"); + return (-1); + } + len = a_to_u_2_byte(tp->len) + sizeof (struct tocheader)-2; + printf("CD-Text len: %d\n", len); + + if (read_toc(usalp, xxb, 0, len, 0, FMT_CDTEXT) < 0) { + if (usalp->silent == 0) + errmsgno(EX_BAD, "Cannot read CD-Text\n"); + return (-1); + } + { + FILE *f = fileopen("cdtext.dat", "wctb"); + filewrite(f, xxb, len); + } + return (0); +} + +int +read_trackinfo(SCSI *usalp, int track, long *offp, struct msf *msfp, int *adrp, + int *controlp, int *modep) +{ + struct diskinfo *dp; + char xb[256]; + int len; + + dp = (struct diskinfo *)xb; + + fillbytes((caddr_t)xb, sizeof (xb), '\0'); + if (read_toc(usalp, xb, track, sizeof (struct diskinfo), 0, FMT_TOC) < 0) { + if (usalp->silent <= 0) + errmsgno(EX_BAD, "Cannot read TOC\n"); + return (-1); + } + len = a_to_u_2_byte(dp->hd.len) + sizeof (struct tocheader)-2; + if (len < (int)sizeof (struct diskinfo)) + return (-1); + + if (offp) + *offp = a_to_4_byte(dp->desc[0].addr); + if (adrp) + *adrp = dp->desc[0].adr; + if (controlp) + *controlp = dp->desc[0].control; + + if (msfp) { + usalp->silent++; + if (read_toc(usalp, xb, track, sizeof (struct diskinfo), 1, FMT_TOC) + >= 0) { + msfp->msf_min = dp->desc[0].addr[1]; + msfp->msf_sec = dp->desc[0].addr[2]; + msfp->msf_frame = dp->desc[0].addr[3]; + } else if (read_toc(usalp, xb, track, sizeof (struct diskinfo), 0, FMT_TOC) + >= 0) { + /* + * Some drives (e.g. the Philips CDD-522) don't support + * to read the TOC in MSF mode. + */ + long off = a_to_4_byte(dp->desc[0].addr); + + lba_to_msf(off, msfp); + } else { + msfp->msf_min = 0; + msfp->msf_sec = 0; + msfp->msf_frame = 0; + } + usalp->silent--; + } + + if (modep == NULL) + return (0); + + if (track == 0xAA) { + *modep = -1; + return (0); + } + + fillbytes((caddr_t)xb, sizeof (xb), '\0'); + + usalp->silent++; + if (read_header(usalp, xb, *offp, 8, 0) >= 0) { + *modep = xb[0]; + } else if (read_track_info_philips(usalp, xb, track, 14) >= 0) { + *modep = xb[0xb] & 0xF; + } else { + *modep = -1; + } + usalp->silent--; + return (0); +} + +int +read_B0(SCSI *usalp, BOOL isbcd, long *b0p, long *lop) +{ + struct fdiskinfo *dp; + struct ftrackdesc *tp; + char xb[8192]; + char *pe; + int len; + long l; + + dp = (struct fdiskinfo *)xb; + + fillbytes((caddr_t)xb, sizeof (xb), '\0'); + if (read_toc_philips(usalp, xb, 1, sizeof (struct tocheader), 0, FMT_FULLTOC) < 0) { + return (-1); + } + len = a_to_u_2_byte(dp->hd.len) + sizeof (struct tocheader)-2; + if (len < (int)sizeof (struct fdiskinfo)) + return (-1); + if (read_toc_philips(usalp, xb, 1, len, 0, FMT_FULLTOC) < 0) { + return (-1); + } + if (usalp->verbose) { + usal_prbytes("TOC data: ", (Uchar *)xb, + len > (int)sizeof (xb) - usal_getresid(usalp) ? + sizeof (xb) - usal_getresid(usalp) : len); + + tp = &dp->desc[0]; + pe = &xb[len]; + + while ((char *)tp < pe) { + usal_prbytes("ENT: ", (Uchar *)tp, 11); + tp++; + } + } + tp = &dp->desc[0]; + pe = &xb[len]; + + for (; (char *)tp < pe; tp++) { + if (tp->sess_number != dp->hd.last) + continue; + if (tp->point != 0xB0) + continue; + if (usalp->verbose) + usal_prbytes("B0: ", (Uchar *)tp, 11); + if (isbcd) { + l = msf_to_lba(from_bcd(tp->amin), + from_bcd(tp->asec), + from_bcd(tp->aframe), TRUE); + } else { + l = msf_to_lba(tp->amin, + tp->asec, + tp->aframe, TRUE); + } + if (b0p) + *b0p = l; + + if (usalp->verbose) + printf("B0 start: %ld\n", l); + + if (isbcd) { + l = msf_to_lba(from_bcd(tp->pmin), + from_bcd(tp->psec), + from_bcd(tp->pframe), TRUE); + } else { + l = msf_to_lba(tp->pmin, + tp->psec, + tp->pframe, TRUE); + } + + if (usalp->verbose) + printf("B0 lout: %ld\n", l); + if (lop) + *lop = l; + return (0); + } + return (-1); +} + + +/* + * Return address of first track in last session (SCSI-3/mmc version). + */ +int +read_session_offset(SCSI *usalp, long *offp) +{ + struct diskinfo *dp; + char xb[256]; + int len; + + dp = (struct diskinfo *)xb; + + fillbytes((caddr_t)xb, sizeof (xb), '\0'); + if (read_toc(usalp, (caddr_t)xb, 0, sizeof (struct tocheader), 0, FMT_SINFO) < 0) + return (-1); + + if (usalp->verbose) + usal_prbytes("tocheader: ", + (Uchar *)xb, sizeof (struct tocheader) - usal_getresid(usalp)); + + len = a_to_u_2_byte(dp->hd.len) + sizeof (struct tocheader)-2; + if (len > (int)sizeof (xb)) { + errmsgno(EX_BAD, "Session info too big.\n"); + return (-1); + } + if (read_toc(usalp, (caddr_t)xb, 0, len, 0, FMT_SINFO) < 0) + return (-1); + + if (usalp->verbose) + usal_prbytes("tocheader: ", + (Uchar *)xb, len - usal_getresid(usalp)); + + dp = (struct diskinfo *)xb; + if (offp) + *offp = a_to_u_4_byte(dp->desc[0].addr); + return (0); +} + +/* + * Return address of first track in last session (pre SCSI-3 version). + */ +int +read_session_offset_philips(SCSI *usalp, long *offp) +{ + struct sinfo *sp; + char xb[256]; + int len; + + sp = (struct sinfo *)xb; + + fillbytes((caddr_t)xb, sizeof (xb), '\0'); + if (read_toc_philips(usalp, (caddr_t)xb, 0, sizeof (struct siheader), 0, FMT_SINFO) < 0) + return (-1); + len = a_to_u_2_byte(sp->hd.len) + sizeof (struct siheader)-2; + if (len > (int)sizeof (xb)) { + errmsgno(EX_BAD, "Session info too big.\n"); + return (-1); + } + if (read_toc_philips(usalp, (caddr_t)xb, 0, len, 0, FMT_SINFO) < 0) + return (-1); + /* + * Old drives return the number of finished sessions in first/finished + * a descriptor is returned for each session. + * New drives return the number of the first and last session + * one descriptor for the last finished session is returned + * as in SCSI-3 + * In all cases the lowest session number is set to 1. + */ + sp = (struct sinfo *)xb; + if (offp) + *offp = a_to_u_4_byte(sp->desc[sp->hd.finished-1].addr); + return (0); +} + +int +sense_secsize(SCSI *usalp, int current) +{ + Uchar mode[0x100]; + Uchar *p; + Uchar *ep; + int len; + int secsize = -1; + + usalp->silent++; + (void) unit_ready(usalp); + usalp->silent--; + + /* XXX Quick and dirty, musz verallgemeinert werden !!! */ + + fillbytes(mode, sizeof (mode), '\0'); + usalp->silent++; + + len = sizeof (struct scsi_mode_header) + + sizeof (struct scsi_mode_blockdesc); + /* + * Wenn wir hier get_mode_params() nehmen bekommen wir die Warnung: + * Warning: controller returns wrong page 1 for All pages page (3F). + */ + if (mode_sense(usalp, mode, len, 0x3F, current?0:2) < 0) { + fillbytes(mode, sizeof (mode), '\0'); + if (mode_sense(usalp, mode, len, 0, current?0:2) < 0) { /* VU (block desc) */ + usalp->silent--; + return (-1); + } + } + if (mode[3] == 8) { + if (usalp->debug) { + printf("Density: 0x%X\n", mode[4]); + printf("Blocks: %ld\n", a_to_u_3_byte(&mode[5])); + printf("Blocklen:%ld\n", a_to_u_3_byte(&mode[9])); + } + secsize = a_to_u_3_byte(&mode[9]); + } + fillbytes(mode, sizeof (mode), '\0'); + /* + * The ACARD TECH AEC-7720 ATAPI<->SCSI adaptor + * chokes if we try to transfer more than 0x40 bytes with + * mode_sense of all pages. So try to avoid to run this + * command if possible. + */ + if (usalp->debug && + mode_sense(usalp, mode, 0xFE, 0x3F, current?0:2) >= 0) { /* All Pages */ + + ep = mode+mode[0]; /* Points to last byte of data */ + p = &mode[4]; + p += mode[3]; + printf("Pages: "); + while (p < ep) { + printf("0x%X ", *p&0x3F); + p += p[1]+2; + } + printf("\n"); + } + usalp->silent--; + + return (secsize); +} + +int +select_secsize(SCSI *usalp, int secsize) +{ + struct scsi_mode_data md; + int count = sizeof (struct scsi_mode_header) + + sizeof (struct scsi_mode_blockdesc); + + (void) test_unit_ready(usalp); /* clear any error situation */ + + fillbytes((caddr_t)&md, sizeof (md), '\0'); + md.header.blockdesc_len = 8; + i_to_3_byte(md.blockdesc.lblen, secsize); + + return (mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2)); +} + +BOOL +is_cddrive(SCSI *usalp) +{ + return (usalp->inq->type == INQ_ROMD || usalp->inq->type == INQ_WORM); +} + +BOOL +is_unknown_dev(SCSI *usalp) +{ + return (usalp->dev == DEV_UNKNOWN); +} + +#ifndef DEBUG +#define DEBUG +#endif +#ifdef DEBUG + +int +read_scsi(SCSI *usalp, caddr_t bp, long addr, int cnt) +{ + if (addr <= G0_MAXADDR && cnt < 256 && !is_atapi) + return (read_g0(usalp, bp, addr, cnt)); + else + return (read_g1(usalp, bp, addr, cnt)); +} + +int +read_g0(SCSI *usalp, caddr_t bp, long addr, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + if (usalp->cap->c_bsize <= 0) + raisecond("capacity_not_set", 0L); + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt*usalp->cap->c_bsize; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G0_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g0_cdb.cmd = SC_READ; + scmd->cdb.g0_cdb.lun = usal_lun(usalp); + g0_cdbaddr(&scmd->cdb.g0_cdb, addr); + scmd->cdb.g0_cdb.count = cnt; +/* scmd->cdb.g0_cdb.vu_56 = 1;*/ + + usalp->cmdname = "read_g0"; + + return (usal_cmd(usalp)); +} + +int +read_g1(SCSI *usalp, caddr_t bp, long addr, int cnt) +{ + register struct usal_cmd *scmd = usalp->scmd; + + if (usalp->cap->c_bsize <= 0) + raisecond("capacity_not_set", 0L); + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->addr = bp; + scmd->size = cnt*usalp->cap->c_bsize; + 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_EREAD; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdbaddr(&scmd->cdb.g1_cdb, addr); + g1_cdblen(&scmd->cdb.g1_cdb, cnt); + + usalp->cmdname = "read_g1"; + + return (usal_cmd(usalp)); +} +#endif /* DEBUG */ + +BOOL +getdev(SCSI *usalp, BOOL print) +{ + BOOL got_inquiry = TRUE; + char vendor_info[8+1]; + char prod_ident[16+1]; + char prod_revision[4+1]; + int inq_len = 0; + register struct usal_cmd *scmd = usalp->scmd; + register struct scsi_inquiry *inq = usalp->inq; + + + fillbytes((caddr_t)inq, sizeof (*inq), '\0'); + usalp->dev = DEV_UNKNOWN; + usalp->silent++; + (void) unit_ready(usalp); + if (scmd->error >= SCG_FATAL && + !(scmd->scb.chk && scmd->sense_count > 0)) { + usalp->silent--; + return (FALSE); + } + + +/* if (scmd->error < SCG_FATAL || scmd->scb.chk && scmd->sense_count > 0){*/ + + if (inquiry(usalp, (caddr_t)inq, sizeof (*inq)) < 0) { + got_inquiry = FALSE; + } else { + inq_len = sizeof (*inq) - usal_getresid(usalp); + } + if (!got_inquiry) { + if (usalp->verbose) { + printf( + "error: %d scb.chk: %d sense_count: %d sense.code: 0x%x\n", + scmd->error, scmd->scb.chk, + scmd->sense_count, scmd->sense.code); + } + /* + * Folgende Kontroller kennen das Kommando + * INQUIRY nicht: + * + * ADAPTEC ACB-4000, ACB-4010, ACB 4070 + * SYSGEN SC4000 + * + * Leider reagieren ACB40X0 und ACB5500 identisch + * wenn drive not ready (code == not ready), + * sie sind dann nicht zu unterscheiden. + */ + + if (scmd->scb.chk && scmd->sense_count == 4) { + /* Test auf SYSGEN */ + (void) qic02(usalp, 0x12); /* soft lock on */ + if (qic02(usalp, 1) < 0) { /* soft lock off */ + usalp->dev = DEV_ACB40X0; +/* usalp->dev = acbdev();*/ + } else { + usalp->dev = DEV_SC4000; + inq->type = INQ_SEQD; + inq->removable = 1; + } + } + } else if (usalp->verbose) { + int i; + int len = inq->add_len + 5; + Uchar ibuf[256+5]; + Uchar *ip = (Uchar *)inq; + Uchar c; + + if (len > (int)sizeof (*inq) && + inquiry(usalp, (caddr_t)ibuf, inq->add_len+5) >= 0) { + len = inq->add_len+5 - usal_getresid(usalp); + ip = ibuf; + } else { + len = sizeof (*inq); + } + printf("Inquiry Data : "); + for (i = 0; i < len; i++) { + c = ip[i]; + if (c >= ' ' && c < 0177) + printf("%c", c); + else + printf("."); + } + printf("\n"); + } + + strncpy(vendor_info, inq->vendor_info, sizeof (inq->vendor_info)); + strncpy(prod_ident, inq->prod_ident, sizeof (inq->prod_ident)); + strncpy(prod_revision, inq->prod_revision, sizeof (inq->prod_revision)); + + vendor_info[sizeof (inq->vendor_info)] = '\0'; + prod_ident[sizeof (inq->prod_ident)] = '\0'; + prod_revision[sizeof (inq->prod_revision)] = '\0'; + + switch (inq->type) { + + case INQ_DASD: + if (inq->add_len == 0 && inq->vendor_info[0] != '\0') { + Uchar *p; + /* + * NT-4.0 creates fake inquiry data for IDE disks. + * Unfortunately, it does not set add_len wo we + * check if vendor_info, prod_ident and prod_revision + * contains valid chars for a CCS inquiry. + */ + if (inq_len >= 36) + inq->add_len = 31; + + for (p = (Uchar *)&inq->vendor_info[0]; + p < (Uchar *)&inq->prod_revision[4]; + p++) { + if (*p < 0x20 || *p > 0x7E) { + inq->add_len = 0; + break; + } + } + } + if (inq->add_len == 0) { + if (usalp->dev == DEV_UNKNOWN && got_inquiry) { + usalp->dev = DEV_ACB5500; + strncpy(inq->vendor_info, "ADAPTEC ", 8); + strncpy(inq->prod_ident,"ACB-5500 ", 16); + strncpy(inq->prod_revision, "FAKE", 4); + + } else switch (usalp->dev) { + case DEV_ACB40X0: + strncpy(inq->vendor_info, "ADAPTEC ", 8); + strncpy(inq->prod_ident, "ACB-40X0 ",16); + strncpy(inq->prod_revision, "FAKE", 4); + break; + case DEV_ACB4000: + strncpy(inq->vendor_info, "ADAPTEC ",8); + strncpy(inq->prod_ident, "ACB-4000 ",16); + strncpy(inq->prod_revision, "FAKE",4); + break; + case DEV_ACB4010: + strncpy(inq->vendor_info, "ADAPTEC ",8); + strncpy(inq->prod_ident, "ACB-4010 ",16); + strncpy(inq->prod_revision, "FAKE",4); + break; + case DEV_ACB4070: + strncpy(inq->vendor_info,"ADAPTEC ",8); + strncpy(inq->prod_ident, "ACB-4070 ", 16); + strncpy(inq->prod_revision, "FAKE",4 ); + break; + } + } else if (inq->add_len < 31) { + usalp->dev = DEV_NON_CCS_DSK; + + } else if (strbeg("EMULEX", vendor_info)) { + if (strbeg("MD21", prod_ident)) + usalp->dev = DEV_MD21; + if (strbeg("MD23", prod_ident)) + usalp->dev = DEV_MD23; + else + usalp->dev = DEV_CCS_GENDISK; + } else if (strbeg("ADAPTEC", vendor_info)) { + if (strbeg("ACB-4520", prod_ident)) + usalp->dev = DEV_ACB4520A; + if (strbeg("ACB-4525", prod_ident)) + usalp->dev = DEV_ACB4525; + else + usalp->dev = DEV_CCS_GENDISK; + } else if (strbeg("SONY", vendor_info) && + strbeg("SMO-C501", prod_ident)) { + usalp->dev = DEV_SONY_SMO; + } else { + usalp->dev = DEV_CCS_GENDISK; + } + break; + + case INQ_SEQD: + if (usalp->dev == DEV_SC4000) { + strncpy(inq->vendor_info,"SYSGEN ",8); + strncpy(inq->prod_ident, "SC4000 ",16); + strncpy(inq->prod_revision, "FAKE",4); + } else if (inq->add_len == 0 && + inq->removable && + inq->ansi_version == 1) { + usalp->dev = DEV_MT02; + strncpy(inq->vendor_info,"EMULEX ",8); + strncpy(inq->prod_ident, "MT02 ",16); + strncpy(inq->prod_revision, "FAKE",4); + } + break; + +/* case INQ_OPTD:*/ + case INQ_ROMD: + case INQ_WORM: + if (strbeg("RXT-800S", prod_ident)) + usalp->dev = DEV_RXT800S; + + /* + * Start of CD-Recorders: + */ + if (strbeg("ACER", vendor_info)) { + if (strbeg("CR-4020C", prod_ident)) + usalp->dev = DEV_RICOH_RO_1420C; + + } else if (strbeg("CREATIVE", vendor_info)) { + if (strbeg("CDR2000", prod_ident)) + usalp->dev = DEV_RICOH_RO_1060C; + + } else if (strbeg("GRUNDIG", vendor_info)) { + if (strbeg("CDR100IPW", prod_ident)) + usalp->dev = DEV_CDD_2000; + + } else if (strbeg("JVC", vendor_info)) { + if (strbeg("XR-W2001", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + else if (strbeg("XR-W2010", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + else if (strbeg("R2626", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + + } else if (strbeg("MITSBISH", vendor_info)) { + +#ifdef XXXX_REALLY + /* It's MMC compliant */ + if (strbeg("CDRW226", prod_ident)) + usalp->dev = DEV_MMC_CDRW; +#else + /* EMPTY */ +#endif + + } else if (strbeg("MITSUMI", vendor_info)) { + /* Don't know any product string */ + usalp->dev = DEV_CDD_522; + + } else if (strbeg("OPTIMA", vendor_info)) { + if (strbeg("CD-R 650", prod_ident)) + usalp->dev = DEV_SONY_CDU_924; + + } else if (strbeg("PHILIPS", vendor_info) || + strbeg("IMS", vendor_info) || + strbeg("KODAK", vendor_info) || + strbeg("HP", vendor_info)) { + + if (strbeg("CDD521/00", prod_ident)) + usalp->dev = DEV_CDD_521_OLD; + else if (strbeg("CDD521/02", prod_ident)) + usalp->dev = DEV_CDD_521_OLD; /* PCD 200R? */ + else if (strbeg("CDD521", prod_ident)) + usalp->dev = DEV_CDD_521; + + if (strbeg("CDD522", prod_ident)) + usalp->dev = DEV_CDD_522; + if (strbeg("PCD225", prod_ident)) + usalp->dev = DEV_CDD_522; + if (strbeg("KHSW/OB", prod_ident)) /* PCD600 */ + usalp->dev = DEV_PCD_600; + if (strbeg("CDR-240", prod_ident)) + usalp->dev = DEV_CDD_2000; + + if (strbeg("CDD20", prod_ident)) + usalp->dev = DEV_CDD_2000; + if (strbeg("CDD26", prod_ident)) + usalp->dev = DEV_CDD_2600; + + if (strbeg("C4324/C4325", prod_ident)) + usalp->dev = DEV_CDD_2000; + if (strbeg("CD-Writer 6020", prod_ident)) + usalp->dev = DEV_CDD_2600; + + } else if (strbeg("PINNACLE", vendor_info)) { + if (strbeg("RCD-1000", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + if (strbeg("RCD5020", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + if (strbeg("RCD5040", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + if (strbeg("RCD 4X4", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + + } else if (strbeg("PIONEER", vendor_info)) { + if (strbeg("CD-WO DW-S114X", prod_ident)) + usalp->dev = DEV_PIONEER_DW_S114X; + else if (strbeg("CD-WO DR-R504X", prod_ident)) /* Reoprt from philip@merge.com */ + usalp->dev = DEV_PIONEER_DW_S114X; + else if (strbeg("DVD-R DVR-S101", prod_ident)) + usalp->dev = DEV_PIONEER_DVDR_S101; + + } else if (strbeg("PLASMON", vendor_info)) { + if (strbeg("RF4100", prod_ident)) + usalp->dev = DEV_PLASMON_RF_4100; + else if (strbeg("CDR4220", prod_ident)) + usalp->dev = DEV_CDD_2000; + + } else if (strbeg("PLEXTOR", vendor_info)) { + if (strbeg("CD-R PX-R24CS", prod_ident)) + usalp->dev = DEV_RICOH_RO_1420C; + + } else if (strbeg("RICOH", vendor_info)) { + if (strbeg("RO-1420C", prod_ident)) + usalp->dev = DEV_RICOH_RO_1420C; + if (strbeg("RO1060C", prod_ident)) + usalp->dev = DEV_RICOH_RO_1060C; + + } else if (strbeg("SAF", vendor_info)) { /* Smart & Friendly */ + if (strbeg("CD-R2004", prod_ident) || + strbeg("CD-R2006 ", prod_ident)) + usalp->dev = DEV_SONY_CDU_924; + else if (strbeg("CD-R2006PLUS", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + else if (strbeg("CD-RW226", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + else if (strbeg("CD-R4012", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + + } else if (strbeg("SANYO", vendor_info)) { + if (strbeg("CD-WO CRD-R24S", prod_ident)) + usalp->dev = DEV_CDD_521; + + } else if (strbeg("SONY", vendor_info)) { + if (strbeg("CD-R CDU92", prod_ident) || + strbeg("CD-R CDU94", prod_ident)) + usalp->dev = DEV_SONY_CDU_924; + + } else if (strbeg("TEAC", vendor_info)) { + if (strbeg("CD-R50S", prod_ident) || + strbeg("CD-R55S", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + + } else if (strbeg("TRAXDATA", vendor_info) || + strbeg("Traxdata", vendor_info)) { + if (strbeg("CDR4120", prod_ident)) + usalp->dev = DEV_TEAC_CD_R50S; + + } else if (strbeg("T.YUDEN", vendor_info)) { + if (strbeg("CD-WO EW-50", prod_ident)) + usalp->dev = DEV_TYUDEN_EW50; + + } else if (strbeg("WPI", vendor_info)) { /* Wearnes */ + if (strbeg("CDR-632P", prod_ident)) + usalp->dev = DEV_CDD_2600; + + } else if (strbeg("YAMAHA", vendor_info)) { + if (strbeg("CDR10", prod_ident)) + usalp->dev = DEV_YAMAHA_CDR_100; + if (strbeg("CDR200", prod_ident)) + usalp->dev = DEV_YAMAHA_CDR_400; + if (strbeg("CDR400", prod_ident)) + usalp->dev = DEV_YAMAHA_CDR_400; + + } else if (strbeg("MATSHITA", vendor_info)) { + if (strbeg("CD-R CW-7501", prod_ident)) + usalp->dev = DEV_MATSUSHITA_7501; + if (strbeg("CD-R CW-7502", prod_ident)) + usalp->dev = DEV_MATSUSHITA_7502; + } + if (usalp->dev == DEV_UNKNOWN) { + /* + * We do not have Manufacturer strings for + * the following drives. + */ + if (strbeg("CDS615E", prod_ident)) /* Olympus */ + usalp->dev = DEV_SONY_CDU_924; + } + if (usalp->dev == DEV_UNKNOWN && inq->type == INQ_ROMD) { + BOOL cdrr = FALSE; + BOOL cdwr = FALSE; + BOOL cdrrw = FALSE; + BOOL cdwrw = FALSE; + BOOL dvd = FALSE; + BOOL dvdwr = FALSE; + + usalp->dev = DEV_CDROM; + + if (mmc_check(usalp, &cdrr, &cdwr, &cdrrw, &cdwrw, + &dvd, &dvdwr)) + usalp->dev = DEV_MMC_CDROM; + if (cdwr) + usalp->dev = DEV_MMC_CDR; + if (cdwrw) + usalp->dev = DEV_MMC_CDRW; + if (dvd) + usalp->dev = DEV_MMC_DVD; + if (dvdwr) + usalp->dev = DEV_MMC_DVD_WR; + } + break; + + case INQ_PROCD: + if (strbeg("BERTHOLD", vendor_info)) { + if (strbeg("", prod_ident)) + usalp->dev = DEV_HRSCAN; + } + break; + + case INQ_SCAN: + usalp->dev = DEV_MS300A; + break; + } + usalp->silent--; + if (!print) + return (TRUE); + + if (usalp->dev == DEV_UNKNOWN && !got_inquiry) { +#ifdef PRINT_INQ_ERR + usal_printerr(usalp); +#endif + return (FALSE); + } + + printinq(usalp, stdout); + return (TRUE); +} + +void +printinq(SCSI *usalp, FILE *f) +{ + register struct scsi_inquiry *inq = usalp->inq; + + fprintf(f, "Device type : "); + usal_fprintdev(f, inq); + fprintf(f, "Version : %d\n", inq->ansi_version); + fprintf(f, "Response Format: %d\n", inq->data_format); + if (inq->data_format >= 2) { + fprintf(f, "Capabilities : "); + if (inq->aenc) fprintf(f, "AENC "); + if (inq->termiop) fprintf(f, "TERMIOP "); + if (inq->reladr) fprintf(f, "RELADR "); + if (inq->wbus32) fprintf(f, "WBUS32 "); + if (inq->wbus16) fprintf(f, "WBUS16 "); + if (inq->sync) fprintf(f, "SYNC "); + if (inq->linked) fprintf(f, "LINKED "); + if (inq->cmdque) fprintf(f, "CMDQUE "); + if (inq->softreset) fprintf(f, "SOFTRESET "); + fprintf(f, "\n"); + } + if (inq->add_len >= 31 || + inq->vendor_info[0] || + inq->prod_ident[0] || + inq->prod_revision[0]) { + fprintf(f, "Vendor_info : '%.8s'\n", inq->vendor_info); + fprintf(f, "Identification : '%.16s'\n", inq->prod_ident); + fprintf(f, "Revision : '%.4s'\n", inq->prod_revision); + } +} + +void +printdev(SCSI *usalp) +{ + printf("Device seems to be: "); + + switch (usalp->dev) { + + case DEV_UNKNOWN: printf("unknown"); break; + case DEV_ACB40X0: printf("Adaptec 4000/4010/4070"); break; + case DEV_ACB4000: printf("Adaptec 4000"); break; + case DEV_ACB4010: printf("Adaptec 4010"); break; + case DEV_ACB4070: printf("Adaptec 4070"); break; + case DEV_ACB5500: printf("Adaptec 5500"); break; + case DEV_ACB4520A: printf("Adaptec 4520A"); break; + case DEV_ACB4525: printf("Adaptec 4525"); break; + case DEV_MD21: printf("Emulex MD21"); break; + case DEV_MD23: printf("Emulex MD23"); break; + case DEV_NON_CCS_DSK: printf("Generic NON CCS Disk"); break; + case DEV_CCS_GENDISK: printf("Generic CCS Disk"); break; + case DEV_SONY_SMO: printf("Sony SMO-C501"); break; + case DEV_MT02: printf("Emulex MT02"); break; + case DEV_SC4000: printf("Sysgen SC4000"); break; + case DEV_RXT800S: printf("Maxtor RXT800S"); break; + case DEV_HRSCAN: printf("Berthold HR-Scanner"); break; + case DEV_MS300A: printf("Microtek MS300A"); break; + + case DEV_CDROM: printf("Generic CD-ROM"); break; + case DEV_MMC_CDROM: printf("Generic mmc CD-ROM"); break; + case DEV_MMC_CDR: printf("Generic mmc CD-R"); break; + case DEV_MMC_CDRW: printf("Generic mmc CD-RW"); break; + case DEV_MMC_DVD: printf("Generic mmc2 DVD-ROM"); break; + case DEV_MMC_DVD_WR: printf("Generic mmc2 DVD-R/DVD-RW"); break; + case DEV_CDD_521_OLD: printf("Philips old CDD-521"); break; + case DEV_CDD_521: printf("Philips CDD-521"); break; + case DEV_CDD_522: printf("Philips CDD-522"); break; + case DEV_PCD_600: printf("Kodak PCD-600"); break; + case DEV_CDD_2000: printf("Philips CDD-2000"); break; + case DEV_CDD_2600: printf("Philips CDD-2600"); break; + case DEV_YAMAHA_CDR_100:printf("Yamaha CDR-100"); break; + case DEV_YAMAHA_CDR_400:printf("Yamaha CDR-400"); break; + case DEV_PLASMON_RF_4100:printf("Plasmon RF-4100"); break; + case DEV_SONY_CDU_924: printf("Sony CDU-924S"); break; + case DEV_RICOH_RO_1060C:printf("Ricoh RO-1060C"); break; + case DEV_RICOH_RO_1420C:printf("Ricoh RO-1420C"); break; + case DEV_TEAC_CD_R50S: printf("Teac CD-R50S"); break; + case DEV_MATSUSHITA_7501:printf("Matsushita CW-7501"); break; + case DEV_MATSUSHITA_7502:printf("Matsushita CW-7502"); break; + + case DEV_PIONEER_DW_S114X: printf("Pioneer DW-S114X"); break; + case DEV_PIONEER_DVDR_S101:printf("Pioneer DVD-R S101"); break; + + default: printf("Missing Entry for dev %d", + usalp->dev); break; + + } + printf(".\n"); + +} + +BOOL +do_inquiry(SCSI *usalp, int print) +{ + if (getdev(usalp, print)) { + if (print) + printdev(usalp); + return (TRUE); + } else { + return (FALSE); + } +} + +BOOL +recovery_needed(SCSI *usalp, cdr_t *dp) +{ + int err; + register struct usal_cmd *scmd = usalp->scmd; + + usalp->silent++; + err = test_unit_ready(usalp); + usalp->silent--; + + if (err >= 0) + return (FALSE); + else if (scmd->error >= SCG_FATAL) /* nicht selektierbar */ + return (FALSE); + + if (scmd->sense.code < 0x70) /* non extended Sense */ + return (FALSE); + + /* XXX Old Philips code */ + return (((struct scsi_ext_sense *)&scmd->sense)->sense_code == 0xD0); +} + +int +scsi_load(SCSI *usalp, cdr_t *dp) +{ + int key; + int code; + + if ((dp->cdr_flags & CDR_CADDYLOAD) == 0) { + if (scsi_start_stop_unit(usalp, 1, 1, dp && (dp->cdr_cmdflags&F_IMMED)) >= 0) + return (0); + } + + if (wait_unit_ready(usalp, 60)) + return (0); + + key = usal_sense_key(usalp); + code = usal_sense_code(usalp); + + if (key == SC_NOT_READY && (code == 0x3A || code == 0x30)) { + errmsgno(EX_BAD, "Cannot load media with %s drive!\n", + (dp->cdr_flags & CDR_CADDYLOAD) ? "caddy" : "this"); + errmsgno(EX_BAD, "Try to load media by hand.\n"); + } + return (-1); +} + +int +scsi_unload(SCSI *usalp, cdr_t *dp) +{ + return (scsi_start_stop_unit(usalp, 0, 1, dp && (dp->cdr_cmdflags&F_IMMED))); +} + +int +scsi_cdr_write(SCSI *usalp, + 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 */) +{ + return (write_xg1(usalp, bp, sectaddr, size, blocks)); +} + +struct cd_mode_page_2A * +mmc_cap(SCSI *usalp, Uchar *modep) +{ + int len; + int val; + Uchar mode[0x100]; + struct cd_mode_page_2A *mp; + struct cd_mode_page_2A *mp2; + + +retry: + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + if (!get_mode_params(usalp, 0x2A, "CD capabilities", + mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) { + + if (usal_sense_key(usalp) == SC_NOT_READY) { + if (wait_unit_ready(usalp, 60)) + goto retry; + } + return (NULL); /* Pre SCSI-3/mmc drive */ + } + + if (len == 0) /* Pre SCSI-3/mmc drive */ + return (NULL); + + mp = (struct cd_mode_page_2A *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); + + /* + * Do some heuristics against pre SCSI-3/mmc VU page 2A + * We should test for a minimum p_len of 0x14, but some + * buggy CD-ROM readers ommit the write speed values. + */ + if (mp->p_len < 0x10) + return (NULL); + + val = a_to_u_2_byte(mp->max_read_speed); + if (val != 0 && val < 176) + return (NULL); + + val = a_to_u_2_byte(mp->cur_read_speed); + if (val != 0 && val < 176) + return (NULL); + + len -= sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len; + if (modep) + mp2 = (struct cd_mode_page_2A *)modep; + else + mp2 = malloc(len); + if (mp2) + movebytes(mp, mp2, len); + + return (mp2); +} + +void +mmc_getval(struct cd_mode_page_2A *mp, + BOOL *cdrrp /* CD ROM */, + BOOL *cdwrp /* CD-R writer */, + BOOL *cdrrwp /* CD-RW reader */, + BOOL *cdwrwp /* CD-RW writer */, + BOOL *dvdp /* DVD reader */, + BOOL *dvdwp /* DVD writer */) +{ + BOOL isdvd; /* Any DVD reader */ + BOOL isdvd_wr; /* DVD writer (R / RAM) */ + BOOL iscd_wr; /* CD writer */ + + iscd_wr = (mp->cd_r_write != 0) || /* SCSI-3/mmc CD-R */ + (mp->cd_rw_write != 0); /* SCSI-3/mmc CD-RW */ + + if (cdrrp) + *cdrrp = (mp->cd_r_read != 0); /* SCSI-3/mmc CD */ + if (cdwrp) + *cdwrp = (mp->cd_r_write != 0); /* SCSI-3/mmc CD-R */ + + if (cdrrwp) + *cdrrwp = (mp->cd_rw_read != 0); /* SCSI-3/mmc CD */ + if (cdwrwp) + *cdwrwp = (mp->cd_rw_write != 0); /* SCSI-3/mmc CD-RW */ + + isdvd = /* SCSI-3/mmc2 DVD */ + (mp->dvd_ram_read + mp->dvd_r_read + + mp->dvd_rom_read) != 0; + + isdvd_wr = /* SCSI-3/mmc2 DVD writer*/ + (mp->dvd_ram_write + mp->dvd_r_write) != 0; + + if (dvdp) + *dvdp = isdvd; + if (dvdwp) + *dvdwp = isdvd_wr; +} + +BOOL +is_mmc(SCSI *usalp, BOOL *cdwp, BOOL *dvdwp) +{ + BOOL cdwr = FALSE; + BOOL cdwrw = FALSE; + + if (cdwp) + *cdwp = FALSE; + if (dvdwp) + *dvdwp = FALSE; + + if (!mmc_check(usalp, NULL, &cdwr, NULL, &cdwrw, NULL, dvdwp)) + return (FALSE); + + if (cdwp) + *cdwp = cdwr | cdwrw; + + return (TRUE); +} + +BOOL +mmc_check(SCSI *usalp, + BOOL *cdrrp /* CD ROM */, + BOOL *cdwrp /* CD-R writer */, + BOOL *cdrrwp /* CD-RW reader */, + BOOL *cdwrwp /* CD-RW writer */, + BOOL *dvdp /* DVD reader */, + BOOL *dvdwp /* DVD writer */) +{ + Uchar mode[0x100]; + BOOL was_atapi; + struct cd_mode_page_2A *mp; + + if (usalp->inq->type != INQ_ROMD) + return (FALSE); + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + was_atapi = allow_atapi(usalp, TRUE); + usalp->silent++; + mp = mmc_cap(usalp, mode); + usalp->silent--; + allow_atapi(usalp, was_atapi); + if (mp == NULL) + return (FALSE); + + mmc_getval(mp, cdrrp, cdwrp, cdrrwp, cdwrwp, dvdp, dvdwp); + + return (TRUE); /* Generic SCSI-3/mmc CD */ +} + +static void +print_speed(char *fmt, int val) +{ + printf(" %s: %5d kB/s", fmt, val); + printf(" (CD %3ux,", val/176); + printf(" DVD %2ux)\n", val/1385); +} + +#define DOES(what, flag) printf(" Does %s%s\n", flag?"":"not ", what) +#define IS(what, flag) printf(" Is %s%s\n", flag?"":"not ", what) +#define VAL(what, val) printf(" %s: %d\n", what, val[0]*256 + val[1]) +#define SVAL(what, val) printf(" %s: %s\n", what, val) + +void +print_capabilities(SCSI *usalp) +{ + BOOL was_atapi; + Uchar mode[0x100]; + struct cd_mode_page_2A *mp; +static const char *bclk[4] = {"32", "16", "24", "24 (I2S)"}; +static const char *load[8] = {"caddy", "tray", "pop-up", "reserved(3)", + "disc changer", "cartridge changer", + "reserved(6)", "reserved(7)" }; +static const char *rotctl[4] = {"CLV/PCAV", "CAV", "reserved(2)", "reserved(3)"}; + + + if (usalp->inq->type != INQ_ROMD) + return; + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + was_atapi = allow_atapi(usalp, TRUE); /* Try to switch to 10 byte mode cmds */ + usalp->silent++; + mp = mmc_cap(usalp, mode); + usalp->silent--; + allow_atapi(usalp, was_atapi); + if (mp == NULL) + return; + + printf("\nDrive capabilities, per"); + if (mp->p_len >= 28) + printf(" MMC-3"); + else if (mp->p_len >= 24) + printf(" MMC-2"); + else + printf(" MMC"); + printf(" page 2A:\n\n"); + + DOES("read CD-R media", mp->cd_r_read); + DOES("write CD-R media", mp->cd_r_write); + DOES("read CD-RW media", mp->cd_rw_read); + DOES("write CD-RW media", mp->cd_rw_write); + DOES("read DVD-ROM media", mp->dvd_rom_read); + DOES("read DVD-R media", mp->dvd_r_read); + DOES("write DVD-R media", mp->dvd_r_write); + DOES("read DVD-RAM media", mp->dvd_ram_read); + DOES("write DVD-RAM media", mp->dvd_ram_write); + DOES("support test writing", mp->test_write); + printf("\n"); + DOES("read Mode 2 Form 1 blocks", mp->mode_2_form_1); + DOES("read Mode 2 Form 2 blocks", mp->mode_2_form_2); + DOES("read digital audio blocks", mp->cd_da_supported); + if (mp->cd_da_supported) + DOES("restart non-streamed digital audio reads accurately", mp->cd_da_accurate); + DOES("support Buffer-Underrun-Free recording", mp->BUF); + DOES("read multi-session CDs", mp->multi_session); + DOES("read fixed-packet CD media using Method 2", mp->method2); + DOES("read CD bar code", mp->read_bar_code); + DOES("read R-W subcode information", mp->rw_supported); + if (mp->rw_supported) + DOES("return R-W subcode de-interleaved and error-corrected", mp->rw_deint_corr); + DOES("read raw P-W subcode data from lead in", mp->pw_in_lead_in); + DOES("return CD media catalog number", mp->UPC); + DOES("return CD ISRC information", mp->ISRC); + DOES("support C2 error pointers", mp->c2_pointers); + DOES("deliver composite A/V data", mp->composite); + printf("\n"); + DOES("play audio CDs", mp->audio_play); + if (mp->audio_play) { + VAL("Number of volume control levels", mp->num_vol_levels); + DOES("support individual volume control setting for each channel", mp->sep_chan_vol); + DOES("support independent mute setting for each channel", mp->sep_chan_mute); + DOES("support digital output on port 1", mp->digital_port_1); + DOES("support digital output on port 2", mp->digital_port_2); + if (mp->digital_port_1 || mp->digital_port_2) { + DOES("send digital data LSB-first", mp->LSBF); + DOES("set LRCK high for left-channel data", mp->RCK); + DOES("have valid data on falling edge of clock", mp->BCK); + SVAL("Length of data in BCLKs", bclk[mp->length]); + } + } + printf("\n"); + SVAL("Loading mechanism type", load[mp->loading_type]); + DOES("support ejection of CD via START/STOP command", mp->eject); + DOES("lock media on power up via prevent jumper", mp->prevent_jumper); + DOES("allow media to be locked in the drive via PREVENT/ALLOW command", mp->lock); + IS("currently in a media-locked state", mp->lock_state); + DOES("support changing side of disk", mp->side_change); + DOES("have load-empty-slot-in-changer feature", mp->sw_slot_sel); + DOES("support Individual Disk Present feature", mp->disk_present_rep); + printf("\n"); + print_speed("Maximum read speed", a_to_u_2_byte(mp->max_read_speed)); + print_speed("Current read speed", a_to_u_2_byte(mp->cur_read_speed)); + print_speed("Maximum write speed", a_to_u_2_byte(mp->max_write_speed)); + if (mp->p_len >= 28) + print_speed("Current write speed", a_to_u_2_byte(mp->v3_cur_write_speed)); + else + print_speed("Current write speed", a_to_u_2_byte(mp->cur_write_speed)); + if (mp->p_len >= 28) { + SVAL("Rotational control selected", rotctl[mp->rot_ctl_sel]); + } + VAL("Buffer size in KB", mp->buffer_size); + + if (mp->p_len >= 24) { + VAL("Copy management revision supported", mp->copy_man_rev); + } + + if (mp->p_len >= 28) { + struct cd_wr_speed_performance *pp; + Uint ndesc; + Uint i; + Uint n; + + ndesc = a_to_u_2_byte(mp->num_wr_speed_des); + pp = mp->wr_speed_des; + printf(" Number of supported write speeds: %d\n", ndesc); + for (i = 0; i < ndesc; i++, pp++) { + printf(" Write speed # %d:", i); + n = a_to_u_2_byte(pp->wr_speed_supp); + printf(" %5d kB/s", n); + printf(" %s", rotctl[pp->rot_ctl_sel]); + printf(" (CD %3ux,", n/176); + printf(" DVD %2ux)\n", n/1385); + } + } + + /* Generic SCSI-3/mmc CD */ +} diff --git a/wodim/wodim.c b/wodim/wodim.c index f930cce..90d8893 100644 --- a/wodim/wodim.c +++ b/wodim/wodim.c @@ -946,6 +946,40 @@ int main(int argc, char *argv[]) print_toc(usalp, dp); comexit(0); } + + if ((flags & F_FORMAT) || (dp->cdr_dstat->ds_flags & DSF_NEED_FORMAT)) { + printf("wodim: media format asked\n"); + /* + * Do not abort if OPC failes. Just give it a chance + * for better laser power calibration than without OPC. + * + * Ricoh drives return with a vendor unique sense code. + * This is most likely because they refuse to do OPC + * on a non blank media. + */ + usalp->silent++; + do_opc(usalp, dp, flags); + usalp->silent--; + wait_unit_ready(usalp, 120); + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + errmsg("Cannot get start time\n"); + + if ((*dp->cdr_format)(usalp, dp, formattype) < 0) { + errmsgno(EX_BAD, "Cannot format disk, aborting.\n"); + comexit(EX_BAD); + } + if (gettimeofday(&fixtime, (struct timezone *)0) < 0) + errmsg("Cannot get format time\n"); + if (lverbose) + prtimediff("Formatting time: ", &starttime, &fixtime); + + if (!wait_unit_ready(usalp, 240) || tracks == 0) { + comexit(0); + } + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + errmsg("Cannot get start time\n"); + } + #ifdef XXX if ((*dp->cdr_check_session)() < 0) { comexit(EX_BAD); @@ -1230,38 +1264,6 @@ int main(int argc, char *argv[]) comexit(0); } } - if (flags & F_FORMAT) { - printf("wodim: media format asked\n"); - /* - * Do not abort if OPC failes. Just give it a chance - * for better laser power calibration than without OPC. - * - * Ricoh drives return with a vendor unique sense code. - * This is most likely because they refuse to do OPC - * on a non blank media. - */ - usalp->silent++; - do_opc(usalp, dp, flags); - usalp->silent--; - wait_unit_ready(usalp, 120); - if (gettimeofday(&starttime, (struct timezone *)0) < 0) - errmsg("Cannot get start time\n"); - - if ((*dp->cdr_format)(usalp, dp, formattype) < 0) { - errmsgno(EX_BAD, "Cannot format disk, aborting.\n"); - comexit(EX_BAD); - } - if (gettimeofday(&fixtime, (struct timezone *)0) < 0) - errmsg("Cannot get format time\n"); - if (lverbose) - prtimediff("Formatting time: ", &starttime, &fixtime); - - if (!wait_unit_ready(usalp, 240) || tracks == 0) { - comexit(0); - } - if (gettimeofday(&starttime, (struct timezone *)0) < 0) - errmsg("Cannot get start time\n"); - } /* * Reset start time so we will not see blanking time and * writing time counted together. diff --git a/wodim/wodim.c.format b/wodim/wodim.c.format new file mode 100644 index 0000000..f930cce --- /dev/null +++ b/wodim/wodim.c.format @@ -0,0 +1,4701 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* + * + * Modified by Eduard Bloch in 08/2006 and later + */ + +/* @(#)cdrecord.c 1.310 06/02/09 Copyright 1995-2006 J. Schilling */ +/* + * Record data on a CD/CVD-Recorder + * + * Copyright (c) 1995-2006 J. Schilling + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_RESOURCE_H +#include /* for rlimit */ +#endif +#include +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PRIV_H +#include +#endif + +#include "xio.h" + +#include /* XXX wegen SC_NOT_READY */ +#include +#include /* XXX fuer read_buffer */ +#include "scsi_scan.h" + +#include "auheader.h" +#include "wodim.h" +#include "defaults.h" +#include "movesect.h" + +#ifdef __linux__ +#include /* for rawio capability */ +#endif + +#if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING -0 >= 0 +#ifdef HAVE_SYS_PRIOCNTL_H /* The preferred SYSvR4 schduler */ +#else +#define USE_POSIX_PRIORITY_SCHEDULING +#endif +#endif + +/* + * Map toc/track types into names. + */ +char *toc2name[] = { + "CD-DA", + "CD-ROM", + "CD-ROM XA mode 1", + "CD-ROM XA mode 2", + "CD-I", + "Illegal toc type 5", + "Illegal toc type 6", + "Illegal toc type 7", +}; + +/* + * Map sector types into names. + */ +char *st2name[] = { + "Illegal sector type 0", + "CD-ROM mode 1", + "CD-ROM mode 2", + "Illegal sector type 3", + "CD-DA without preemphasis", + "CD-DA with preemphasis", + "Illegal sector type 6", + "Illegal sector type 7", +}; + +/* + * Map data block types into names. + */ +char *db2name[] = { + "Raw (audio)", + "Raw (audio) with P/Q sub channel", + "Raw (audio) with P/W packed sub channel", + "Raw (audio) with P/W raw sub channel", + "Reserved mode 4", + "Reserved mode 5", + "Reserved mode 6", + "Vendor unique mode 7", + "CD-ROM mode 1", + "CD-ROM mode 2", + "CD-ROM XA mode 2 form 1", + "CD-ROM XA mode 2 form 1 (with subheader)", + "CD-ROM XA mode 2 form 2", + "CD-ROM XA mode 2 form 1/2/mix", + "Reserved mode 14", + "Vendor unique mode 15", +}; + +/* + * Map write modes into names. + */ +static char wm_none[] = "unknown"; +static char wm_ill[] = "illegal"; + +char *wm2name[] = { + wm_none, + "BLANK", + "FORMAT", + wm_ill, + "PACKET", + wm_ill, + wm_ill, + wm_ill, + "TAO", + wm_ill, + wm_ill, + wm_ill, + "SAO", + "SAO/RAW16", /* Most liklely not needed */ + "SAO/RAW96P", + "SAO/RAW96R", + "RAW", + "RAW/RAW16", + "RAW/RAW96P", + "RAW/RAW96R", +}; + +int debug; /* print debug messages */ +static int kdebug; /* print kernel debug messages */ +static int scsi_verbose; /* SCSI verbose flag */ +static int silent; /* SCSI silent flag */ +int lverbose; /* static verbose flag */ +int xdebug; /* extended debug flag */ + +char *buf; /* The transfer buffer */ +long bufsize = -1; /* The size of the transfer buffer */ + +static int gracetime = GRACE_TIME; +static int raw_speed = -1; +static int dma_speed = -1; +static int dminbuf = -1; /* XXX Hack for now drive min buf fill */ +BOOL isgui; +static int didintr; +char *driveropts; +static char *cuefilename = NULL; +static uid_t oeuid = (uid_t)-1; + +struct timeval starttime; +struct timeval wstarttime; +struct timeval stoptime; +struct timeval fixtime; + +static long fs = -1; /* fifo (ring buffer) size */ +static Llong warn_minisize = -1L; + +static int gracewait(cdr_t *dp, BOOL *didgracep); +static void cdrstats(cdr_t *dp); +static void susage(int); +static void usage(int); +static void blusage(int); +static void formattypeusage(int); +static void intr(int sig); +static void catchsig(int sig); +static int scsi_cb(void *arg); +static void intfifo(int sig); +static void exscsi(int excode, void *arg); +static void excdr(int excode, void *arg); +int read_buf(int f, char *bp, int size); +int fill_buf(int f, track_t *trackp, long secno, char *bp, int size); +int get_buf(int f, track_t *trackp, long secno, char **bpp, int size); +int write_secs(SCSI *usalp, cdr_t *dp, char *bp, long startsec, int bytespt, + int secspt, BOOL islast); +static int write_track_data(SCSI *usalp, cdr_t *, track_t *); +int pad_track(SCSI *usalp, cdr_t *dp, track_t *trackp, long startsec, + Llong amt, BOOL dolast, Llong *bytesp); +int write_buf(SCSI *usalp, cdr_t *dp, track_t *trackp, char *bp, + long startsec, Llong amt, int secsize, BOOL dolast, + Llong *bytesp); +static void printdata(int, track_t *); +static void printaudio(int, track_t *); +static void checkfile(int, track_t *); +static int checkfiles(int, track_t *); +static void setleadinout(int, track_t *); +static void setpregaps(int, track_t *); +static long checktsize(int, track_t *); +static void opentracks(track_t *); +static void checksize(track_t *); +static BOOL checkdsize(SCSI *usalp, cdr_t *dp, long tsize, int flags); +static void raise_fdlim(void); +static void raise_memlock(void); +static int gargs(int, char **, int *, track_t *, char **, int *, cdr_t **, + int *, long *, int *, int *); +static void set_trsizes(cdr_t *, int, track_t *); +void load_media(SCSI *usalp, cdr_t *, BOOL); +void unload_media(SCSI *usalp, cdr_t *, int); +void reload_media(SCSI *usalp, cdr_t *); +void set_secsize(SCSI *usalp, int secsize); +static int get_dmaspeed(SCSI *usalp, cdr_t *); +static BOOL do_opc(SCSI *usalp, cdr_t *, int); +static void check_recovery(SCSI *usalp, cdr_t *, int); +void audioread(SCSI *usalp, cdr_t *, int); +static void print_msinfo(SCSI *usalp, cdr_t *); +static void print_toc(SCSI *usalp, cdr_t *); +static void print_track(int, long, struct msf *, int, int, int); +#if !defined(HAVE_SYS_PRIOCNTL_H) +static int rt_raisepri(int); +#endif +void raisepri(int); +static void wait_input(void); +static void checkgui(void); +static int getbltype(char *optstr, long *typep); +static int getformattype(char *optstr, long *typep); +static void print_drflags(cdr_t *dp); +static void print_wrmodes(cdr_t *dp); +static BOOL check_wrmode(cdr_t *dp, int wmode, int tflags); +static void set_wrmode(cdr_t *dp, int wmode, int tflags); +static void linuxcheck(void); + +#ifdef __linux__ +static int get_cap(cap_value_t cap_array); +#endif + +struct exargs { + SCSI *usalp; + cdr_t *dp; + int old_secsize; + int flags; + int exflags; +} exargs; + +void fifo_cleanup(void) { + kill_faio(); +} + +/* shared variables */ +int scandevs = 0; +char *msifile = NULL; + +int main(int argc, char *argv[]) +{ + char *dev = NULL; + int timeout = 40; /* Set default timeout to 40s CW-7502 is slow*/ + int speed = -1; + long flags = 0L; + int blanktype = 0; + int formattype = 0; + int i; + int tracks = 0; + int trackno; + long tsize; + track_t track[MAX_TRACK+2]; /* Max tracks + track 0 + track AA */ + cdr_t *dp = (cdr_t *)0; + long startsec = 0L; + int errs = 0; + SCSI *usalp = NULL; + char errstr[80]; + BOOL gracedone = FALSE; + int ispacket; + BOOL is_cdwr = FALSE; + BOOL is_dvdwr = FALSE; + + buf=strstr(argv[0], "cdrecord"); + if(buf && '\0' == buf[8]) /* lame cheater detected */ + argv[0]="wodim"; + +#ifdef __EMX__ + /* This gives wildcard expansion with Non-Posix shells with EMX */ + _wildcard(&argc, &argv); +#endif + save_args(argc, argv); + oeuid = geteuid(); /* Remember saved set uid */ + + fillbytes(track, sizeof (track), '\0'); + for (i = 0; i < MAX_TRACK+2; i++) + track[i].track = track[i].trackno = i; + track[0].tracktype = TOC_MASK; + raise_fdlim(); + ispacket = gargs(argc, argv, &tracks, track, &dev, &timeout, &dp, &speed, &flags, + &blanktype, &formattype); + if ((track[0].tracktype & TOC_MASK) == TOC_MASK) + comerrno(EX_BAD, "Internal error: Bad TOC type.\n"); + + if (flags & F_VERSION) { + fprintf(stderr, + "Cdrecord-yelling-line-to-tell-frontends-to-use-it-like-version 2.01.01a03-dvd \n" + "Wodim " CDRKIT_VERSION "\n" + "Copyright (C) 2006 Cdrkit suite contributors\n" + "Based on works from Joerg Schilling, Copyright (C) 1995-2006, J. Schilling\n" + ); + exit(0); + } + + checkgui(); + + if (debug || lverbose) { + printf("TOC Type: %d = %s\n", + track[0].tracktype & TOC_MASK, + toc2name[track[0].tracktype & TOC_MASK]); + } + + if ((flags & (F_MSINFO|F_TOC|F_PRATIP|F_FIX|F_VERSION|F_CHECKDRIVE|F_INQUIRY|F_SCANBUS|F_RESET)) == 0) { + /* + * Try to lock us im memory (will only work for root) + * but you need access to root anyway to send SCSI commands. + * We need to be root to open /dev/usal? or similar devices + * on other OS variants and we need to be root to be able + * to send SCSI commands at least on AIX and + * Solaris (USCSI only) regardless of the permissions for + * opening files + * + * XXX The following test used to be + * XXX #if defined(HAVE_MLOCKALL) || defined(_POSIX_MEMLOCK) + * XXX but the definition for _POSIX_MEMLOCK did change during + * XXX the last 8 years and the autoconf test is better for + * XXX the static case. sysconf() only makes sense if we like + * XXX to check dynamically. + */ + raise_memlock(); +#if defined(HAVE_MLOCKALL) + /* + * XXX mlockall() needs root privilleges. + */ + if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) { + if(lverbose>2) + fprintf(stderr, + "W: Cannot do mlockall(2). Possibly increased risk for buffer underruns.\n"); + } +#endif + + /* + * XXX raisepri() needs root privilleges. + */ + raisepri(0); /* max priority */ + /* + * XXX shmctl(id, SHM_LOCK, 0) needs root privilleges. + * XXX So if we use SysV shared memory, wee need to be root. + * + * Note that not being able to set up a FIFO bombs us + * back to the DOS ages. Trying to run cdrecord without + * root privillegs is extremely silly, it breaks most + * of the advanced features. We need to be at least installed + * suid root or called by RBACs pfexec. + */ + init_fifo(fs); /* Attach shared memory (still one process) */ + } + + if ((flags & F_WAITI) != 0) { + if (lverbose) + printf("Waiting for data on stdin...\n"); + wait_input(); + } + + /* + * Call usal_remote() to force loading the remote SCSI transport library + * code that is located in librusal instead of the dummy remote routines + * that are located inside libusal. + */ + usal_remote(); + if (dev != NULL && + ((strncmp(dev, "HELP", 4) == 0) || + (strncmp(dev, "help", 4) == 0))) { + usal_help(stderr); + exit(0); + } + + if( (!dev || *dev=='\0'|| 0==strcmp(dev, "-1")) && (flags & F_SCANBUS)==0 ) { + int64_t need_size=0L; + struct stat statbuf; + int t; + + fprintf(stderr, "Device was not specified. Trying to find an appropriate drive...\n"); + + /* estimate how much data user wants to write */ + for(t=1;t<=tracks;t++) { + if(track[t].tracksize>=0) + need_size+=track[t].tracksize; + else if(0==stat(track[t].filename, &statbuf)) + need_size+=statbuf.st_size; + } + usalp=open_auto(need_size, debug, lverbose); + } + + if(!usalp) + usalp = usal_open(dev, errstr, sizeof(errstr), debug, lverbose); + + if(!usalp) + { + errmsg("\nCannot open SCSI driver!\n" + "For possible targets try 'wodim --devices' or 'wodim -scanbus'.\n" + "For possible transport specifiers try 'wodim dev=help'.\n" + "For IDE/ATAPI devices configuration, see the file README.ATAPI.setup from\n" + "the wodim documentation.\n"); + exit(EX_BAD); + } + +#ifdef HAVE_PRIV_SET +#ifdef PRIV_DEBUG + fprintf(stderr, "file_dac_read: %d\n", priv_ineffect(PRIV_FILE_DAC_READ)); +#endif + /* + * Give up privs we do not need anymore. + * We no longer need: + * file_dac_read,proc_lock_memory,proc_priocntl,net_privaddr + * We still need: + * sys_devices + */ + priv_set(PRIV_OFF, PRIV_EFFECTIVE, + PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY, + PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, NULL); + priv_set(PRIV_OFF, PRIV_PERMITTED, + PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY, + PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, NULL); + priv_set(PRIV_OFF, PRIV_INHERITABLE, + PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY, + PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, PRIV_SYS_DEVICES, NULL); +#endif + /* + * This is only for OS that do not support fine grained privs. + * + * XXX Below this point we do not need root privilleges anymore. + */ + if (geteuid() != getuid()) { /* AIX does not like to do this */ + /* If we are not root */ +#ifdef HAVE_SETREUID + if (setreuid(-1, getuid()) < 0) +#else +#ifdef HAVE_SETEUID + if (seteuid(getuid()) < 0) +#else + if (setuid(getuid()) < 0) +#endif +#endif + comerr("Panic cannot set back effective uid.\n"); + } + +#ifdef __linux__ + /* get the rawio capability */ + if (get_cap(CAP_SYS_RAWIO) && (debug || lverbose>2)) + { + perror("Warning: Cannot gain SYS_RAWIO capability"); + fprintf(stderr, "Possible reason: wodim not installed SUID root.\n"); + } +#endif + + /* + * WARNING: We now are no more able to do any privilleged operation + * unless we have been called by root. + * + * XXX It may be that we later get problems in init_faio() because + * XXX this function calls raisepri() to lower the priority slightly. + */ + usal_settimeout(usalp, timeout); + usalp->verbose = scsi_verbose; + usalp->silent = silent; + usalp->debug = debug; + usalp->kdebug = kdebug; + usalp->cap->c_bsize = DATA_SEC_SIZE; + + if ((flags & F_MSINFO) == 0 || lverbose) { + char *vers; + char *auth; + + + if(lverbose) + fprintf(stderr, "Wodim version: " CDRKIT_VERSION "\n"); + + vers = usal_version(0, SCG_VERSION); + auth = usal_version(0, SCG_AUTHOR); + if(lverbose >1 && auth && vers) + fprintf(stderr, "Using libusal version '%s-%s'.\n", auth, vers); + + + vers = usal_version(usalp, SCG_RVERSION); + auth = usal_version(usalp, SCG_RAUTHOR); + if (lverbose > 1 && vers && auth) + fprintf(stderr, "Using remote transport code version '%s-%s'\n", auth, vers); + } + + if (lverbose && driveropts) + printf("Driveropts: '%s'\n", driveropts); + +/* bufsize = usal_bufsize(usalp, CDR_BUF_SIZE);*/ + bufsize = usal_bufsize(usalp, bufsize); + if (lverbose || debug) + fprintf(stderr, "SCSI buffer size: %ld\n", bufsize); + if ((buf = usal_getbuf(usalp, bufsize)) == NULL) + comerr("Cannot get SCSI I/O buffer.\n"); + + if (scandevs) + return (list_devices(usalp, stdout, 0)); + + if ((flags & F_SCANBUS) != 0) { + select_target(usalp, stdout); + exit(0); + } + if ((flags & F_RESET) != 0) { + if (usal_reset(usalp, SCG_RESET_NOP) < 0) + comerr("Cannot reset (OS does not implement reset).\n"); + if (usal_reset(usalp, SCG_RESET_TGT) >= 0) + exit(0); + if (usal_reset(usalp, SCG_RESET_BUS) < 0) + comerr("Cannot reset target.\n"); + exit(0); + } + + /* + * First try to check which type of SCSI device we + * have. + if (debug || lverbose) + printf("atapi: %d\n", usal_isatapi(usalp)); + */ + usalp->silent++; + test_unit_ready(usalp); /* eat up unit attention */ + usalp->silent--; + if (!do_inquiry(usalp, (flags & F_MSINFO) == 0 || lverbose)) { + errmsgno(EX_BAD, "Cannot do inquiry for CD/DVD-Recorder.\n"); + if (unit_ready(usalp)) + errmsgno(EX_BAD, "The unit seems to be hung and needs power cycling.\n"); + exit(EX_BAD); + } +#ifdef GCONF + /* + * Debug only + */ + { + extern void gconf(SCSI *); + + if (lverbose > 2) + gconf(usalp); + } +#endif + + if ((flags & F_PRCAP) != 0) { + print_capabilities(usalp); + print_capabilities_mmc4(usalp); + exit(0); + } + if ((flags & F_INQUIRY) != 0) + exit(0); + + if (dp == (cdr_t *)NULL) { /* No driver= option specified */ + dp = get_cdrcmds(usalp); /* Calls dp->cdr_identify() */ + } + else if (!is_unknown_dev(usalp) && dp != get_cdrcmds(usalp)) { + errmsgno(EX_BAD, "WARNING: Trying to use other driver on known device.\n"); + } + is_mmc(usalp, &is_cdwr, &is_dvdwr); + if (ispacket) { + if (is_dvdwr) { + track[0].flags |= TI_PACKET; + /*XXX put here to only affect DVD writing, should be in gargs. + * however if set in args for all mode, packet writing is then + * broken for all disc as cdrecord assume that PACKET imply TAO which + * is not true at all???? */ + track[0].flags &= ~TI_TAO; + } + } + + if (dp == (cdr_t *)0) + comerrno(EX_BAD, "Sorry, no supported CD/DVD-Recorder found on this target.\n"); + + /* DVD does not support TAO */ + if (dp->is_dvd) { + if(lverbose>1) + fprintf(stderr, "Using Session At Once (SAO) for DVD mode.\n"); + dp->cdr_flags |= F_SAO; + for (i = 0; i <= MAX_TRACK; i++) { + track[i].flags &= ~TI_TAO; + track[i].flags |= TI_SAO; + } + } + + if (!is_cddrive(usalp)) + comerrno(EX_BAD, "Sorry, no CD/DVD-Drive found on this target.\n"); + /* + * The driver is known, set up data structures... + */ + { + cdr_t *ndp; + dstat_t *dsp; + + ndp = malloc(sizeof (cdr_t)); + dsp = malloc(sizeof (dstat_t)); + if (ndp == NULL || dsp == NULL) + comerr("Cannot allocate memory for driver structure.\n"); + movebytes(dp, ndp, sizeof (cdr_t)); + dp = ndp; + dp->cdr_flags |= CDR_ALLOC; + dp->cdr_cmdflags = flags; + + fillbytes(dsp, sizeof (*dsp), '\0'); + dsp->ds_minbuf = 0xFFFF; + dp->cdr_dstat = dsp; + } + + if ((flags & (F_MSINFO|F_TOC|F_LOAD|F_DLCK|F_EJECT)) == 0 || + tracks > 0 || + cuefilename != NULL) + { + if ((dp->cdr_flags & CDR_ISREADER) != 0) { + errmsgno(EX_BAD, + "Sorry, no CD/DVD-Recorder or unsupported CD/DVD-Recorder found on this target.\n"); + } + + if (!is_mmc(usalp, &is_cdwr, &is_dvdwr)) + is_cdwr = TRUE; /* If it is not MMC, it must be a CD writer */ + + if (is_dvdwr && !set_cdrcmds("mmc_mdvd", (cdr_t **)NULL)) { + errmsgno(EX_BAD, + "Internal error, DVD driver failure. Please report to debburn-devel@lists.alioth.debian.org.\n"); + } + /* + * Only exit if this is not the ProDVD test binary. + */ + if (!is_cdwr) + exit(EX_BAD); + } + + /* + * Set up data structures for current drive state. + */ + if ((*dp->cdr_attach)(usalp, dp) != 0) + comerrno(EX_BAD, "Cannot attach driver for CD/DVD-Recorder.\n"); + + if (lverbose > 1) { + printf("Drive current speed: %d\n", dp->cdr_dstat->ds_dr_cur_wspeed); + printf("Drive default speed: %d\n", dp->cdr_speeddef); + printf("Drive max speed : %d\n", dp->cdr_speedmax); + } + if (speed > (int)dp->cdr_speedmax && (flags & F_FORCE) == 0) + speed = dp->cdr_speedmax; + if (speed < 0) + speed = dp->cdr_speeddef; + + if (lverbose > 1) { + printf("Selected speed : %d\n", speed); + } + dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */ + + exargs.usalp = usalp; + exargs.dp = dp; + exargs.old_secsize = -1; + exargs.flags = flags; + + if ((flags & F_MSINFO) == 0 || lverbose) { + printf("Using %s (%s).\n", dp->cdr_drtext, dp->cdr_drname); + print_drflags(dp); + print_wrmodes(dp); + } + usalp->silent++; + if ((debug || lverbose)) { + tsize = -1; + if ((*dp->cdr_buffer_cap)(usalp, &tsize, (long *)0) < 0 || tsize < 0) { + if (read_buffer(usalp, buf, 4, 0) >= 0) + tsize = a_to_u_4_byte(buf); + } + if (tsize > 0) { + printf("Drive buf size : %lu = %lu KB\n", + tsize, tsize >> 10); + } + } + usalp->silent--; + + dma_speed = get_dmaspeed(usalp, dp); + + if ((debug || lverbose) && dma_speed > 0) { + /* + * We do not yet know what medium type is in... + */ + printf("Drive DMA Speed: %d kB/s %dx CD %dx DVD\n", + dma_speed, dma_speed/176, dma_speed/1385); + } + if ((tracks > 0 || cuefilename != NULL) && (debug || lverbose)) + printf("FIFO size : %lu = %lu KB\n", fs, fs >> 10); + +#ifdef HAVE_LIB_EDC_ECC + if ((flags & F_RAW) != 0 && (dp->cdr_dstat->ds_flags & DSF_DVD) == 0) + raw_speed = encspeed(debug || lverbose); +#endif + + if ((flags & F_CHECKDRIVE) != 0) + exit(0); + + if ((flags & F_ABORT) != 0) { + /* + * flush cache is not supported by CD-ROMs avoid prob with -toc + */ + usalp->silent++; + scsi_flush_cache(usalp, FALSE); + (*dp->cdr_abort_session)(usalp, dp); + usalp->silent--; + exit(0); + } + + if (tracks == 0 && cuefilename == NULL && + (flags & (F_FIX|F_BLANK)) == 0 && (flags & F_EJECT) != 0) { + /* + * Do not check if the unit is ready here to allow to open + * an empty unit too. + */ + unload_media(usalp, dp, flags); + exit(0); + } + flush(); + + if (cuefilename) { + parsecue(cuefilename, track); + tracks = track[0].tracks; + } else { + opentracks(track); + } + + if (tracks > 1) + sleep(2); /* Let the user watch the inquiry messages */ + + if (tracks > 0 && !check_wrmode(dp, flags, track[1].flags)) + comerrno(EX_BAD, "Illegal write mode for this drive.\n"); + + if ((track[0].flags & TI_TEXT) == 0 && /* CD-Text not yet processed */ + (track[MAX_TRACK+1].flags & TI_TEXT) != 0) { + /* + * CD-Text from textfile= or from CUE CDTEXTFILE will win + * over CD-Text from *.inf files and over CD-Text from + * CUE SONGWRITER, ... + */ + packtext(tracks, track); + track[0].flags |= TI_TEXT; + } +#ifdef CLONE_WRITE + if (flags & F_CLONE) { + clone_toc(track); + clone_tracktype(track); + } +#endif + setleadinout(tracks, track); + set_trsizes(dp, tracks, track); + setpregaps(tracks, track); + checkfiles(tracks, track); + tsize = checktsize(tracks, track); + + /* + * Make wm2name[wrmode] work. + * This must be done after the track flags have been set up + * by the functions above. + */ + if (tracks == 0 && (flags & F_BLANK) != 0) + dp->cdr_dstat->ds_wrmode = WM_BLANK; + else if (tracks == 0 && (flags & F_FORMAT) != 0) + dp->cdr_dstat->ds_wrmode = WM_FORMAT; + else + set_wrmode(dp, flags, track[1].flags); + + /* + * Debug only + */ + { + void *cp = NULL; + + (*dp->cdr_gen_cue)(track, &cp, FALSE); + if (cp) + free(cp); + } + + /* + * Create Lead-in data. Only needed in RAW mode. + */ + do_leadin(track); + + + /* + * Install exit handler before we change the drive status. + */ + on_comerr(exscsi, &exargs); + + if ((flags & F_FORCE) == 0) + load_media(usalp, dp, TRUE); + + if ((flags & (F_LOAD|F_DLCK)) != 0) { + if ((flags & F_DLCK) == 0) { + usalp->silent++; /* silently */ + scsi_prevent_removal( + usalp, 0); /* allow manual open */ + usalp->silent--; /* if load failed... */ + } + exit(0); /* we did not change status */ + } + exargs.old_secsize = sense_secsize(usalp, 1); + if (exargs.old_secsize < 0) + exargs.old_secsize = sense_secsize(usalp, 0); + if (debug) + printf("Current Secsize: %d\n", exargs.old_secsize); + usalp->silent++; + if (read_capacity(usalp) < 0) { + if (exargs.old_secsize > 0) + usalp->cap->c_bsize = exargs.old_secsize; + } + usalp->silent--; + if (exargs.old_secsize < 0) + exargs.old_secsize = usalp->cap->c_bsize; + if (exargs.old_secsize != usalp->cap->c_bsize) + errmsgno(EX_BAD, "Warning: blockdesc secsize %d differs from cap secsize %d\n", + exargs.old_secsize, usalp->cap->c_bsize); + + if (lverbose) + printf("Current Secsize: %d\n", exargs.old_secsize); + + if (exargs.old_secsize > 0 && exargs.old_secsize != DATA_SEC_SIZE) { + /* + * Some drives (e.g. Plextor) don't like to write correctly + * in SAO mode if the sector size is set to 512 bytes. + * In addition, wodim -msinfo will not work properly + * if the sector size is not 2048 bytes. + */ + set_secsize(usalp, DATA_SEC_SIZE); + } + + /* + * Is this the right place to do this ? + */ + check_recovery(usalp, dp, flags); + +/*audioread(dp, flags);*/ +/*unload_media(usalp, dp, flags);*/ +/*return 0;*/ + if (flags & F_WRITE) + dp->cdr_dstat->ds_cdrflags |= RF_WRITE; + if (flags & F_BLANK) + dp->cdr_dstat->ds_cdrflags |= RF_BLANK; + if (flags & F_PRATIP || lverbose > 0) { + dp->cdr_dstat->ds_cdrflags |= RF_PRATIP; + } + if (flags & F_IMMED || dminbuf > 0) { + if (dminbuf <= 0) + dminbuf = 50; + if (lverbose <= 0) /* XXX Hack needed for now */ + lverbose++; + dp->cdr_dstat->ds_cdrflags |= RF_WR_WAIT; + } + if ((*dp->cdr_getdisktype)(usalp, dp) < 0) { + errmsgno(EX_BAD, "Cannot get disk type.\n"); + if ((flags & F_FORCE) == 0) + comexit(EX_BAD); + } + if (flags & F_PRATIP) { + comexit(0); + } + /* + * The next actions should depend on the disk type. + */ + if (dma_speed > 0) { + if ((dp->cdr_dstat->ds_flags & DSF_DVD) == 0) + dma_speed /= 176; + else + dma_speed /= 1385; + } + + /* + * Init drive to default modes: + * + * We set TAO unconditionally to make checkdsize() work + * currectly in SAO mode too. + * + * At least MMC drives will not return the next writable + * address we expect when the drive's write mode is set + * to SAO. We need this address for mkisofs and thus + * it must be the first user accessible sector and not the + * first sector of the pregap. + * + * XXX The ACER drive: + * XXX Vendor_info : 'ATAPI ' + * XXX Identifikation : 'CD-R/RW 8X4X32 ' + * XXX Revision : '5.EW' + * XXX Will not return from -dummy to non-dummy without + * XXX opening the tray. + */ + usalp->silent++; + if ((*dp->cdr_init)(usalp, dp) < 0) + comerrno(EX_BAD, "Cannot init drive.\n"); + usalp->silent--; + + if (flags & F_SETDROPTS) { + /* + * Note that the set speed function also contains + * drive option processing for speed related drive options. + */ + if ((*dp->cdr_opt1)(usalp, dp) < 0) { + errmsgno(EX_BAD, "Cannot set up 1st set of driver options.\n"); + } + if ((*dp->cdr_set_speed_dummy)(usalp, dp, &speed) < 0) { + errmsgno(EX_BAD, "Cannot set speed/dummy.\n"); + } + dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */ + if ((*dp->cdr_opt2)(usalp, dp) < 0) { + errmsgno(EX_BAD, "Cannot set up 2nd set of driver options.\n"); + } + comexit(0); + } + /* + * XXX If dp->cdr_opt1() ever affects the result for + * XXX the multi session info we would need to move it here. + */ + if (flags & F_MSINFO) { + print_msinfo(usalp, dp); + comexit(0); + } + if (flags & F_TOC) { + print_toc(usalp, dp); + comexit(0); + } +#ifdef XXX + if ((*dp->cdr_check_session)() < 0) { + comexit(EX_BAD); + } +#endif + { + Int32_t omb = dp->cdr_dstat->ds_maxblocks; + + if ((*dp->cdr_opt1)(usalp, dp) < 0) { + errmsgno(EX_BAD, "Cannot set up 1st set of driver options.\n"); + } + if (tsize > 0 && omb != dp->cdr_dstat->ds_maxblocks) { + printf("Disk size changed by user options.\n"); + printf("Checking disk capacity according to new values.\n"); + } + } + if (tsize == 0) { + if (tracks > 0) { + errmsgno(EX_BAD, + "WARNING: Total disk size unknown. Data may not fit on disk.\n"); + } + } else if (tracks > 0) { + /* + * XXX How do we let the user check the remaining + * XXX disk size witout starting the write process? + */ + if (!checkdsize(usalp, dp, tsize, flags)) + comexit(EX_BAD); + } + if (tracks > 0 && fs > 0l) { +#if defined(USE_POSIX_PRIORITY_SCHEDULING) && defined(HAVE_SETREUID) + /* + * Hack to work around the POSIX design bug in real time + * priority handling: we need to be root even to lower + * our priority. + * Note that we need to find a more general way that works + * even on OS that do not support getreuid() which is *BSD + * and SUSv3 only. + */ + if (oeuid != getuid()) { + if (setreuid(-1, oeuid) < 0) + errmsg("Could set back effective uid.\n"); + } + +#endif + /* + * fork() here to start the extra process needed for + * improved buffering. + */ + if (!init_faio(track, bufsize)) + fs = 0L; + else + on_comerr(excdr, &exargs); + + atexit(fifo_cleanup); + +#if defined(USE_POSIX_PRIORITY_SCHEDULING) && defined(HAVE_SETREUID) + /* + * XXX Below this point we never need root privilleges anymore. + */ + if (geteuid() != getuid()) { /* AIX does not like to do this */ + /* If we are not root */ + if (setreuid(-1, getuid()) < 0) + comerr("Panic cannot set back effective uid.\n"); + } +#ifdef __linux__ + if (get_cap(CAP_SYS_RAWIO) && (debug || lverbose>2)) + perror("Error: Cannot gain SYS_RAWIO capability, is wodim installed SUID root? Reason"); +#endif + +#endif + } + + if ((*dp->cdr_set_speed_dummy)(usalp, dp, &speed) < 0) { + errmsgno(EX_BAD, "Cannot set speed/dummy.\n"); + if ((flags & F_FORCE) == 0) + comexit(EX_BAD); + } + dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */ + if ((flags & F_WRITE) != 0 && raw_speed >= 0) { + int max_raw = (flags & F_FORCE) != 0 ? raw_speed:raw_speed/2; + + if (getenv("CDR_FORCERAWSPEED")) + max_raw = raw_speed; + + for (i = 1; i <= MAX_TRACK; i++) { + /* + * Check for Clone tracks + */ + if ((track[i].sectype & ST_MODE_RAW) != 0) + continue; + /* + * Check for non-data tracks + */ + if ((track[i].sectype & ST_MODE_MASK) == ST_MODE_AUDIO) + continue; + + if (speed > max_raw) { + errmsgno(EX_BAD, + "Processor too slow. Cannot write RAW data at speed %d.\n", + speed); + comerrno(EX_BAD, "Max RAW data speed on this processor is %d.\n", + max_raw); + } + break; + } + } + if (tracks > 0 && (flags & F_WRITE) != 0 && dma_speed > 0) { + int max_dma = (dma_speed+1)*4/5; /* use an empirical formula to estimate available bandwith */ + + if((flags & F_FORCE) != 0 || getenv("CDR_FORCESPEED")) + max_dma = dma_speed; + + if (speed > max_dma) { + errmsgno(EX_BAD, + "DMA speed too slow (OK for %dx). Cannot write at speed %dx.\n", + max_dma, speed); + if ((dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0) { + errmsgno(EX_BAD, "Max DMA data speed is %d.\n", max_dma); + comerrno(EX_BAD, "Try to use 'driveropts=burnfree'.\n"); + } + } + } + if ((flags & (F_WRITE|F_BLANK)) != 0 && + (dp->cdr_dstat->ds_flags & DSF_ERA) != 0) { + if (xdebug) { + printf("Current speed %d, medium low speed: %d medium high speed: %d\n", + speed, + dp->cdr_dstat->ds_at_min_speed, + dp->cdr_dstat->ds_at_max_speed); + } + if (dp->cdr_dstat->ds_at_max_speed > 0 && + speed <= 8 && + speed > (int)dp->cdr_dstat->ds_at_max_speed) { + /* + * Be careful here: 10x media may be written faster. + * The current code will work as long as there is no + * writer that can only write faster than 8x + */ + if ((flags & F_FORCE) == 0) { + errmsgno(EX_BAD, + "Write speed %d of medium not sufficient for this writer.\n", + dp->cdr_dstat->ds_at_max_speed); + comerrno(EX_BAD, + "You may have used an ultra low speed medium on a high speed writer.\n"); + } + } + + if ((dp->cdr_dstat->ds_flags & DSF_ULTRASPP_ERA) != 0 && + (speed < 16 || (dp->cdr_cdrw_support & CDR_CDRW_ULTRAP) == 0)) { + if ((dp->cdr_cdrw_support & CDR_CDRW_ULTRAP) == 0) { + comerrno(EX_BAD, + "Trying to use ultra high speed+ medium on a writer which is not\ncompatible with ultra high speed+ media.\n"); + } else if ((flags & F_FORCE) == 0) { + comerrno(EX_BAD, + "Probably trying to use ultra high speed+ medium on improper writer.\n"); + } + } else if ((dp->cdr_dstat->ds_flags & DSF_ULTRASP_ERA) != 0 && + (speed < 16 || (dp->cdr_cdrw_support & CDR_CDRW_ULTRA) == 0)) { + if ((dp->cdr_cdrw_support & CDR_CDRW_ULTRA) == 0) { + comerrno(EX_BAD, + "Trying to use ultra high speed medium on a writer which is not\ncompatible with ultra high speed media.\n"); + } else if ((flags & F_FORCE) == 0) { + comerrno(EX_BAD, + "Probably trying to use ultra high speed medium on improper writer.\n"); + } + } + if (dp->cdr_dstat->ds_at_min_speed >= 4 && + dp->cdr_dstat->ds_at_max_speed > 4 && + dp->cdr_dstat->ds_dr_max_wspeed <= 4) { + if ((flags & F_FORCE) == 0) { + comerrno(EX_BAD, + "Trying to use high speed medium on low speed writer.\n"); + } + } + if ((int)dp->cdr_dstat->ds_at_min_speed > speed) { + if ((flags & F_FORCE) == 0) { + errmsgno(EX_BAD, + "Write speed %d of writer not sufficient for this medium.\n", + speed); + errmsgno(EX_BAD, + "You did use a %s speed medium on an improper writer or\n", + dp->cdr_dstat->ds_flags & DSF_ULTRASP_ERA ? + "ultra high": "high"); + comerrno(EX_BAD, + "you used a speed=# option with a speed too low for this medium.\n"); + } + } + } + if ((flags & (F_BLANK|F_FORCE)) == (F_BLANK|F_FORCE)) { + printf("Waiting for drive to calm down.\n"); + wait_unit_ready(usalp, 120); + if (gracewait(dp, &gracedone) < 0) { + /* + * In case kill() did not work ;-) + */ + errs++; + goto restore_it; + } + scsi_blank(usalp, 0L, blanktype, FALSE); + } + + /* + * Last chance to quit! + */ + if (gracewait(dp, &gracedone) < 0) { + /* + * In case kill() did not work ;-) + */ + errs++; + goto restore_it; + } + + if (dp->profile == 0x2B && flags & F_SAO && tsize > 0) { + printf("Preparing middle zone location for this DVD+R dual layer disc\n"); + if (!dp->cdr_layer_split(usalp, dp, tsize)) { + errmsgno(EX_BAD, "Cannot send structure for middle zone location.\n"); + comexit(EX_BAD); + } + } + + if (tracks > 0 && fs > 0l) { + /* + * Wait for the read-buffer to become full. + * This should be take no extra time if the input is a file. + * If the input is a pipe (e.g. mkisofs) this can take a + * while. If mkisofs dumps core before it starts writing, + * we abort before the writing process started. + */ + if (!await_faio()) { + comerrno(EX_BAD, "Input buffer error, aborting.\n"); + } + } + wait_unit_ready(usalp, 120); + + starttime.tv_sec = 0; + wstarttime.tv_sec = 0; + stoptime.tv_sec = 0; + fixtime.tv_sec = 0; + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + errmsg("Cannot get start time\n"); + + /* + * Blank the media if we were requested to do so + */ + if (flags & F_BLANK) { + /* + * Do not abort if OPC failes. Just give it a chance + * for better laser power calibration than without OPC. + * + * Ricoh drives return with a vendor unique sense code. + * This is most likely because they refuse to do OPC + * on a non blank media. + */ + usalp->silent++; + do_opc(usalp, dp, flags); + usalp->silent--; + wait_unit_ready(usalp, 120); + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + errmsg("Cannot get start time\n"); + + if ((*dp->cdr_blank)(usalp, dp, 0L, blanktype) < 0) { + errmsgno(EX_BAD, "Cannot blank disk, aborting.\n"); + if (blanktype != BLANK_DISC) { + errmsgno(EX_BAD, "Some drives do not support all blank types.\n"); + errmsgno(EX_BAD, "Try again with wodim blank=all.\n"); + } + comexit(EX_BAD); + } + if (gettimeofday(&fixtime, (struct timezone *)0) < 0) + errmsg("Cannot get blank time\n"); + if (lverbose) + prtimediff("Blanking time: ", &starttime, &fixtime); + + /* + * XXX Erst blank und dann format? + * XXX Wenn ja, dann hier (flags & F_FORMAT) testen + * + * EB: nee, besser nicht + */ + if (!wait_unit_ready(usalp, 240) || tracks == 0) { + comexit(0); + } + } + if (flags & F_FORMAT) { + printf("wodim: media format asked\n"); + /* + * Do not abort if OPC failes. Just give it a chance + * for better laser power calibration than without OPC. + * + * Ricoh drives return with a vendor unique sense code. + * This is most likely because they refuse to do OPC + * on a non blank media. + */ + usalp->silent++; + do_opc(usalp, dp, flags); + usalp->silent--; + wait_unit_ready(usalp, 120); + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + errmsg("Cannot get start time\n"); + + if ((*dp->cdr_format)(usalp, dp, formattype) < 0) { + errmsgno(EX_BAD, "Cannot format disk, aborting.\n"); + comexit(EX_BAD); + } + if (gettimeofday(&fixtime, (struct timezone *)0) < 0) + errmsg("Cannot get format time\n"); + if (lverbose) + prtimediff("Formatting time: ", &starttime, &fixtime); + + if (!wait_unit_ready(usalp, 240) || tracks == 0) { + comexit(0); + } + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + errmsg("Cannot get start time\n"); + } + /* + * Reset start time so we will not see blanking time and + * writing time counted together. + */ + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + errmsg("Cannot get start time\n"); + if (tracks == 0 && (flags & F_FIX) == 0) + comerrno(EX_BAD, "No tracks found.\n"); + /* + * Get the number of the next recordable track by reading the TOC and + * use the number the last current track number. + */ + usalp->silent++; + if (read_tochdr(usalp, dp, NULL, &trackno) < 0) { + trackno = 0; + } + usalp->silent--; + + /* If it is DVD, the information in TOC is fabricated :) + The real information is from read disk info command*/ + if((dp->cdr_dstat->ds_disktype&DT_DVD) && (dp->cdr_dstat->ds_trlast>0)){ + trackno=dp->cdr_dstat->ds_trlast-1; + if (lverbose > 2) + printf("trackno=%d\n",trackno); + } + + if ((tracks + trackno) > MAX_TRACK) { + /* + * XXX How many tracks are allowed on a DVD? + */ + comerrno(EX_BAD, "Too many tracks for this disk, last track number is %d.\n", + tracks + trackno); + } + + for (i = 0; i <= tracks+1; i++) { /* Lead-in ... Lead-out */ + track[i].trackno = i + trackno; /* Set up real track # */ + } + + if ((*dp->cdr_opt2)(usalp, dp) < 0) { + errmsgno(EX_BAD, "Cannot set up 2nd set of driver options.\n"); + } + + /* + * Now we actually start writing to the CD/DVD. + * XXX Check total size of the tracks and remaining size of disk. + */ + if ((*dp->cdr_open_session)(usalp, dp, track) < 0) { + comerrno(EX_BAD, "Cannot open new session.\n"); + } + if (!do_opc(usalp, dp, flags)) + comexit(EX_BAD); + + /* + * As long as open_session() will do nothing but + * set up parameters, we may leave fix_it here. + * I case we have to add an open_session() for a drive + * that wants to do something that modifies the disk + * We have to think about a new solution. + */ + if (flags & F_FIX) + goto fix_it; + + /* + * This call may modify trackp[i].trackstart for all tracks. + */ + if ((*dp->cdr_write_leadin)(usalp, dp, track) < 0) + comerrno(EX_BAD, "Could not write Lead-in.\n"); + + if (lverbose && (dp->cdr_dstat->ds_cdrflags & RF_LEADIN) != 0) { + + if (gettimeofday(&fixtime, (struct timezone *)0) < 0) + errmsg("Cannot get lead-in write time\n"); + prtimediff("Lead-in write time: ", &starttime, &fixtime); + } + + if (gettimeofday(&wstarttime, (struct timezone *)0) < 0) + errmsg("Cannot get start time\n"); + for (i = 1; i <= tracks; i++) { + startsec = 0L; + + if ((*dp->cdr_open_track)(usalp, dp, &track[i]) < 0) { + errmsgno(EX_BAD, "Cannot open next track.\n"); + errs++; + break; + } + + if ((flags & (F_SAO|F_RAW)) == 0) { + if ((*dp->cdr_next_wr_address)(usalp, &track[i], &startsec) < 0) { + errmsgno(EX_BAD, "Cannot get next writable address.\n"); + errs++; + break; + } + track[i].trackstart = startsec; + } + if (debug || lverbose) { + printf("Starting new track at sector: %ld\n", + track[i].trackstart); + flush(); + } + if (write_track_data(usalp, dp, &track[i]) < 0) { + if (cdr_underrun(usalp)) { + errmsgno(EX_BAD, + "The current problem looks like a buffer underrun.\n"); + if ((dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0) + errmsgno(EX_BAD, "Try to use 'driveropts=burnfree'.\n"); + else { + errmsgno(EX_BAD, "It looks like 'driveropts=burnfree' does not work for this drive.\n"); + errmsgno(EX_BAD, "Please report.\n"); + } + + errmsgno(EX_BAD, + "Make sure that you are root, enable DMA and check your HW/OS set up.\n"); + } else { + errmsgno(EX_BAD, "A write error occured.\n"); + errmsgno(EX_BAD, "Please properly read the error message above.\n"); + } + errs++; + sleep(5); + unit_ready(usalp); + (*dp->cdr_close_track)(usalp, dp, &track[i]); + break; + } + if ((*dp->cdr_close_track)(usalp, dp, &track[i]) < 0) { + /* + * Check for "Dummy blocks added" message first. + */ + if (usal_sense_key(usalp) != SC_ILLEGAL_REQUEST || + usal_sense_code(usalp) != 0xB5) { + errmsgno(EX_BAD, "Cannot close track.\n"); + errs++; + break; + } + } + } +fix_it: + if (gettimeofday(&stoptime, (struct timezone *)0) < 0) + errmsg("Cannot get stop time\n"); + cdrstats(dp); + + if (flags & F_RAW) { + if (lverbose) { + printf("Writing Leadout...\n"); + flush(); + } + write_leadout(usalp, dp, track); + } + if ((flags & F_NOFIX) == 0) { + if (lverbose) { + printf("Fixating...\n"); + flush(); + } + if ((*dp->cdr_fixate)(usalp, dp, track) < 0) { + /* + * Ignore fixating errors in dummy mode. + */ + if ((flags & F_DUMMY) == 0) { + errmsgno(EX_BAD, "Cannot fixate disk.\n"); + errs++; + } + } + if (gettimeofday(&fixtime, (struct timezone *)0) < 0) + errmsg("Cannot get fix time\n"); + if (lverbose) + prtimediff("Fixating time: ", &stoptime, &fixtime); + } + if ((dp->cdr_dstat->ds_cdrflags & RF_DID_CDRSTAT) == 0) { + dp->cdr_dstat->ds_cdrflags |= RF_DID_CDRSTAT; + (*dp->cdr_stats)(usalp, dp); + } + if ((flags & (F_RAW|F_EJECT)) == F_RAW) { + /* + * Most drives seem to forget to reread the TOC from disk + * if they are in RAW mode. + */ + usalp->silent++; + if (read_tochdr(usalp, dp, NULL, NULL) < 0) { + usalp->silent--; + if ((flags & F_DUMMY) == 0) + reload_media(usalp, dp); + } else { + usalp->silent--; + } + } + +restore_it: + /* + * Try to restore the old sector size and stop FIFO. + */ + kill_faio(); + comexit(errs?-2:0); + return (0); +} + +static int +gracewait(cdr_t *dp, BOOL *didgracep) +{ + int i; + BOOL didgrace = FALSE; + + if (didgracep) + didgrace = *didgracep; + + if(warn_minisize>=0) { + fprintf(stderr, "\nWARNING: found a microscopic small track size (%lld bytes).\n" + " Do you really want to write this image? Press Ctrl-C to abort...\n\n", + warn_minisize); + gracetime+=20; + } + if (gracetime < MIN_GRACE_TIME) + gracetime = MIN_GRACE_TIME; + if (gracetime > 999) + gracetime = 999; + + printf("Starting to write CD/DVD at speed %5.1f in %s%s %s mode for %s session.\n", + (float)dp->cdr_dstat->ds_wspeed, + (dp->cdr_cmdflags & F_DUMMY) ? "dummy" : "real", + (dp->cdr_cmdflags & F_FORCE) ? " force" : "", + wm2name[dp->cdr_dstat->ds_wrmode], + (dp->cdr_cmdflags & F_MULTI) ? "multi" : "single"); + if (didgrace) { + printf("No chance to quit anymore."); + goto grace_done; + } + printf("Last chance to quit, starting %s write in %4d seconds.", + (dp->cdr_cmdflags & F_DUMMY)?"dummy":"real", gracetime); + flush(); + signal(SIGINT, intr); + signal(SIGHUP, intr); + signal(SIGTERM, intr); + + for (i = gracetime; --i >= 0; ) { + sleep(1); + if (didintr) { + printf("\n"); + excdr(SIGINT, &exargs); + signal(SIGINT, SIG_DFL); + kill(getpid(), SIGINT); + /* + * In case kill() did not work ;-) + */ + if (didgracep) + *didgracep = FALSE; + return (-1); + } + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b%4d seconds.", i); + flush(); + } +grace_done: + printf(" Operation starts."); + flush(); + signal(SIGINT, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGINT, intfifo); + signal(SIGHUP, intfifo); + signal(SIGTERM, intfifo); + printf("\n"); + + if (didgracep) + *didgracep = TRUE; + return (0); +} + +static void +cdrstats(cdr_t *dp) +{ + float secsps = 75.0; + int nsecs; + float fspeed; + struct timeval tcur; + struct timeval tlast; + BOOL nostop = FALSE; + + if (starttime.tv_sec == 0) + return; + + if (stoptime.tv_sec == 0) { + gettimeofday(&stoptime, (struct timezone *)0); + nostop = TRUE; + } + + if ((dp->cdr_dstat->ds_cdrflags & RF_DID_STAT) != 0) + return; + dp->cdr_dstat->ds_cdrflags |= RF_DID_STAT; + + if (lverbose == 0) + return; + + if (dp->cdr_cmdflags & F_FIX) + return; + + if ((dp->cdr_cmdflags & (F_WRITE|F_BLANK)) == F_BLANK) + return; + + tlast = wstarttime; + tcur = stoptime; + + prtimediff("Writing time: ", &starttime, &stoptime); + + nsecs = dp->cdr_dstat->ds_endsec - dp->cdr_dstat->ds_startsec; + + if (dp->cdr_dstat->ds_flags & DSF_DVD) + secsps = 676.27; + + tlast.tv_sec = tcur.tv_sec - tlast.tv_sec; + tlast.tv_usec = tcur.tv_usec - tlast.tv_usec; + while (tlast.tv_usec < 0) { + tlast.tv_usec += 1000000; + tlast.tv_sec -= 1; + } + if (!nostop && nsecs != 0 && dp->cdr_dstat->ds_endsec > 0) { + /* + * May not be known (e.g. cdrecord -) + * + * XXX if we later allow this code to know how much has + * XXX actually been written, then we may remove the + * XXX dependance from nostop & nsecs != 0 + */ + fspeed = (nsecs / secsps) / + (tlast.tv_sec * 1.0 + tlast.tv_usec * 0.000001); + if (fspeed > 999.0) + fspeed = 999.0; + if (dp->is_dvd) fspeed /= 9; + printf("Average write speed %5.1fx.\n", fspeed); + } + + if (dp->cdr_dstat->ds_minbuf <= 100) { + printf("Min drive buffer fill was %u%%\n", + (unsigned int)dp->cdr_dstat->ds_minbuf); + } + if (dp->cdr_dstat->ds_buflow > 0) { + printf("Total of %ld possible drive buffer underruns predicted.\n", + (long)dp->cdr_dstat->ds_buflow); + } +} + +/* + * Short usage + */ +static void +susage(int ret) +{ + fprintf(stderr, "Usage: %s [options] track1...trackn\n", get_progname()); + fprintf(stderr, "\nUse\t%s -help\n", get_progname()); + fprintf(stderr, "to get a list of valid options.\n"); + fprintf(stderr, "\nUse\t%s blank=help\n", get_progname()); + fprintf(stderr, "to get a list of valid blanking options.\n"); + fprintf(stderr, "\nUse\t%s dev=b,t,l driveropts=help -checkdrive\n", get_progname()); + fprintf(stderr, "to get a list of drive specific options.\n"); + fprintf(stderr, "\nUse\t%s dev=help\n", get_progname()); + fprintf(stderr, "to get a list of possible SCSI transport specifiers.\n"); + exit(ret); + /* NOTREACHED */ +} + +static void +usage(int excode) +{ + fprintf(stderr, "Usage: %s [options] track1...trackn\n", get_progname()); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "\t-version print version information and exit\n"); + fprintf(stderr, "\tdev=target SCSI target to use as CD/DVD-Recorder\n"); + fprintf(stderr, "\tgracetime=# set the grace time before starting to write to #.\n"); + fprintf(stderr, "\ttimeout=# set the default SCSI command timeout to #.\n"); + fprintf(stderr, "\tdebug=#,-d Set to # or increment misc debug level\n"); + fprintf(stderr, "\tkdebug=#,kd=# do Kernel debugging\n"); + fprintf(stderr, "\t-verbose,-v increment general verbose level by one\n"); + fprintf(stderr, "\t-Verbose,-V increment SCSI command transport verbose level by one\n"); + fprintf(stderr, "\t-silent,-s do not print status of failed SCSI commands\n"); + fprintf(stderr, "\tdriver=name user supplied driver name, use with extreme care\n"); + fprintf(stderr, "\tdriveropts=opt a comma separated list of driver specific options\n"); + fprintf(stderr, "\t-setdropts set driver specific options and exit\n"); + fprintf(stderr, "\t-checkdrive check if a driver for the drive is present\n"); + fprintf(stderr, "\t-prcap print drive capabilities for MMC compliant drives\n"); + fprintf(stderr, "\t-inq do an inquiry for the drive and exit\n"); + fprintf(stderr, "\t-scanbus scan the SCSI and IDE buses and exit\n"); + fprintf(stderr, "\t-reset reset the SCSI bus with the cdrecorder (if possible)\n"); + fprintf(stderr, "\t-abort send an abort sequence to the drive (may help if hung)\n"); + fprintf(stderr, "\t-overburn allow to write more than the official size of a medium\n"); + fprintf(stderr, "\t-ignsize ignore the known size of a medium (may cause problems)\n"); + fprintf(stderr, "\t-useinfo use *.inf files to overwrite audio options.\n"); + fprintf(stderr, "\tspeed=# set speed of drive\n"); + fprintf(stderr, "\tblank=type blank a CD-RW disc (see blank=help)\n"); + fprintf(stderr, "\t-format format a CD-RW/DVD-RW/DVD+RW disc\n"); + fprintf(stderr, "\tformattype=# select the format method for DVD+RW disc\n"); +#ifdef FIFO + fprintf(stderr, "\tfs=# Set fifo size to # (0 to disable, default is %ld MB)\n", + DEFAULT_FIFOSIZE/(1024L*1024L)); +#endif + fprintf(stderr, "\tts=# set maximum transfer size for a single SCSI command\n"); + fprintf(stderr, "\t-load load the disk and exit (works only with tray loader)\n"); + fprintf(stderr, "\t-lock load and lock the disk and exit (works only with tray loader)\n"); + fprintf(stderr, "\t-eject eject the disk after doing the work\n"); + fprintf(stderr, "\t-dummy do everything with laser turned off\n"); + fprintf(stderr, "\t-msinfo retrieve multi-session info for genisoimage\n"); + fprintf(stderr, "\t-msifile=path run -msinfo and copy output to file\n"); + fprintf(stderr, "\t-toc retrieve and print TOC/PMA data\n"); + fprintf(stderr, "\t-atip retrieve and print ATIP data\n"); + fprintf(stderr, "\t-multi generate a TOC that allows multi session\n"); + fprintf(stderr, "\t In this case default track type is CD-ROM XA mode 2 form 1 - 2048 bytes\n"); + fprintf(stderr, "\t-fix fixate a corrupt or unfixated disk (generate a TOC)\n"); + fprintf(stderr, "\t-nofix do not fixate disk after writing tracks\n"); + fprintf(stderr, "\t-waiti wait until input is available before opening SCSI\n"); + fprintf(stderr, "\t-immed Try to use the SCSI IMMED flag with certain long lasting commands\n"); + fprintf(stderr, "\t-force force to continue on some errors to allow blanking bad disks\n"); + fprintf(stderr, "\t-tao Write disk in TAO mode.\n"); + fprintf(stderr, "\t-dao Write disk in SAO mode.\n"); + fprintf(stderr, "\t-sao Write disk in SAO mode.\n"); + fprintf(stderr, "\t-raw Write disk in RAW mode.\n"); + fprintf(stderr, "\t-raw96r Write disk in RAW/RAW96R mode.\n"); + fprintf(stderr, "\t-raw96p Write disk in RAW/RAW96P mode.\n"); + fprintf(stderr, "\t-raw16 Write disk in RAW/RAW16 mode.\n"); +#ifdef CLONE_WRITE + fprintf(stderr, "\t-clone Write disk in clone write mode.\n"); +#endif + fprintf(stderr, "\ttsize=# Length of valid data in next track\n"); + fprintf(stderr, "\tpadsize=# Amount of padding for next track\n"); + fprintf(stderr, "\tpregap=# Amount of pre-gap sectors before next track\n"); + fprintf(stderr, "\tdefpregap=# Amount of pre-gap sectors for all but track #1\n"); + fprintf(stderr, "\tmcn=text Set the media catalog number for this CD to 'text'\n"); + fprintf(stderr, "\tisrc=text Set the ISRC number for the next track to 'text'\n"); + fprintf(stderr, "\tindex=list Set the index list for the next track to 'list'\n"); + fprintf(stderr, "\t-text Write CD-Text from information from *.inf or *.cue files\n"); + fprintf(stderr, "\ttextfile=name Set the file with CD-Text data to 'name'\n"); + fprintf(stderr, "\tcuefile=name Set the file with CDRWIN CUE data to 'name'\n"); + + fprintf(stderr, "\t-audio Subsequent tracks are CD-DA audio tracks\n"); + fprintf(stderr, "\t-data Subsequent tracks are CD-ROM data mode 1 - 2048 bytes (default)\n"); + fprintf(stderr, "\t-mode2 Subsequent tracks are CD-ROM data mode 2 - 2336 bytes\n"); + fprintf(stderr, "\t-xa Subsequent tracks are CD-ROM XA mode 2 form 1 - 2048 bytes\n"); + fprintf(stderr, "\t-xa1 Subsequent tracks are CD-ROM XA mode 2 form 1 - 2056 bytes\n"); + fprintf(stderr, "\t-xa2 Subsequent tracks are CD-ROM XA mode 2 form 2 - 2324 bytes\n"); + fprintf(stderr, "\t-xamix Subsequent tracks are CD-ROM XA mode 2 form 1/2 - 2332 bytes\n"); + fprintf(stderr, "\t-cdi Subsequent tracks are CDI tracks\n"); + fprintf(stderr, "\t-isosize Use iso9660 file system size for next data track\n"); + fprintf(stderr, "\t-preemp Audio tracks are mastered with 50/15 microseconds preemphasis\n"); + fprintf(stderr, "\t-nopreemp Audio tracks are mastered with no preemphasis (default)\n"); + fprintf(stderr, "\t-copy Audio tracks have unlimited copy permission\n"); + fprintf(stderr, "\t-nocopy Audio tracks may only be copied once for personal use (default)\n"); + fprintf(stderr, "\t-scms Audio tracks will not have any copy permission at all\n"); + fprintf(stderr, "\t-pad Pad data tracks with %d zeroed sectors\n", PAD_SECS); + fprintf(stderr, "\t Pad audio tracks to a multiple of %d bytes\n", AUDIO_SEC_SIZE); + fprintf(stderr, "\t-nopad Do not pad data tracks (default)\n"); + fprintf(stderr, "\t-shorttrack Subsequent tracks may be non Red Book < 4 seconds if in SAO or RAW mode\n"); + fprintf(stderr, "\t-noshorttrack Subsequent tracks must be >= 4 seconds\n"); + fprintf(stderr, "\t-swab Audio data source is byte-swapped (little-endian/Intel)\n"); + fprintf(stderr, "The type of the first track is used for the toc type.\n"); + fprintf(stderr, "Currently only form 1 tracks are supported.\n"); + exit(excode); +} + +static void +blusage(int ret) +{ + fprintf(stderr, "Blanking options:\n"); + fprintf(stderr, "\tall\t\tblank the entire disk\n"); + fprintf(stderr, "\tdisc\t\tblank the entire disk\n"); + fprintf(stderr, "\tdisk\t\tblank the entire disk\n"); + fprintf(stderr, "\tfast\t\tminimally blank the entire disk (PMA, TOC, pregap)\n"); + fprintf(stderr, "\tminimal\t\tminimally blank the entire disk (PMA, TOC, pregap)\n"); + fprintf(stderr, "\ttrack\t\tblank a track\n"); + fprintf(stderr, "\tunreserve\tunreserve a track\n"); + fprintf(stderr, "\ttrtail\t\tblank a track tail\n"); + fprintf(stderr, "\tunclose\t\tunclose last session\n"); + fprintf(stderr, "\tsession\t\tblank last session\n"); + + exit(ret); + /* NOTREACHED */ +} + +static void +formattypeusage(int ret) +{ + fprintf(stderr, "Formating options:\n"); + fprintf(stderr, "\tfull\t\tstandard formating\n"); + fprintf(stderr, "\tbackground\t\tbackground formating\n"); + fprintf(stderr, "\tforce\t\tforce reformat\n"); + + exit(ret); + /* NOTREACHED */ +} + +/* ARGSUSED */ +static void +intr(int sig) +{ + sig = 0; /* Fake usage for gcc */ + + signal(SIGINT, intr); + + didintr++; +} + +static void +catchsig(int sig) +{ + signal(sig, catchsig); +} + +static int +scsi_cb(void *arg) +{ + comexit(EX_BAD); + /* NOTREACHED */ + return (0); /* Keep lint happy */ +} + +static void +intfifo(int sig) +{ + errmsgno(EX_BAD, "Caught interrupt.\n"); + if (exargs.usalp) { + SCSI *usalp = exargs.usalp; + + if (usalp->running) { + if (usalp->cb_fun != NULL) { + comerrno(EX_BAD, "Second interrupt. Doing hard abort.\n"); + /* NOTREACHED */ + } + usalp->cb_fun = scsi_cb; + usalp->cb_arg = &exargs; + return; + } + } + comexit(sig); +} + +/* ARGSUSED */ +static void +exscsi(int excode, void *arg) +{ + struct exargs *exp = (struct exargs *)arg; + + /* + * Try to restore the old sector size. + */ + if (exp != NULL && exp->exflags == 0) { + if (exp->usalp->running) { + return; + } + /* + * flush cache is not supported by CD-ROMs avoid prob with -toc + */ + exp->usalp->silent++; + scsi_flush_cache(exp->usalp, FALSE); + (*exp->dp->cdr_abort_session)(exp->usalp, exp->dp); + exp->usalp->silent--; + set_secsize(exp->usalp, exp->old_secsize); + unload_media(exp->usalp, exp->dp, exp->flags); + + exp->exflags++; /* Make sure that it only get called once */ + } +} + +static void +excdr(int excode, void *arg) +{ + struct exargs *exp = (struct exargs *)arg; + + exscsi(excode, arg); + + cdrstats(exp->dp); + if ((exp->dp->cdr_dstat->ds_cdrflags & RF_DID_CDRSTAT) == 0) { + exp->dp->cdr_dstat->ds_cdrflags |= RF_DID_CDRSTAT; + (*exp->dp->cdr_stats)(exp->usalp, exp->dp); + } + +#ifdef FIFO + kill_faio(); + wait_faio(); + if (debug || lverbose) + fifo_stats(); +#endif +} + +int +read_buf(int f, char *bp, int size) +{ + char *p = bp; + int amount = 0; + int n; + + do { + do { + n = read(f, p, size-amount); + } while (n < 0 && (geterrno() == EAGAIN || geterrno() == EINTR)); + if (n < 0) + return (n); + amount += n; + p += n; + + } while (amount < size && n > 0); + return (amount); +} + +int +fill_buf(int f, track_t *trackp, long secno, char *bp, int size) +{ + int amount = 0; + int nsecs; + int rsize; + int rmod; + int readoffset = 0; + + nsecs = size / trackp->secsize; + if (nsecs < trackp->secspt) { + /* + * Clear buffer to prepare for last transfer. + * Make sure that a partial sector ends with NULs + */ + fillbytes(bp, trackp->secspt * trackp->secsize, '\0'); + } + + if (!is_raw(trackp)) { + amount = read_buf(f, bp, size); + if (amount != size) { + if (amount < 0) + return (amount); + /* + * We got less than expected, clear rest of buf. + */ + fillbytes(&bp[amount], size-amount, '\0'); + } + if (is_swab(trackp)) + swabbytes(bp, amount); + return (amount); + } + + rsize = nsecs * trackp->isecsize; + rmod = size % trackp->secsize; + if (rmod > 0) { + rsize += rmod; + nsecs++; + } + + readoffset = trackp->dataoff; + amount = read_buf(f, bp + readoffset, rsize); + if (is_swab(trackp)) + swabbytes(bp + readoffset, amount); + + if (trackp->isecsize == 2448 && trackp->secsize == 2368) + subrecodesecs(trackp, (Uchar *)bp, secno, nsecs); + + scatter_secs(trackp, bp + readoffset, nsecs); + + if (amount != rsize) { + if (amount < 0) + return (amount); + /* + * We got less than expected, clear rest of buf. + */ + fillbytes(&bp[amount], rsize-amount, '\0'); + nsecs = amount / trackp->isecsize; + rmod = amount % trackp->isecsize; + amount = nsecs * trackp->secsize; + if (rmod > 0) { + nsecs++; + amount += rmod; + } + } else { + amount = size; + } + if ((trackp->sectype & ST_MODE_RAW) == 0) { + encsectors(trackp, (Uchar *)bp, secno, nsecs); + fillsubch(trackp, (Uchar *)bp, secno, nsecs); + } else { + scrsectors(trackp, (Uchar *)bp, secno, nsecs); + } + return (amount); +} + +int +get_buf(int f, track_t *trackp, long secno, char **bpp, int size) +{ + if (fs > 0) { +/* return (faio_read_buf(f, *bpp, size));*/ + return (faio_get_buf(f, bpp, size)); + } else { + return (fill_buf(f, trackp, secno, *bpp, size)); + } +} + +int +write_secs(SCSI *usalp, cdr_t *dp, char *bp, long startsec, int bytespt, + int secspt, BOOL islast) +{ + int amount; + +again: + usalp->silent++; + amount = (*dp->cdr_write_trackdata)(usalp, bp, startsec, bytespt, secspt, islast); + usalp->silent--; + if (amount < 0) { + if (scsi_in_progress(usalp)) { + /* + * If we sleep too long, the drive buffer is empty + * before we start filling it again. The max. CD speed + * is ~ 10 MB/s (52x RAW writing). The max. DVD speed + * is ~ 25 MB/s (18x DVD 1385 kB/s). + * With 10 MB/s, a 1 MB buffer empties within 100ms. + * With 25 MB/s, a 1 MB buffer empties within 40ms. + */ + if ((dp->cdr_dstat->ds_flags & DSF_DVD) == 0) { + usleep(60000); + } else { +#ifndef _SC_CLK_TCK + usleep(20000); +#else + if (sysconf(_SC_CLK_TCK) < 100) + usleep(20000); + else + usleep(10000); + +#endif + } + goto again; + } + return (-1); + } + return (amount); +} + +static int +write_track_data(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + int track = trackp->trackno; + int f = -1; + int isaudio; + long startsec; + Llong bytes_read = 0; + Llong bytes = 0; + Llong savbytes = 0; + int count; + Llong tracksize; + int secsize; + int secspt; + int bytespt; + int bytes_to_read; + long amount; + int pad; + BOOL neednl = FALSE; + BOOL islast = FALSE; + char *bp = buf; + struct timeval tlast; + struct timeval tcur; + float secsps = 75.0; +long bsize; +long bfree; +#define BCAP +#ifdef BCAP +int per = 0; +#ifdef XBCAP +int oper = -1; +#endif +#endif + + if (dp->cdr_dstat->ds_flags & DSF_DVD) + secsps = 676.27; + + usalp->silent++; + if ((*dp->cdr_buffer_cap)(usalp, &bsize, &bfree) < 0) + bsize = -1L; + if (bsize == 0) /* If we have no (known) buffer, we cannot */ + bsize = -1L; /* retrieve the buffer fill ratio */ + usalp->silent--; + + + if (is_packet(trackp)) /* XXX Ugly hack for now */ + return (write_packet_data(usalp, dp, trackp)); + + if (trackp->xfp != NULL) + f = xfileno(trackp->xfp); + + isaudio = is_audio(trackp); + tracksize = trackp->tracksize; + startsec = trackp->trackstart; + + secsize = trackp->secsize; + secspt = trackp->secspt; + bytespt = secsize * secspt; + + pad = !isaudio && is_pad(trackp); /* Pad only data tracks */ + + if (debug) { + printf("secsize:%d secspt:%d bytespt:%d audio:%d pad:%d\n", + secsize, secspt, bytespt, isaudio, pad); + } + + if (lverbose) { + if (tracksize > 0) + printf("\rTrack %02d: 0 of %4lld MB written.", + track, tracksize >> 20); + else + printf("\rTrack %02d: 0 MB written.", track); + flush(); + neednl = TRUE; + } + + gettimeofday(&tlast, (struct timezone *)0); + do { + bytes_to_read = bytespt; + if (tracksize > 0) { + if ((tracksize - bytes_read) > bytespt) + bytes_to_read = bytespt; + else + bytes_to_read = tracksize - bytes_read; + } + count = get_buf(f, trackp, startsec, &bp, bytes_to_read); + + if (count < 0) + comerr("read error on input file\n"); + if (count == 0) + break; + bytes_read += count; + if (tracksize >= 0 && bytes_read >= tracksize) { + count -= bytes_read - tracksize; + /* + * Paranoia: tracksize is known (trackp->tracksize >= 0) + * At this point, trackp->padsize should alway be set + * if the tracksize is less than 300 sectors. + */ + if (trackp->padsecs == 0 && + (is_shorttrk(trackp) || (bytes_read/secsize) >= 300)) + islast = TRUE; + } + + if (count < bytespt) { + if (debug) { + printf("\nNOTICE: reducing block size for last record.\n"); + neednl = FALSE; + } + + if ((amount = count % secsize) != 0) { + amount = secsize - amount; + count += amount; + printf("\nWARNING: padding up to secsize.\n"); + neednl = FALSE; + } + bytespt = count; + secspt = count / secsize; + /* + * If tracksize is not known (trackp->tracksize < 0) + * we may need to set trackp->padsize + * if the tracksize is less than 300 sectors. + */ + if (trackp->padsecs == 0 && + (is_shorttrk(trackp) || (bytes_read/secsize) >= 300)) + islast = TRUE; + } + + amount = write_secs(usalp, dp, bp, startsec, bytespt, secspt, islast); + if (amount < 0) { + printf("%swrite track data: error after %lld bytes\n", + neednl?"\n":"", bytes); + return (-1); + } + bytes += amount; + startsec += amount / secsize; + + if (lverbose && (bytes >= (savbytes + 0x100000))) { + int fper; + int nsecs = (bytes - savbytes) / secsize; + float fspeed; + + gettimeofday(&tcur, (struct timezone *)0); + printf("\rTrack %02d: %4lld", track, bytes >> 20); + if (tracksize > 0) + printf(" of %4lld MB", tracksize >> 20); + else + printf(" MB"); + printf(" written"); + fper = fifo_percent(TRUE); + if (fper >= 0) + printf(" (fifo %3d%%)", fper); +#ifdef BCAP + if (bsize > 0) { /* buffer size known */ + usalp->silent++; + per = (*dp->cdr_buffer_cap)(usalp, (long *)0, &bfree); + usalp->silent--; + if (per >= 0) { + per = 100*(bsize - bfree) / bsize; + if ((bsize - bfree) <= amount || per <= 5) + dp->cdr_dstat->ds_buflow++; + if (per < (int)dp->cdr_dstat->ds_minbuf && + (startsec*secsize) > bsize) { + dp->cdr_dstat->ds_minbuf = per; + } + printf(" [buf %3d%%]", per); +#ifdef BCAPDBG + printf(" %3ld %3ld", bsize >> 10, bfree >> 10); +#endif + } + } +#endif + + tlast.tv_sec = tcur.tv_sec - tlast.tv_sec; + tlast.tv_usec = tcur.tv_usec - tlast.tv_usec; + while (tlast.tv_usec < 0) { + tlast.tv_usec += 1000000; + tlast.tv_sec -= 1; + } + fspeed = (nsecs / secsps) / + (tlast.tv_sec * 1.0 + tlast.tv_usec * 0.000001); + if (fspeed > 999.0) + fspeed = 999.0; +#ifdef BCAP + if (bsize > 0 && per > dminbuf && + dp->cdr_dstat->ds_cdrflags & RF_WR_WAIT) { + int wsecs = (per-dminbuf)*(bsize/secsize)/100; + int msecs = 0x100000/secsize; + int wt; + int mt; + int s = dp->cdr_dstat->ds_dr_cur_wspeed; + + + if (s <= 0) { + if (dp->cdr_dstat->ds_flags & DSF_DVD) + s = 4; + else + s = 50; + } + if (wsecs > msecs) /* Less that 1 MB */ + wsecs = msecs; + wt = wsecs * 1000 / secsps / fspeed; + mt = (per-dminbuf)*(bsize/secsize)/100 * 1000 / secsps/s; + + if (wt > mt) + wt = mt; + if (wt > 1000) /* Max 1 second */ + wt = 1000; + if (wt < 20) /* Min 20 ms */ + wt = 0; + + if (xdebug) + printf(" |%3d %4dms %5dms|", wsecs, wt, mt); + else + printf(" |%3d %4dms|", wsecs, wt); + if (wt > 0) + usleep(wt*1000); + } +#endif + if (dp->is_dvd) fspeed /= 9; + printf(" %5.1fx", fspeed); + printf("."); + savbytes = (bytes >> 20) << 20; + flush(); + neednl = TRUE; + tlast = tcur; + } +#ifdef XBCAP + if (bsize > 0) { /* buffer size known */ + (*dp->cdr_buffer_cap)(usalp, (long *)0, &bfree); + per = 100*(bsize - bfree) / bsize; + if (per != oper) + printf("[buf %3d%%] %3ld %3ld\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", + per, bsize >> 10, bfree >> 10); + oper = per; + flush(); + } + + +#endif + } while (tracksize < 0 || bytes_read < tracksize); + + if (!is_shorttrk(trackp) && (bytes / secsize) < 300) { + /* + * If tracksize is not known (trackp->tracksize < 0) or + * for some strange reason we did not set padsecs properly + * we may need to modify trackp->padsecs if + * tracksize+padsecs is less than 300 sectors. + */ + if ((trackp->padsecs + (bytes / secsize)) < 300) + trackp->padsecs = 300 - (bytes / secsize); + } + if (trackp->padsecs > 0) { + Llong padbytes; + + /* + * pad_track() is based on secsize. Compute the amount of bytes + * assumed by pad_track(). + */ + padbytes = (Llong)trackp->padsecs * secsize; + + if (neednl) { + printf("\n"); + neednl = FALSE; + } + if ((padbytes >> 20) > 0) { + neednl = TRUE; + } else if (lverbose) { + printf("Track %02d: writing %3lld KB of pad data.\n", + track, (Llong)(padbytes >> 10)); + neednl = FALSE; + } + pad_track(usalp, dp, trackp, startsec, padbytes, + TRUE, &savbytes); + bytes += savbytes; + startsec += savbytes / secsize; + } + printf("%sTrack %02d: Total bytes read/written: %lld/%lld (%lld sectors).\n", + neednl?"\n":"", track, bytes_read, bytes, bytes/secsize); + flush(); + return (0); +} + +int +pad_track(SCSI *usalp, cdr_t *dp, track_t *trackp, long startsec, Llong amt, + BOOL dolast, Llong *bytesp) +{ + int track = trackp->trackno; + Llong bytes = 0; + Llong savbytes = 0; + Llong padsize = amt; + int secsize; + int secspt; + int bytespt; + int amount; + BOOL neednl = FALSE; + BOOL islast = FALSE; + struct timeval tlast; + struct timeval tcur; + float secsps = 75.0; +long bsize; +long bfree; +#define BCAP +#ifdef BCAP +int per; +#ifdef XBCAP +int oper = -1; +#endif +#endif + + if (dp->cdr_dstat->ds_flags & DSF_DVD) + secsps = 676.27; + + usalp->silent++; + if ((*dp->cdr_buffer_cap)(usalp, &bsize, &bfree) < 0) + bsize = -1L; + if (bsize == 0) /* If we have no (known) buffer, we cannot */ + bsize = -1L; /* retrieve the buffer fill ratio */ + usalp->silent--; + + secsize = trackp->secsize; + secspt = trackp->secspt; + bytespt = secsize * secspt; + + fillbytes(buf, bytespt, '\0'); + + if ((amt >> 20) > 0) { + printf("\rTrack %02d: 0 of %4lld MB pad written.", + track, amt >> 20); + flush(); + } + gettimeofday(&tlast, (struct timezone *)0); + do { + if (amt < bytespt) { + bytespt = roundup(amt, secsize); + secspt = bytespt / secsize; + } + if (dolast && (amt - bytespt) <= 0) + islast = TRUE; + + if (is_raw(trackp)) { + encsectors(trackp, (Uchar *)buf, startsec, secspt); + fillsubch(trackp, (Uchar *)buf, startsec, secspt); + } + + amount = write_secs(usalp, dp, buf, startsec, bytespt, secspt, islast); + if (amount < 0) { + printf("%swrite track pad data: error after %lld bytes\n", + neednl?"\n":"", bytes); + if (bytesp) + *bytesp = bytes; +(*dp->cdr_buffer_cap)(usalp, (long *)0, (long *)0); + return (-1); + } + amt -= amount; + bytes += amount; + startsec += amount / secsize; + + if (lverbose && (bytes >= (savbytes + 0x100000))) { + int nsecs = (bytes - savbytes) / secsize; + float fspeed; + + gettimeofday(&tcur, (struct timezone *)0); + printf("\rTrack %02d: %4lld", track, bytes >> 20); + if (padsize > 0) + printf(" of %4lld MB", padsize >> 20); + else + printf(" MB"); + printf(" pad written"); + savbytes = (bytes >> 20) << 20; + +#ifdef BCAP + if (bsize > 0) { /* buffer size known */ + usalp->silent++; + per = (*dp->cdr_buffer_cap)(usalp, (long *)0, &bfree); + usalp->silent--; + if (per >= 0) { + per = 100*(bsize - bfree) / bsize; + if ((bsize - bfree) <= amount || per <= 5) + dp->cdr_dstat->ds_buflow++; + if (per < (int)dp->cdr_dstat->ds_minbuf && + (startsec*secsize) > bsize) { + dp->cdr_dstat->ds_minbuf = per; + } + printf(" [buf %3d%%]", per); +#ifdef BCAPDBG + printf(" %3ld %3ld", bsize >> 10, bfree >> 10); +#endif + } + } +#endif + tlast.tv_sec = tcur.tv_sec - tlast.tv_sec; + tlast.tv_usec = tcur.tv_usec - tlast.tv_usec; + while (tlast.tv_usec < 0) { + tlast.tv_usec += 1000000; + tlast.tv_sec -= 1; + } + fspeed = (nsecs / secsps) / + (tlast.tv_sec * 1.0 + tlast.tv_usec * 0.000001); + if (fspeed > 999.0) + fspeed = 999.0; + printf(" %5.1fx", fspeed); + printf("."); + flush(); + neednl = TRUE; + tlast = tcur; + } + } while (amt > 0); + + if (bytesp) + *bytesp = bytes; + if (bytes == 0) + return (0); + return (bytes > 0 ? 1:-1); +} + +#ifdef USE_WRITE_BUF +int +write_buf(SCSI *usalp, cdr_t *dp, track_t *trackp, char *bp, long startsec, + Llong amt, int secsize, BOOL dolast, Llong *bytesp) +{ + int track = trackp->trackno; + Llong bytes = 0; + Llong savbytes = 0; +/* int secsize;*/ + int secspt; + int bytespt; + int amount; + BOOL neednl = FALSE; + BOOL islast = FALSE; + +/* secsize = trackp->secsize;*/ +/* secspt = trackp->secspt;*/ + + secspt = bufsize/secsize; + secspt = min(255, secspt); + bytespt = secsize * secspt; + +/* fillbytes(buf, bytespt, '\0');*/ + + if ((amt >> 20) > 0) { + printf("\rTrack %02d: 0 of %4ld MB pad written.", + track, amt >> 20); + flush(); + } + do { + if (amt < bytespt) { + bytespt = roundup(amt, secsize); + secspt = bytespt / secsize; + } + if (dolast && (amt - bytespt) <= 0) + islast = TRUE; + + amount = write_secs(usalp, dp, bp, startsec, bytespt, secspt, islast); + if (amount < 0) { + printf("%swrite track data: error after %ld bytes\n", + neednl?"\n":"", bytes); + if (bytesp) + *bytesp = bytes; +(*dp->cdr_buffer_cap)(usalp, (long *)0, (long *)0); + return (-1); + } + amt -= amount; + bytes += amount; + startsec += amount / secsize; + + if (lverbose && (bytes >= (savbytes + 0x100000))) { + printf("\rTrack %02d: %3ld", track, bytes >> 20); + savbytes = (bytes >> 20) << 20; + flush(); + neednl = TRUE; + } + } while (amt > 0); + + if (bytesp) + *bytesp = bytes; + return (bytes); +} +#endif /* USE_WRITE_BUF */ + +static void +printdata(int track, track_t *trackp) +{ + if (trackp->itracksize >= 0) { + printf("Track %02d: data %4lld MB ", + track, (Llong)(trackp->itracksize >> 20)); + } else { + printf("Track %02d: data unknown length", + track); + } + if (trackp->padsecs > 0) { + Llong padbytes = (Llong)trackp->padsecs * trackp->isecsize; + + if ((padbytes >> 20) > 0) + printf(" padsize: %4lld MB", (Llong)(padbytes >> 20)); + else + printf(" padsize: %4lld KB", (Llong)(padbytes >> 10)); + } + if (trackp->pregapsize != (trackp->flags & TI_DVD)? 0 : 150) { + printf(" pregapsize: %3ld", trackp->pregapsize); + } + if (xdebug) + printf(" START: %ld SECTORS: %ld INDEX0 %ld", + trackp->trackstart, trackp->tracksecs, trackp->index0start); + printf("\n"); +} + +static void +printaudio(int track, track_t *trackp) +{ + if (trackp->itracksize >= 0) { + printf("Track %02d: audio %4lld MB (%02d:%02d.%02d) %spreemp%s%s", + track, (Llong)(trackp->itracksize >> 20), + minutes(trackp->itracksize), + seconds(trackp->itracksize), + hseconds(trackp->itracksize), + is_preemp(trackp) ? "" : "no ", + is_swab(trackp) ? " swab":"", + ((trackp->itracksize < 300L*trackp->isecsize) || + (trackp->itracksize % trackp->isecsize)) && + is_pad(trackp) ? " pad" : ""); + } else { + printf("Track %02d: audio unknown length %spreemp%s%s", + track, is_preemp(trackp) ? "" : "no ", + is_swab(trackp) ? " swab":"", + (trackp->itracksize % trackp->isecsize) && is_pad(trackp) ? " pad" : ""); + } + if (is_scms(trackp)) + printf(" scms"); + else if (is_copy(trackp)) + printf(" copy"); + else + printf(" "); + + if (trackp->padsecs > 0) { + Llong padbytes = (Llong)trackp->padsecs * trackp->isecsize; + + if ((padbytes >> 20) > 0) + printf(" padsize: %4lld MB", (Llong)(padbytes >> 20)); + else + printf(" padsize: %4lld KB", (Llong)(padbytes >> 10)); + printf(" (%02d:%02d.%02d)", + Sminutes(trackp->padsecs), + Sseconds(trackp->padsecs), + Shseconds(trackp->padsecs)); + } + if (trackp->pregapsize != ((trackp->flags & TI_DVD)? 0 : 150) || xdebug > 0) { + printf(" pregapsize: %3ld", trackp->pregapsize); + } + if (xdebug) + printf(" START: %ld SECTORS: %ld INDEX0 %ld", + trackp->trackstart, trackp->tracksecs, trackp->index0start); + printf("\n"); +} + +static void +checkfile(int track, track_t *trackp) +{ + if (trackp->itracksize > 0 && + is_audio(trackp) && + ((!is_shorttrk(trackp) && + (trackp->itracksize < 300L*trackp->isecsize)) || + (trackp->itracksize % trackp->isecsize)) && + !is_pad(trackp)) { + errmsgno(EX_BAD, "Bad audio track size %lld for track %02d.\n", + (Llong)trackp->itracksize, track); + errmsgno(EX_BAD, "Audio tracks must be at least %ld bytes and a multiple of %d.\n", + 300L*trackp->isecsize, trackp->isecsize); + + if (!is_shorttrk(trackp) && (trackp->itracksize < 300L*trackp->isecsize)) + comerrno(EX_BAD, "See -shorttrack option.\n"); + if (!is_pad(trackp) && (trackp->itracksize % trackp->isecsize)) + comerrno(EX_BAD, "See -pad option.\n"); + } + + if (lverbose == 0 && xdebug == 0) + return; + + if (is_audio(trackp)) + printaudio(track, trackp); + else + printdata(track, trackp); +} + +static int +checkfiles(int tracks, track_t *trackp) +{ + int i; + int isaudio = 1; + int starttrack = 1; + int endtrack = tracks; + + if (xdebug) { + /* + * Include Lead-in & Lead-out. + */ + starttrack--; + endtrack++; + } + for (i = starttrack; i <= endtrack; i++) { + if (!is_audio(&trackp[i])) + isaudio = 0; + if (xdebug) + printf("SECTYPE %X ", trackp[i].sectype); + checkfile(i, &trackp[i]); + } + return (isaudio); +} + +static void +setleadinout(int tracks, track_t *trackp) +{ + /* + * Set some values for track 0 (the lead-in) + */ + if (!is_clone(&trackp[0])) { + trackp[0].sectype = trackp[1].sectype; + trackp[0].dbtype = trackp[1].dbtype; + trackp[0].dataoff = trackp[1].dataoff; + + /* + * XXX Which other flags should be copied to Track 0 ? + */ + if (is_audio(&trackp[1])) + trackp[0].flags |= TI_AUDIO; + } + + /* + * Set some values for track 0xAA (the lead-out) + */ + trackp[tracks+1].pregapsize = 0; + trackp[tracks+1].isecsize = trackp[tracks].isecsize; + trackp[tracks+1].secsize = trackp[tracks].secsize; + + if (!is_clone(&trackp[0])) { + trackp[tracks+1].tracktype = trackp[tracks].tracktype; + trackp[tracks+1].sectype = trackp[tracks].sectype; + trackp[tracks+1].dbtype = trackp[tracks].dbtype; + trackp[tracks+1].dataoff = trackp[tracks].dataoff; + } + + trackp[tracks+1].flags = trackp[tracks].flags; +} + +static void +setpregaps(int tracks, track_t *trackp) +{ + int i; + int sectype; + long pregapsize; + track_t *tp; + + sectype = trackp[1].sectype; + sectype &= ST_MASK; + + for (i = 1; i <= tracks; i++) { + tp = &trackp[i]; + if (tp->pregapsize == -1L) { + tp->pregapsize = 150; /* Default CD Pre GAP*/ + if (trackp->flags & TI_DVD) { + tp->pregapsize = 0; + } else if (sectype != (tp->sectype & ST_MASK)) { + tp->pregapsize = 255; /* Pre GAP is 255 */ + tp->flags &= ~TI_PREGAP; + } + } + sectype = tp->sectype & ST_MASK; /* Save old sectype */ + } + trackp[tracks+1].pregapsize = 0; + trackp[tracks+1].index0start = 0; + + for (i = 1; i <= tracks; i++) { + /* + * index0start is set below tracksecks if this track contains + * the pregap (index 0) of the next track. + */ + trackp[i].index0start = trackp[i].tracksecs; + + pregapsize = trackp[i+1].pregapsize; + if (is_pregap(&trackp[i+1]) && pregapsize > 0) + trackp[i].index0start -= pregapsize; + } +} + +/* + * Check total size of the medium + */ +static long +checktsize(int tracks, track_t *trackp) +{ + int i; + Llong curr; + Llong total = -150; /* CD track #1 pregap compensation */ + Ullong btotal; + track_t *tp; + + if (trackp->flags & TI_DVD) + total = 0; + for (i = 1; i <= tracks; i++) { + tp = &trackp[i]; + if (!is_pregap(tp)) + total += tp->pregapsize; + + if (lverbose > 1) { + printf("track: %d start: %lld pregap: %ld\n", + i, total, tp->pregapsize); + } + tp->trackstart = total; + if (tp->itracksize >= 0) { + curr = (tp->itracksize + (tp->isecsize-1)) / tp->isecsize; + curr += tp->padsecs; + /* + * Minimum track size is 4s + */ + if (!is_shorttrk(tp) && curr < 300) + curr = 300; + if ((trackp->flags & TI_DVD) == 0) { + /* + * XXX Was passiert hier bei is_packet() ??? + */ + if (is_tao(tp) && !is_audio(tp)) { + curr += 2; + } + } + total += curr; + } else if (is_sao(tp) || is_raw(tp)) { + errmsgno(EX_BAD, "Track %d has unknown length.\n", i); + comerrno(EX_BAD, + "Use tsize= option in %s mode to specify track size.\n", + is_sao(tp) ? "SAO" : "RAW"); + } + } + tp = &trackp[i]; + tp->trackstart = total; + tp->tracksecs = 6750; /* Size of first session Lead-Out */ + if (!lverbose) + return (total); + + if (trackp->flags & TI_DVD) + btotal = (Ullong)total * 2048; + else + btotal = (Ullong)total * 2352; +/* XXX CD Sector Size ??? */ + if (tracks > 0) { + if (trackp->flags & TI_DVD) { + printf("Total size: %4llu MB = %lld sectors\n", + btotal >> 20, total); + } else { + printf("Total size: %4llu MB (%02d:%02d.%02d) = %lld sectors\n", + btotal >> 20, + minutes(btotal), + seconds(btotal), + hseconds(btotal), total); + btotal += 150 * 2352; + printf("Lout start: %4llu MB (%02d:%02d/%02d) = %lld sectors\n", + btotal >> 20, + minutes(btotal), + seconds(btotal), + frames(btotal), total); + } + } + return (total); +} + +static void +opentracks(track_t *trackp) +{ + track_t *tp; + int i; + int tracks = trackp[0].tracks; + + Llong tracksize; + int secsize; + + for (i = 1; i <= tracks; i++) { + tp = &trackp[i]; + + if (auinfosize(tp->filename, tp)) { + /* + * open stdin + */ + tp->xfp = xopen(NULL, O_RDONLY|O_BINARY, 0); + } else if (strcmp("-", tp->filename) == 0) { + /* + * open stdin + */ + tp->xfp = xopen(NULL, O_RDONLY|O_BINARY, 0); + } else { + if ((tp->xfp = xopen(tp->filename, + O_RDONLY|O_BINARY, 0)) == NULL) { + comerr("Cannot open '%s'.\n", tp->filename); + } + } + + checksize(tp); + tracksize = tp->itracksize; + secsize = tp->isecsize; + if (!is_shorttrk(tp) && + tracksize > 0 && (tracksize / secsize) < 300) { + + tracksize = roundup(tracksize, secsize); + if ((tp->padsecs + + (tracksize / secsize)) < 300) { + tp->padsecs = + 300 - tracksize / secsize; + } + if (xdebug) { + printf("TRACK %d SECTORS: %ld", + i, tp->tracksecs); + printf(" pasdize %lld (%ld sectors)\n", + (Llong)tp->padsecs * secsize, + tp->padsecs); + } + } +#ifdef AUINFO + if (tp->flags & TI_USEINFO) { + auinfo(tp->filename, i, trackp); + if (lverbose > 0 && i == 1) + printf("pregap1: %ld\n", trackp[1].pregapsize); + } +#endif + /* + * tracksecks is total numbers of sectors in track (starting from + * index 0). + */ + if (tp->padsecs > 0) + tp->tracksecs += tp->padsecs; + + if (debug) { + printf("File: '%s' itracksize: %lld isecsize: %d tracktype: %d = %s sectype: %X = %s dbtype: %s flags %X\n", + tp->filename, (Llong)tp->itracksize, + tp->isecsize, + tp->tracktype & TOC_MASK, toc2name[tp->tracktype & TOC_MASK], + tp->sectype, st2name[tp->sectype & ST_MASK], db2name[tp->dbtype], tp->flags); + } + } +} + +static void +checksize(track_t *trackp) +{ + struct stat st; + Llong lsize; + int f = -1; + + if (trackp->xfp != NULL) + f = xfileno(trackp->xfp); + + /* + * If the current input file is a regular file and + * 'padsize=' has not been specified, + * use fstat() or file parser to get the size of the file. + */ + if (trackp->itracksize < 0 && (trackp->flags & TI_ISOSIZE) != 0) { + lsize = isosize(f); + trackp->itracksize = lsize; + if (trackp->itracksize != lsize) + comerrno(EX_BAD, "This OS cannot handle large ISO-9660 images.\n"); + } + if (trackp->itracksize < 0 && (trackp->flags & TI_NOAUHDR) == 0) { + lsize = ausize(f); + trackp->itracksize = lsize; + if (trackp->itracksize != lsize) + comerrno(EX_BAD, "This OS cannot handle large audio images.\n"); + } + if (trackp->itracksize < 0 && (trackp->flags & TI_NOAUHDR) == 0) { + lsize = wavsize(f); + trackp->itracksize = lsize; + if (trackp->itracksize != lsize) + comerrno(EX_BAD, "This OS cannot handle large WAV images.\n"); + if (trackp->itracksize > 0) /* Force little endian input */ + trackp->flags |= TI_SWAB; + } + if (trackp->itracksize == AU_BAD_CODING) { + comerrno(EX_BAD, "Inappropriate audio coding in '%s'.\n", + trackp->filename); + } + if (trackp->itracksize < 0 && + fstat(f, &st) >= 0 && S_ISREG(st.st_mode)) { + trackp->itracksize = st.st_size; + } + if (trackp->itracksize >= 0) { + /* + * We do not allow cdrecord to start if itracksize is not + * a multiple of isecsize or we are allowed to pad to secsize via -pad. + * For this reason, we may safely always assume padding. + */ + trackp->tracksecs = (trackp->itracksize + trackp->isecsize -1) / trackp->isecsize; + trackp->tracksize = (trackp->itracksize / trackp->isecsize) * trackp->secsize + + trackp->itracksize % trackp->isecsize; + } else { + trackp->tracksecs = -1L; + } +} + +static BOOL +checkdsize(SCSI *usalp, cdr_t *dp, long tsize, int flags) +{ + long startsec = 0L; + long endsec = 0L; + dstat_t *dsp = dp->cdr_dstat; + int profile; + + usalp->silent++; + (*dp->cdr_next_wr_address)(usalp, (track_t *)0, &startsec); + usalp->silent--; + + /* + * This only should happen when the drive is currently in SAO mode. + * We rely on the drive being in TAO mode, a negative value for + * startsec is not correct here it may be caused by bad firmware or + * by a drive in SAO mode. In SAO mode the drive will report the + * pre-gap as part of the writable area. + */ + if (startsec < 0) + startsec = 0; + + /* + * Size limitations (sectors) for CD's: + * + * 404850 == 90 min Red book calls this the + * first negative time + * allows lead out start up to + * block 404700 + * + * 449850 == 100 min This is the first time that + * is no more representable + * in a two digit BCD number. + * allows lead out start up to + * block 449700 + * + * ~540000 == 120 min The largest CD ever made. + * + * ~650000 == 1.3 GB a Double Density (DD) CD. + */ + + endsec = startsec + tsize; + dsp->ds_startsec = startsec; + dsp->ds_endsec = endsec; + + + if (dsp->ds_maxblocks > 0) { + /* + * dsp->ds_maxblocks > 0 (disk capacity is known). + */ + if (lverbose) + printf("Blocks total: %ld Blocks current: %ld Blocks remaining: %ld\n", + (long)dsp->ds_maxblocks, + (long)dsp->ds_maxblocks - startsec, + (long)dsp->ds_maxblocks - endsec); + + if (endsec > dsp->ds_maxblocks) { + if (dsp->ds_flags & DSF_DVD) { /* A DVD and not a CD */ + /* + * There is no overburning on DVD... + */ + errmsgno(EX_BAD, + "Data does not fit on current disk.\n"); + goto toolarge; + } + errmsgno(EX_BAD, + "WARNING: Data may not fit on current disk.\n"); + + /* XXX Check for flags & CDR_NO_LOLIMIT */ +/* goto toolarge;*/ + } + if (lverbose && dsp->ds_maxrblocks > 0) + printf("RBlocks total: %ld RBlocks current: %ld RBlocks remaining: %ld\n", + (long)dsp->ds_maxrblocks, + (long)dsp->ds_maxrblocks - startsec, + (long)dsp->ds_maxrblocks - endsec); + if (dsp->ds_maxrblocks > 0 && endsec > dsp->ds_maxrblocks) { + errmsgno(EX_BAD, + "Data does not fit on current disk.\n"); + goto toolarge; + } + if ((endsec > dsp->ds_maxblocks && endsec > 404700) || + (dsp->ds_maxrblocks > 404700 && 449850 > dsp->ds_maxrblocks)) { + /* + * Assume that this must be a CD and not a DVD. + * So this is a non Red Book compliant CD with a + * capacity between 90 and 99 minutes. + */ + if (dsp->ds_maxrblocks > 404700) + printf("RedBook total: %ld RedBook current: %ld RedBook remaining: %ld\n", + 404700L, + 404700L - startsec, + 404700L - endsec); + if (endsec > dsp->ds_maxblocks && endsec > 404700) { + if ((flags & (F_IGNSIZE|F_FORCE)) == 0) { + errmsgno(EX_BAD, + "Notice: Most recorders cannot write CD's >= 90 minutes.\n"); + errmsgno(EX_BAD, + "Notice: Use -ignsize option to allow >= 90 minutes.\n"); + } + goto toolarge; + } + } + } else { + /* + * dsp->ds_maxblocks == 0 (disk capacity is unknown). + */ + profile = dp->profile; + if (endsec >= (4200000)) { + errmsgno(EX_BAD, + "ERROR: Could not manage to find medium size, and more than 8.0 GB of data.\n"); + goto toolarge; + } else if (profile != 0x2B) { + if (endsec >= (2300000)) { + errmsgno(EX_BAD, + "ERROR: Could not manage to find medium size, and more than 4.3 GB of data for a non dual layer disc.\n"); + goto toolarge; + } else if (endsec >= (405000-300)) { /*<90 min disk or DVD*/ + errmsgno(EX_BAD, + "WARNING: Could not manage to find medium size, and more than 90 mins of data.\n"); + } else if (endsec >= (333000-150)) { /* 74 min disk*/ + errmsgno(EX_BAD, + "WARNING: Data may not fit on standard 74min disk.\n"); + } + } + } + if (dsp->ds_maxblocks <= 0 || endsec <= dsp->ds_maxblocks) + return (TRUE); + /* FALLTHROUGH */ +toolarge: + if (dsp->ds_maxblocks > 0 && endsec > dsp->ds_maxblocks) { + if ((flags & (F_OVERBURN|F_IGNSIZE|F_FORCE)) != 0) { + if (dsp->ds_flags & DSF_DVD) { /* A DVD and not a CD */ + errmsgno(EX_BAD, + "Notice: -overburn is not expected to work with DVD media.\n"); + } + errmsgno(EX_BAD, + "Notice: Overburning active. Trying to write more than the official disk capacity.\n"); + return (TRUE); + } else { + if ((dsp->ds_flags & DSF_DVD) == 0) { /* A CD and not a DVD */ + errmsgno(EX_BAD, + "Notice: Use -overburn option to write more than the official disk capacity.\n"); + errmsgno(EX_BAD, + "Notice: Most CD-writers do overburning only on SAO or RAW mode.\n"); + } + return (FALSE); + } + } + if (dsp->ds_maxblocks < 449850) { + if ((dsp->ds_flags & DSF_DVD) == 0) { /* A CD and not a DVD */ + if (endsec <= dsp->ds_maxblocks) + return (TRUE); + errmsgno(EX_BAD, "Cannot write more than remaining DVD capacity.\n"); + return (FALSE); + } + /* + * Assume that this must be a CD and not a DVD. + */ + if (endsec > 449700) { + errmsgno(EX_BAD, "Cannot write CD's >= 100 minutes.\n"); + return (FALSE); + } + } + if ((flags & (F_IGNSIZE|F_FORCE)) != 0) + return (TRUE); + return (FALSE); +} + +static void +raise_fdlim() +{ +#ifdef RLIMIT_NOFILE + + struct rlimit rlim; + + /* + * Set max # of file descriptors to be able to hold all files open + */ + getrlimit(RLIMIT_NOFILE, &rlim); + if (rlim.rlim_cur >= (MAX_TRACK + 10)) + return; + + rlim.rlim_cur = MAX_TRACK + 10; + if (rlim.rlim_cur > rlim.rlim_max) + errmsgno(EX_BAD, + "Warning: low file descriptor limit (%lld)\n", + (Llong)rlim.rlim_max); + setrlimit(RLIMIT_NOFILE, &rlim); + +#endif /* RLIMIT_NOFILE */ +} + +static void +raise_memlock() +{ +#ifdef RLIMIT_MEMLOCK + struct rlimit rlim; + + rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; + + if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) + errmsg("Warning: Cannot raise RLIMIT_MEMLOCK limits.\n"); +#endif /* RLIMIT_MEMLOCK */ +} + +char *opts = +"help,version,checkdrive,prcap,inq,devices,scanbus,reset,abort,overburn,ignsize,useinfo,dev*,timeout#,driver*,driveropts*,setdropts,tsize&,padsize&,pregap&,defpregap&,speed#,load,lock,eject,dummy,msinfo,toc,atip,multi,fix,nofix,waiti,immed,debug#,d+,kdebug#,kd#,verbose+,v+,Verbose+,V+,x+,xd#,silent,s,audio,data,mode2,xa,xa1,xa2,xamix,cdi,isosize,nopreemp,preemp,nocopy,copy,nopad,pad,swab,fs&,ts&,blank&,format,formattype&,pktsize#,packet,noclose,force,tao,dao,sao,raw,raw96r,raw96p,raw16,clone,scms,isrc*,mcn*,index*,cuefile*,textfile*,text,shorttrack,noshorttrack,gracetime#,minbuf#,msifile*"; + +/* + * Defines used to find whether a write mode has been specified. + */ +#define M_TAO 1 /* Track at Once mode */ +#define M_SAO 2 /* Session at Once mode (also known as DAO) */ +#define M_RAW 4 /* Raw mode */ +#define M_PACKET 8 /* Packed mode */ +static int +gargs(int ac, char **av, int *tracksp, track_t *trackp, char **devp, + int *timeoutp, cdr_t **dpp, int *speedp, long *flagsp, int *blankp, + int *formatp) +{ + int cac; + char * const*cav; + char *driver = NULL; + char *dev = NULL; + char *isrc = NULL; + char *mcn = NULL; + char *tindex = NULL; + char *cuefile = NULL; + char *textfile = NULL; + long bltype = -1; + int doformat = 0; + int formattype = -1; + Llong tracksize; + Llong padsize; + long pregapsize; + long defpregap = -1L; + int pktsize; + int speed = -1; + int help = 0; + int version = 0; + int checkdrive = 0; + int setdropts = 0; + int prcap = 0; + int inq = 0; + int scanbus = 0; + int reset = 0; + int doabort = 0; + int overburn = 0; + int ignsize = 0; + int useinfo = 0; + int load = 0; + int lock = 0; + int eject = 0; + int dummy = 0; + int msinfo = 0; + int toc = 0; + int atip = 0; + int multi = 0; + int fix = 0; + int nofix = 0; + int waiti = 0; + int immed = 0; + int audio; + int autoaudio = 0; + int data; + int mode2; + int xa; + int xa1; + int xa2; + int xamix; + int cdi; + int isize; + int ispacket = 0; + int noclose = 0; + int force = 0; + int tao = 0; + int dao = 0; + int raw = 0; + int raw96r = 0; + int raw96p = 0; + int raw16 = 0; + int clone = 0; + int scms = 0; + int preemp = 0; + int nopreemp; + int copy = 0; + int nocopy; + int pad = 0; + int bswab = 0; + int nopad; + int usetext = 0; + int shorttrack = 0; + int noshorttrack; + int flags; + int tracks = *tracksp; + int tracktype = TOC_ROM; +/* int sectype = ST_ROM_MODE1 | ST_MODE_1;*/ + int sectype = SECT_ROM; + int dbtype = DB_ROM_MODE1; + int secsize = DATA_SEC_SIZE; + int dataoff = 16; + int ga_ret; + int wm = 0; + + trackp[0].flags |= TI_TAO; + trackp[1].pregapsize = -1; + *flagsp |= F_WRITE; + + cac = --ac; + cav = ++av; + for (; ; cac--, cav++) { + tracksize = (Llong)-1L; + padsize = (Llong)0L; + pregapsize = defpregap; + audio = data = mode2 = xa = xa1 = xa2 = xamix = cdi = 0; + isize = nopreemp = nocopy = nopad = noshorttrack = 0; + pktsize = 0; + isrc = NULL; + tindex = NULL; + /* + * Get options up to next file type arg. + */ + if ((ga_ret = getargs(&cac, &cav, opts, + &help, &version, &checkdrive, &prcap, + &inq, &scandevs, &scanbus, &reset, &doabort, &overburn, &ignsize, + &useinfo, + devp, timeoutp, &driver, &driveropts, &setdropts, + getllnum, &tracksize, + getllnum, &padsize, + getnum, &pregapsize, + getnum, &defpregap, + &speed, + &load, &lock, + &eject, &dummy, &msinfo, &toc, &atip, + &multi, &fix, &nofix, &waiti, &immed, + &debug, &debug, + &kdebug, &kdebug, + &lverbose, &lverbose, + &scsi_verbose, &scsi_verbose, + &xdebug, &xdebug, + &silent, &silent, + &audio, &data, &mode2, + &xa, &xa1, &xa2, &xamix, &cdi, + &isize, + &nopreemp, &preemp, + &nocopy, ©, + &nopad, &pad, &bswab, getnum, &fs, getnum, &bufsize, + getbltype, &bltype, &doformat, getformattype, &formattype, &pktsize, + &ispacket, &noclose, &force, + &tao, &dao, &dao, &raw, &raw96r, &raw96p, &raw16, + &clone, + &scms, &isrc, &mcn, &tindex, + &cuefile, &textfile, &usetext, + &shorttrack, &noshorttrack, + &gracetime, &dminbuf, &msifile)) < 0) { + errmsgno(EX_BAD, "Bad Option: %s.\n", cav[0]); + susage(EX_BAD); + } + if (help) + usage(0); + if (tracks == 0) { + if (driver) + set_cdrcmds(driver, dpp); + if (version) + *flagsp |= F_VERSION; + if (checkdrive) + *flagsp |= F_CHECKDRIVE; + if (prcap) + *flagsp |= F_PRCAP; + if (inq) + *flagsp |= F_INQUIRY; + if (scanbus || scandevs) /* scandevs behaves similarly WRT in the legacy code, just the scan operation is different */ + *flagsp |= F_SCANBUS; + if (reset) + *flagsp |= F_RESET; + if (doabort) + *flagsp |= F_ABORT; + if (overburn) + *flagsp |= F_OVERBURN; + if (ignsize) + *flagsp |= F_IGNSIZE; + if (load) + *flagsp |= F_LOAD; + if (lock) + *flagsp |= F_DLCK; + if (eject) + *flagsp |= F_EJECT; + if (dummy) + *flagsp |= F_DUMMY; + if (setdropts) + *flagsp |= F_SETDROPTS; + if(msifile) + msinfo++; + if (msinfo) + *flagsp |= F_MSINFO; + if (toc) { + *flagsp |= F_TOC; + *flagsp &= ~F_WRITE; + } + if (atip) { + *flagsp |= F_PRATIP; + *flagsp &= ~F_WRITE; + } + if (multi) { + /* + * 2048 Bytes user data + */ + *flagsp |= F_MULTI; + tracktype = TOC_XA2; + sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_1; + sectype = SECT_MODE_2_F1; + dbtype = DB_XA_MODE2; /* XXX -multi nimmt DB_XA_MODE2_F1 !!! */ + secsize = DATA_SEC_SIZE; /* 2048 */ + dataoff = 24; + } + if (fix) + *flagsp |= F_FIX; + if (nofix) + *flagsp |= F_NOFIX; + if (waiti) + *flagsp |= F_WAITI; + if (immed) + *flagsp |= F_IMMED; + if (force) + *flagsp |= F_FORCE; + + if (bltype >= 0) { + *flagsp |= F_BLANK; + *blankp = bltype; + } + if (doformat > 0) { + *flagsp |= F_FORMAT; + *formatp |= FULL_FORMAT; + } + if (formattype >= 0) { + *flagsp |= F_FORMAT; + *formatp |= formattype; + } + if (ispacket) + wm |= M_PACKET; + if (tao) + wm |= M_TAO; + if (dao) { + *flagsp |= F_SAO; + trackp[0].flags &= ~TI_TAO; + trackp[0].flags |= TI_SAO; + wm |= M_SAO; + + } else if ((raw == 0) && (raw96r + raw96p + raw16) > 0) + raw = 1; + if ((raw != 0) && (raw96r + raw96p + raw16) == 0) + raw96r = 1; + if (raw96r) { + if (!dao) + *flagsp |= F_RAW; + trackp[0].flags &= ~TI_TAO; + trackp[0].flags |= TI_RAW; + trackp[0].flags |= TI_RAW96R; + wm |= M_RAW; + } + if (raw96p) { + if (!dao) + *flagsp |= F_RAW; + trackp[0].flags &= ~TI_TAO; + trackp[0].flags |= TI_RAW; + wm |= M_RAW; + } + if (raw16) { + if (!dao) + *flagsp |= F_RAW; + trackp[0].flags &= ~TI_TAO; + trackp[0].flags |= TI_RAW; + trackp[0].flags |= TI_RAW16; + wm |= M_RAW; + } + if (mcn) { +#ifdef AUINFO + setmcn(mcn, &trackp[0]); +#else + trackp[0].isrc = malloc(16); + fillbytes(trackp[0].isrc, 16, '\0'); + strncpy(trackp[0].isrc, mcn, 13); +#endif + mcn = NULL; + } + if ((raw96r + raw96p + raw16) > 1) { + errmsgno(EX_BAD, "Too many raw modes.\n"); + comerrno(EX_BAD, "Only one of -raw16, -raw96p, -raw96r allowed.\n"); + } + if ((tao + ispacket + dao + raw) > 1) { + errmsgno(EX_BAD, "Too many write modes.\n"); + comerrno(EX_BAD, "Only one of -packet, -dao, -raw allowed.\n"); + } + if (dao && (raw96r + raw96p + raw16) > 0) { + if (raw16) + comerrno(EX_BAD, "SAO RAW writing does not allow -raw16.\n"); + if (!clone) + comerrno(EX_BAD, "SAO RAW writing only makes sense in clone mode.\n"); +#ifndef CLONE_WRITE + comerrno(EX_BAD, "SAO RAW writing not yet implemented.\n"); +#endif + comerrno(EX_BAD, "SAO RAW writing not yet implemented.\n"); + } + if (clone) { + *flagsp |= F_CLONE; + trackp[0].flags |= TI_CLONE; +#ifndef CLONE_WRITE + comerrno(EX_BAD, "Clone writing not compiled in.\n"); +#endif + } + if (textfile) { + if (!checktextfile(textfile)) { + if ((*flagsp & F_WRITE) != 0) { + comerrno(EX_BAD, + "Cannot use '%s' as CD-Text file.\n", + textfile); + } + } + if ((*flagsp & F_WRITE) != 0) { + if ((dao + raw96r + raw96p) == 0) + comerrno(EX_BAD, + "CD-Text needs -dao, -raw96r or -raw96p.\n"); + } + trackp[0].flags |= TI_TEXT; + } + version = checkdrive = prcap = inq = scanbus = reset = doabort = + overburn = ignsize = + load = lock = eject = dummy = msinfo = toc = atip = multi = fix = nofix = + waiti = immed = force = dao = setdropts = 0; + raw96r = raw96p = raw16 = clone = 0; + } else if ((version + checkdrive + prcap + inq + scanbus + + reset + doabort + overburn + ignsize + + load + lock + eject + dummy + msinfo + toc + atip + multi + fix + nofix + + waiti + immed + force + dao + setdropts + + raw96r + raw96p + raw16 + clone) > 0 || + mcn != NULL) + comerrno(EX_BAD, "Badly placed option. Global options must be before any track.\n"); + + if (nopreemp) + preemp = 0; + if (nocopy) + copy = 0; + if (nopad) + pad = 0; + if (noshorttrack) + shorttrack = 0; + + if ((audio + data + mode2 + xa + xa1 + xa2 + xamix) > 1) { + errmsgno(EX_BAD, "Too many types for track %d.\n", tracks+1); + comerrno(EX_BAD, "Only one of -audio, -data, -mode2, -xa, -xa1, -xa2, -xamix allowed.\n"); + } + if (ispacket && audio) { + comerrno(EX_BAD, "Audio data cannot be written in packet mode.\n"); + } + /* + * Check whether the next argument is a file type arg. + * If this is true, then we got a track file name. + * If getargs() did previously return NOTAFLAG, we may have hit + * an argument that has been escaped via "--", so we may not + * call getfiles() again in this case. If we would call + * getfiles() and the current arg has been escaped and looks + * like an option, a call to getfiles() would skip it. + */ + if (ga_ret != NOTAFLAG) + ga_ret = getfiles(&cac, &cav, opts); + if (autoaudio) { + autoaudio = 0; + tracktype = TOC_ROM; + sectype = ST_ROM_MODE1 | ST_MODE_1; + sectype = SECT_ROM; + dbtype = DB_ROM_MODE1; + secsize = DATA_SEC_SIZE; /* 2048 */ + dataoff = 16; + } + if (ga_ret == NOTAFLAG && (is_auname(cav[0]) || is_wavname(cav[0]))) { + /* + * We got a track and autodetection decided that it + * is an audio track. + */ + autoaudio++; + audio++; + } + if (data) { + /* + * 2048 Bytes user data + */ + tracktype = TOC_ROM; + sectype = ST_ROM_MODE1 | ST_MODE_1; + sectype = SECT_ROM; + dbtype = DB_ROM_MODE1; + secsize = DATA_SEC_SIZE; /* 2048 */ + dataoff = 16; + } + if (mode2) { + /* + * 2336 Bytes user data + */ + tracktype = TOC_ROM; + sectype = ST_ROM_MODE2 | ST_MODE_2; + sectype = SECT_MODE_2; + dbtype = DB_ROM_MODE2; + secsize = MODE2_SEC_SIZE; /* 2336 */ + dataoff = 16; + } + if (audio) { + /* + * 2352 Bytes user data + */ + tracktype = TOC_DA; + sectype = preemp ? ST_AUDIO_PRE : ST_AUDIO_NOPRE; + sectype |= ST_MODE_AUDIO; + sectype = SECT_AUDIO; + if (preemp) + sectype |= ST_PREEMPMASK; + dbtype = DB_RAW; + secsize = AUDIO_SEC_SIZE; /* 2352 */ + dataoff = 0; + } + if (xa) { + /* + * 2048 Bytes user data + */ + if (tracktype != TOC_CDI) + tracktype = TOC_XA2; + sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_1; + sectype = SECT_MODE_2_F1; + dbtype = DB_XA_MODE2; + secsize = DATA_SEC_SIZE; /* 2048 */ + dataoff = 24; + } + if (xa1) { + /* + * 8 Bytes subheader + 2048 Bytes user data + */ + if (tracktype != TOC_CDI) + tracktype = TOC_XA2; + sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_1; + sectype = SECT_MODE_2_F1; + dbtype = DB_XA_MODE2_F1; + secsize = 2056; + dataoff = 16; + } + if (xa2) { + /* + * 2324 Bytes user data + */ + if (tracktype != TOC_CDI) + tracktype = TOC_XA2; + sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_2; + sectype = SECT_MODE_2_F2; + dbtype = DB_XA_MODE2_F2; + secsize = 2324; + dataoff = 24; + } + if (xamix) { + /* + * 8 Bytes subheader + 2324 Bytes user data + */ + if (tracktype != TOC_CDI) + tracktype = TOC_XA2; + sectype = ST_ROM_MODE2 | ST_MODE_2_MIXED; + sectype = SECT_MODE_2_MIX; + dbtype = DB_XA_MODE2_MIX; + secsize = 2332; + dataoff = 16; + } + if (cdi) { + tracktype = TOC_CDI; + } + if (tracks == 0) { + trackp[0].tracktype = tracktype; + trackp[0].dbtype = dbtype; + trackp[0].isecsize = secsize; + trackp[0].secsize = secsize; + if ((*flagsp & F_RAW) != 0) { + trackp[0].secsize = is_raw16(&trackp[0]) ? + RAW16_SEC_SIZE:RAW96_SEC_SIZE; + } + if ((*flagsp & F_DUMMY) != 0) + trackp[0].tracktype |= TOCF_DUMMY; + if ((*flagsp & F_MULTI) != 0) + trackp[0].tracktype |= TOCF_MULTI; + } + + flags = trackp[0].flags; + + if ((sectype & ST_AUDIOMASK) != 0) + flags |= TI_AUDIO; + if (isize) { + flags |= TI_ISOSIZE; + if ((*flagsp & F_MULTI) != 0) + comerrno(EX_BAD, "Cannot get isosize for multi session disks.\n"); + /* + * As we do not get the padding from the ISO-9660 + * formatting utility, we need to force padding here. + */ + flags |= TI_PAD; + if (padsize == (Llong)0L) + padsize = (Llong)PAD_SIZE; + } + + if ((flags & TI_AUDIO) != 0) { + if (preemp) + flags |= TI_PREEMP; + if (copy) + flags |= TI_COPY; + if (scms) + flags |= TI_SCMS; + } + if (pad || ((flags & TI_AUDIO) == 0 && padsize > (Llong)0L)) { + flags |= TI_PAD; + if ((flags & TI_AUDIO) == 0 && padsize == (Llong)0L) + padsize = (Llong)PAD_SIZE; + } + if (shorttrack && (*flagsp & (F_SAO|F_RAW)) != 0) + flags |= TI_SHORT_TRACK; + if (noshorttrack) + flags &= ~TI_SHORT_TRACK; + if (bswab) + flags |= TI_SWAB; + if (ispacket) { + flags |= TI_PACKET; + trackp[0].flags &= ~TI_TAO; + } + if (noclose) + flags |= TI_NOCLOSE; + if (useinfo) + flags |= TI_USEINFO; + + if (ga_ret == NOARGS) { + /* + * All options have already been parsed and no more + * file type arguments are present. + */ + break; + } + if (tracks == 0 && (wm == 0)) { + errmsgno(EX_BAD, "No write mode specified.\n"); + errmsgno(EX_BAD, "Assuming -tao mode.\n"); + errmsgno(EX_BAD, "Future versions of wodim may have different drive dependent defaults.\n"); + tao = 1; + } + tracks++; + + if (tracks > MAX_TRACK) + comerrno(EX_BAD, "Track limit (%d) exceeded\n", + MAX_TRACK); + /* + * Make 'tracks' immediately usable in track structure. + */ + { register int i; + for (i = 0; i < MAX_TRACK+2; i++) + trackp[i].tracks = tracks; + } + + if (strcmp("-", cav[0]) == 0) + *flagsp |= F_STDIN; + + if (!is_auname(cav[0]) && !is_wavname(cav[0])) + flags |= TI_NOAUHDR; + + if ((*flagsp & (F_SAO|F_RAW)) != 0 && (flags & TI_AUDIO) != 0) + flags |= TI_PREGAP; /* Hack for now */ + if (tracks == 1) + flags &= ~TI_PREGAP; + + if (tracks == 1 && (pregapsize != -1L && pregapsize != 150)) + pregapsize = -1L; + trackp[tracks].filename = cav[0]; + trackp[tracks].trackstart = 0L; + trackp[tracks].itracksize = tracksize; + trackp[tracks].tracksize = tracksize; + trackp[tracks].tracksecs = -1L; + if (tracksize >= 0) { + trackp[tracks].tracksecs = (tracksize+secsize-1)/secsize; + if(tracksize<616448) + warn_minisize=tracksize; + } + if (trackp[tracks].pregapsize < 0) + trackp[tracks].pregapsize = pregapsize; + trackp[tracks+1].pregapsize = -1; + trackp[tracks].padsecs = (padsize+2047)/2048; + trackp[tracks].isecsize = secsize; + trackp[tracks].secsize = secsize; + trackp[tracks].flags = flags; + /* + * XXX Dies ist falsch: auch bei SAO/RAW kann + * XXX secsize != isecsize sein. + */ + if ((*flagsp & F_RAW) != 0) { + if (is_raw16(&trackp[tracks])) + trackp[tracks].secsize = RAW16_SEC_SIZE; + else + trackp[tracks].secsize = RAW96_SEC_SIZE; +#ifndef HAVE_LIB_EDC_ECC + if ((sectype & ST_MODE_MASK) != ST_MODE_AUDIO) { + errmsgno(EX_BAD, + "EDC/ECC library not compiled in.\n"); + comerrno(EX_BAD, + "Data sectors are not supported in RAW mode.\n"); + } +#endif + } + trackp[tracks].secspt = 0; /* transfer size is set up in set_trsizes() */ + trackp[tracks].pktsize = pktsize; + trackp[tracks].trackno = tracks; + trackp[tracks].sectype = sectype; +#ifdef CLONE_WRITE + if ((*flagsp & F_CLONE) != 0) { + trackp[tracks].isecsize = 2448; + trackp[tracks].sectype |= ST_MODE_RAW; + dataoff = 0; + } +#endif + trackp[tracks].dataoff = dataoff; + trackp[tracks].tracktype = tracktype; + trackp[tracks].dbtype = dbtype; + trackp[tracks].flags = flags; + trackp[tracks].nindex = 1; + trackp[tracks].tindex = 0; + if (isrc) { +#ifdef AUINFO + setisrc(isrc, &trackp[tracks]); +#else + trackp[tracks].isrc = malloc(16); + fillbytes(trackp[tracks].isrc, 16, '\0'); + strncpy(trackp[tracks].isrc, isrc, 12); +#endif + } + if (tindex) { +#ifdef AUINFO + setindex(tindex, &trackp[tracks]); +#endif + } + } + + if (dminbuf >= 0) { + if (dminbuf < 25 || dminbuf > 95) + comerrno(EX_BAD, + "Bad minbuf=%d option (must be between 25 and 95)\n", + dminbuf); + } + + if (speed < 0 && speed != -1) + comerrno(EX_BAD, "Bad speed option.\n"); + + if (fs < 0L && fs != -1L) + comerrno(EX_BAD, "Bad fifo size option.\n"); + + if (bufsize < 0L && bufsize != -1L) + comerrno(EX_BAD, "Bad transfer size option.\n"); + if (bufsize < 0L) + bufsize = CDR_BUF_SIZE; + if (bufsize > CDR_MAX_BUF_SIZE) + bufsize = CDR_MAX_BUF_SIZE; + + dev = *devp; + cdr_defaults(&dev, &speed, &fs, &driveropts); + if (debug) { + printf("dev: '%s' speed: %d fs: %ld driveropts '%s'\n", + dev, speed, fs, driveropts); + } + if (speed >= 0) + *speedp = speed; + + if (fs < 0L) + fs = DEFAULT_FIFOSIZE; + if (fs < 2*bufsize) { + errmsgno(EX_BAD, "Fifo size %ld too small, turning fifo off.\n", fs); + fs = 0L; + } + + if (dev != *devp && (*flagsp & F_SCANBUS) == 0) + *devp = dev; + + if (*devp && + ((strncmp(*devp, "HELP", 4) == 0) || + (strncmp(*devp, "help", 4) == 0))) { + *flagsp |= F_CHECKDRIVE; /* Set this for not calling mlockall() */ + return ispacket; + } + if (*flagsp & (F_LOAD|F_DLCK|F_SETDROPTS|F_MSINFO|F_TOC|F_PRATIP|F_FIX|F_VERSION|F_CHECKDRIVE|F_PRCAP|F_INQUIRY|F_SCANBUS|F_RESET|F_ABORT)) { + if (tracks != 0) { + fprintf(stderr, + "No tracks allowed with -load, -lock, -setdropts, -msinfo, -toc, -atip, -fix,\n" + "-version, -checkdrive, -prcap, -inq, -scanbus, --devices, -reset and -abort options.\n" ); + exit(EXIT_FAILURE); + } + return ispacket; + } + *tracksp = tracks; + if (*flagsp & F_SAO) { + /* + * Make sure that you change WRITER_MAXWAIT & READER_MAXWAIT + * too if you change this timeout. + */ + if (*timeoutp < 200) /* Lead in size is 2:30 */ + *timeoutp = 200; /* 200s is 150s *1.33 */ + } + if (usetext) { + trackp[MAX_TRACK+1].flags |= TI_TEXT; + } + if (cuefile) { +#ifdef FUTURE + if ((*flagsp & F_SAO) == 0 && + (*flagsp & F_RAW) == 0) { +#else + if ((*flagsp & F_SAO) == 0) { +#endif + errmsgno(EX_BAD, "The cuefile= option only works with -dao.\n"); + susage(EX_BAD); + } + if (tracks > 0) { + errmsgno(EX_BAD, "No tracks allowed with the cuefile= option\n"); + susage(EX_BAD); + } + cuefilename = cuefile; + return ispacket; + } + if (tracks == 0 && (*flagsp & (F_LOAD|F_DLCK|F_EJECT|F_BLANK|F_FORMAT)) == 0) { + errmsgno(EX_BAD, "No tracks specified. Need at least one.\n"); + susage(EX_BAD); + } + return ispacket; +} + +static void +set_trsizes(cdr_t *dp, int tracks, track_t *trackp) +{ + int i; + int secsize; + int secspt; + + trackp[1].flags |= TI_FIRST; + trackp[tracks].flags |= TI_LAST; + + if (xdebug) + printf("Set Transfersizes start\n"); + for (i = 0; i <= tracks+1; i++) { + if ((dp->cdr_flags & CDR_SWABAUDIO) != 0 && + is_audio(&trackp[i])) { + trackp[i].flags ^= TI_SWAB; + } + if (!is_audio(&trackp[i])) + trackp[i].flags &= ~TI_SWAB; /* Only swab audio */ + + /* + * Use the biggest sector size to compute how many + * sectors may fit into one single DMA buffer. + */ + secsize = trackp[i].secsize; + if (trackp[i].isecsize > secsize) + secsize = trackp[i].isecsize; + + /* + * We are using SCSI Group 0 write + * and cannot write more than 255 secs at once. + */ + secspt = bufsize/secsize; + secspt = min(255, secspt); + trackp[i].secspt = secspt; + + if (is_packet(&trackp[i]) && trackp[i].pktsize > 0) { + if (trackp[i].secspt >= trackp[i].pktsize) { + trackp[i].secspt = trackp[i].pktsize; + } else { + comerrno(EX_BAD, + "Track %d packet size %d exceeds buffer limit of %d sectors", + i, trackp[i].pktsize, trackp[i].secspt); + } + } + if (xdebug) { + printf("Track %d flags %X secspt %d secsize: %d isecsize: %d\n", + i, trackp[i].flags, trackp[i].secspt, + trackp[i].secsize, trackp[i].isecsize); + } + } + if (xdebug) + printf("Set Transfersizes end\n"); +} + +void +load_media(SCSI *usalp, cdr_t *dp, BOOL doexit) +{ + int code; + int key; + BOOL immed = (dp->cdr_cmdflags&F_IMMED) != 0; + + /* + * Do some preparation before... + */ + usalp->silent++; /* Be quiet if this fails */ + test_unit_ready(usalp); /* First eat up unit attention */ + if ((*dp->cdr_load)(usalp, dp) < 0) { /* now try to load media and */ + if (!doexit) + return; + comerrno(EX_BAD, "Cannot load media.\n"); + } + scsi_start_stop_unit(usalp, 1, 0, immed); /* start unit in silent mode */ + usalp->silent--; + + if (!wait_unit_ready(usalp, 60)) { + code = usal_sense_code(usalp); + key = usal_sense_key(usalp); + usalp->silent++; + scsi_prevent_removal(usalp, 0); /* In case someone locked it */ + usalp->silent--; + + if (!doexit) + return; + if (key == SC_NOT_READY && (code == 0x3A || code == 0x30)) + comerrno(EX_BAD, "No disk / Wrong disk!\n"); + comerrno(EX_BAD, "CD/DVD-Recorder not ready.\n"); + } + + scsi_prevent_removal(usalp, 1); + scsi_start_stop_unit(usalp, 1, 0, immed); + wait_unit_ready(usalp, 120); + usalp->silent++; + if(geteuid() == 0) /* EB: needed? Not allowed for non-root, that is sure. */ + rezero_unit(usalp); /* Is this needed? Not supported by some drvives */ + usalp->silent--; + test_unit_ready(usalp); + scsi_start_stop_unit(usalp, 1, 0, immed); + wait_unit_ready(usalp, 120); +} + +void +unload_media(SCSI *usalp, cdr_t *dp, int flags) +{ + scsi_prevent_removal(usalp, 0); + if ((flags & F_EJECT) != 0) { + if ((*dp->cdr_unload)(usalp, dp) < 0) + errmsgno(EX_BAD, "Cannot eject media.\n"); + } +} + +void +reload_media(SCSI *usalp, cdr_t *dp) +{ + char ans[2]; +#ifdef F_GETFL + int f = -1; +#endif + + errmsgno(EX_BAD, "Drive needs to reload the media to return to proper status.\n"); + unload_media(usalp, dp, F_EJECT); + + /* + * Note that even Notebook drives identify as CDR_TRAYLOAD + */ + if ((dp->cdr_flags & CDR_TRAYLOAD) != 0) { + usalp->silent++; + load_media(usalp, dp, FALSE); + usalp->silent--; + } + + usalp->silent++; + if (((dp->cdr_flags & CDR_TRAYLOAD) == 0) || + !wait_unit_ready(usalp, 5)) { + static FILE *tty = NULL; + + printf("Re-load disk and hit "); + if (isgui) + printf("\n"); + flush(); + + if (tty == NULL) { + tty = stdin; + if ((dp->cdr_cmdflags & F_STDIN) != 0) + tty = fileluopen(STDERR_FILENO, "rw"); + } +#ifdef F_GETFL + if (tty != NULL) + f = fcntl(fileno(tty), F_GETFL, 0); + if (f < 0 || (f & O_ACCMODE) == O_WRONLY) { +#ifdef SIGUSR1 + signal(SIGUSR1, catchsig); + printf("Controlling file not open for reading, send SIGUSR1 to continue.\n"); + flush(); + pause(); +#endif + } else +#endif + if (rols_fgetline(tty, ans, 1) < 0) + comerrno(EX_BAD, "Aborted by EOF on input.\n"); + } + usalp->silent--; + + load_media(usalp, dp, TRUE); +} + +void +set_secsize(SCSI *usalp, int secsize) +{ + if (secsize > 0) { + /* + * Try to restore the old sector size. + */ + usalp->silent++; + select_secsize(usalp, secsize); + usalp->silent--; + } +} + +static int +get_dmaspeed(SCSI *usalp, cdr_t *dp) +{ + int i; + long t; + int bs; + int tsize; + + if(getenv("CDR_NODMATEST")) + return -1; + + if (debug || lverbose) + fprintf( stderr, + "Beginning DMA speed test. Set CDR_NODMATEST environment variable if device\n" + "communication breaks or freezes immediately after that.\n" ); + + fillbytes((caddr_t)buf, 4, '\0'); + tsize = 0; + usalp->silent++; + i = read_buffer(usalp, buf, 4, 0); + usalp->silent--; + if (i < 0) + return (-1); + tsize = a_to_u_4_byte(buf); + if (tsize <= 0) + return (-1); + + if (gettimeofday(&starttime, (struct timezone *)0) < 0) + return (-1); + + bs = bufsize; + if (tsize < bs) + bs = tsize; + for (i = 0; i < 100; i++) { + if (read_buffer(usalp, buf, bs, 0) < 0) + return (-1); + } + if (gettimeofday(&fixtime, (struct timezone *)0) < 0) { + errmsg("Cannot get DMA stop time\n"); + return (-1); + } + timevaldiff(&starttime, &fixtime); + tsize = bs * 100; + t = fixtime.tv_sec * 1000 + fixtime.tv_usec / 1000; + if (t <= 0) + return (-1); +#ifdef DEBUG + fprintf(stderr, "Read Speed: %lu %ld %ld kB/s %ldx CD %ldx DVD\n", + tsize, t, tsize/t, tsize/t/176, tsize/t/1385); +#endif + + return (tsize/t); +} + + +static BOOL +do_opc(SCSI *usalp, cdr_t *dp, int flags) +{ + if ((flags & F_DUMMY) == 0 && dp->cdr_opc) { + if (debug || lverbose) { + printf("Performing OPC...\n"); + flush(); + } + if (dp->cdr_opc(usalp, NULL, 0, TRUE) < 0) { + errmsgno(EX_BAD, "OPC failed.\n"); + if ((flags & F_FORCE) == 0) + return (FALSE); + } + } + return (TRUE); +} + +static void +check_recovery(SCSI *usalp, cdr_t *dp, int flags) +{ + if ((*dp->cdr_check_recovery)(usalp, dp)) { + errmsgno(EX_BAD, "Recovery needed.\n"); + unload_media(usalp, dp, flags); + comexit(EX_BAD); + } +} + +#ifndef DEBUG +#define DEBUG +#endif +void +audioread(SCSI *usalp, cdr_t *dp, int flags) +{ +#ifdef DEBUG + int speed = 1; + int oflags = dp->cdr_cmdflags; + + dp->cdr_cmdflags &= ~F_DUMMY; + if ((*dp->cdr_set_speed_dummy)(usalp, dp, &speed) < 0) + comexit(-1); + dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */ + dp->cdr_cmdflags = oflags; + + if ((*dp->cdr_set_secsize)(usalp, 2352) < 0) + comexit(-1); + usalp->cap->c_bsize = 2352; + + read_scsi(usalp, buf, 1000, 1); + printf("XXX:\n"); + do{int ret;ret=write(1, buf, 512);}while(0); /* FIXME: handle return value */ + unload_media(usalp, dp, flags); + comexit(0); +#endif +} + +static void +print_msinfo(SCSI *usalp, cdr_t *dp) +{ + long off; + long fa; + + if ((*dp->cdr_session_offset)(usalp, &off) < 0) { + errmsgno(EX_BAD, "Cannot read session offset\n"); + return; + } + if (lverbose) + printf("session offset: %ld\n", off); + + if (dp->cdr_next_wr_address(usalp, (track_t *)0, &fa) < 0) { + errmsgno(EX_BAD, "Cannot read first writable address\n"); + return; + } + printf("%ld,%ld\n", off, fa); + if(msifile) { + FILE *f = fopen(msifile, "w"); + if(f) { + fprintf(f, "%ld,%ld", off, fa); + fclose(f); + } + else { + perror("Unable to write multi session info file"); + exit(EXIT_FAILURE); + } + } +} + +static void +print_toc(SCSI *usalp, cdr_t *dp) +{ + int first; + int last; + long lba; + long xlba; + struct msf msf; + int adr; + int control; + int mode; + int i; + + usalp->silent++; + if (read_capacity(usalp) < 0) { + usalp->silent--; + errmsgno(EX_BAD, "Cannot read capacity\n"); + return; + } + usalp->silent--; + if (read_tochdr(usalp, dp, &first, &last) < 0) { + errmsgno(EX_BAD, "Cannot read TOC/PMA\n"); + return; + } + printf("first: %d last %d\n", first, last); + for (i = first; i <= last; i++) { + read_trackinfo(usalp, i, &lba, &msf, &adr, &control, &mode); + xlba = -150 + + msf.msf_frame + (75*msf.msf_sec) + (75*60*msf.msf_min); + if (xlba == lba/4) + lba = xlba; + print_track(i, lba, &msf, adr, control, mode); + } + i = 0xAA; + read_trackinfo(usalp, i, &lba, &msf, &adr, &control, &mode); + xlba = -150 + + msf.msf_frame + (75*msf.msf_sec) + (75*60*msf.msf_min); + if (xlba == lba/4) + lba = xlba; + print_track(i, lba, &msf, adr, control, mode); + if (lverbose > 1) { + usalp->silent++; + if (read_cdtext(usalp) < 0) + errmsgno(EX_BAD, "No CD-Text or CD-Text unaware drive.\n"); + usalp->silent++; + } +} + +static void +print_track(int track, long lba, struct msf *msp, int adr, + int control, int mode) +{ + long lba_512 = lba*4; + + if (track == 0xAA) + printf("track:lout "); + else + printf("track: %3d ", track); + + printf("lba: %9ld (%9ld) %02d:%02d:%02d adr: %X control: %X mode: %d\n", + lba, lba_512, + msp->msf_min, + msp->msf_sec, + msp->msf_frame, + adr, control, mode); +} + +#ifdef HAVE_SYS_PRIOCNTL_H /* The preferred SYSvR4 schduler */ + +#include /* Needed for SCO Openserver */ +#include +#include + +void +raisepri(int pri) +{ + int pid; + int classes; + int ret; + pcinfo_t info; + pcparms_t param; + rtinfo_t rtinfo; + rtparms_t rtparam; + + pid = getpid(); + + /* get info */ + strcpy(info.pc_clname, "RT"); + classes = priocntl(P_PID, pid, PC_GETCID, (void *)&info); + if (classes == -1) + comerr("Cannot get priority class id priocntl(PC_GETCID)\n"); + + movebytes(info.pc_clinfo, &rtinfo, sizeof (rtinfo_t)); + + /* set priority to max */ + rtparam.rt_pri = rtinfo.rt_maxpri - pri; + rtparam.rt_tqsecs = 0; + rtparam.rt_tqnsecs = RT_TQDEF; + param.pc_cid = info.pc_cid; + movebytes(&rtparam, param.pc_clparms, sizeof (rtparms_t)); + ret = priocntl(P_PID, pid, PC_SETPARMS, (void *)¶m); + if (ret == -1) { + errmsg("WARNING: Cannot set priority class parameters priocntl(PC_SETPARMS)\n"); + errmsgno(EX_BAD, "WARNING: This causes a high risk for buffer underruns.\n"); + } +} + +#else /* HAVE_SYS_PRIOCNTL_H */ + +#if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING -0 >= 0 +/* + * The second best choice: POSIX real time scheduling. + */ +/* + * XXX Ugly but needed because of a typo in /usr/iclude/sched.h on Linux. + * XXX This should be removed as soon as we are sure that Linux-2.0.29 is gone. + */ +#ifdef __linux +#define _P __P +#endif + +#include + +#ifdef __linux +#undef _P +#endif + +static int +rt_raisepri(int pri) +{ + struct sched_param scp; + + /* + * Verify that scheduling is available + */ +#ifdef _SC_PRIORITY_SCHEDULING + if (sysconf(_SC_PRIORITY_SCHEDULING) == -1) { + errmsg("WARNING: RR-scheduler not available, disabling.\n"); + return (-1); + } +#endif + fillbytes(&scp, sizeof (scp), '\0'); + scp.sched_priority = sched_get_priority_max(SCHED_RR) - pri; + if (sched_setscheduler(0, SCHED_RR, &scp) < 0) { + if(lverbose>2) + errmsg("WARNING: Cannot set RR-scheduler\n"); + return (-1); + } + return (0); +} + +#else /* _POSIX_PRIORITY_SCHEDULING */ + +#ifdef __CYGWIN32__ +/* + * Win32 specific priority settings. + */ +/* + * NOTE: Base.h from Cygwin-B20 has a second typedef for BOOL. + * We define BOOL to make all static code use BOOL + * from Windows.h and use the hidden __SBOOL for + * our global interfaces. + * + * NOTE: windows.h from Cygwin-1.x includes a structure field named sample, + * so me may not define our own 'sample' or need to #undef it now. + * With a few nasty exceptions, Microsoft assumes that any global + * defines or identifiers will begin with an Uppercase letter, so + * there may be more of these problems in the future. + * + * NOTE: windows.h defines interface as an alias for struct, this + * is used by COM/OLE2, I guess it is class on C++ + * We man need to #undef 'interface' + */ +#define BOOL WBOOL /* This is the Win BOOL */ +#define format __format /* Avoid format parameter hides global ... */ +#include +#undef format +#undef interface + +static int +rt_raisepri(int pri) +{ + int prios[] = {THREAD_PRIORITY_TIME_CRITICAL, THREAD_PRIORITY_HIGHEST}; + + /* set priority class */ + if (SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == FALSE) { + if(debug || lverbose>2) + errmsgno(EX_BAD, "No realtime priority class possible.\n"); + return (-1); + } + + /* set thread priority */ + if (pri >= 0 && pri <= 1 && SetThreadPriority(GetCurrentThread(), prios[pri]) == FALSE) { + errmsgno(EX_BAD, "Could not set realtime priority.\n"); + return (-1); + } + return (0); +} + +#else +/* + * This OS does not support real time scheduling. + */ +static int +rt_raisepri(int pri) +{ + return (-1); +} + +#endif /* __CYGWIN32__ */ + +#endif /* _POSIX_PRIORITY_SCHEDULING */ + +void +raisepri(int pri) +{ + if (rt_raisepri(pri) >= 0) + return; +#if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS) + + if (setpriority(PRIO_PROCESS, getpid(), -20 + pri) < 0) { + if(debug || lverbose>2) + fprintf(stderr, + "WARNING: Cannot set priority using setpriority()," + "increased risk for buffer underruns.\n"); + } +#else +#ifdef HAVE_DOSSETPRIORITY /* RT priority on OS/2 */ + /* + * Set priority to timecritical 31 - pri (arg) + */ + DosSetPriority(0, 3, 31, 0); + DosSetPriority(0, 3, -pri, 0); +#else +#if defined(HAVE_NICE) && !defined(__DJGPP__) /* DOS has nice but no multitasking */ + if (nice(-20 + pri) == -1) { + errmsg("WARNING: Cannot set priority using nice().\n"); + errmsgno(EX_BAD, "WARNING: This causes a high risk for buffer underruns.\n"); + } +#else + errmsgno(EX_BAD, "WARNING: Cannot set priority on this OS.\n"); + errmsgno(EX_BAD, "WARNING: This causes a high risk for buffer underruns.\n"); +#endif +#endif +#endif +} + +#endif /* HAVE_SYS_PRIOCNTL_H */ + +#ifdef HAVE_SELECT +/* + * sys/types.h and sys/time.h are already included. + */ +#else +# include +# include + +#ifndef INFTIM +#define INFTIM (-1) +#endif +#endif + +#if defined(HAVE_SELECT) && defined(NEED_SYS_SELECT_H) +#include +#endif +#if defined(HAVE_SELECT) && defined(NEED_SYS_SOCKET_H) +#include +#endif + +static void +wait_input() +{ +#ifdef HAVE_SELECT + fd_set in; + + FD_ZERO(&in); + FD_SET(STDIN_FILENO, &in); + select(1, &in, NULL, NULL, 0); +#else + struct pollfd pfd; + + pfd.fd = STDIN_FILENO; + pfd.events = POLLIN; + pfd.revents = 0; + poll(&pfd, (unsigned long)1, INFTIM); +#endif +} + +static void +checkgui() +{ + struct stat st; + + if (fstat(STDERR_FILENO, &st) >= 0 && !S_ISCHR(st.st_mode)) { + isgui = TRUE; + if (lverbose > 1) + printf("Using remote (pipe) mode for interactive i/o.\n"); + } +} + +static int +getbltype(char *optstr, long *typep) +{ + if (streql(optstr, "all")) { + *typep = BLANK_DISC; + } else if (streql(optstr, "disc")) { + *typep = BLANK_DISC; + } else if (streql(optstr, "disk")) { + *typep = BLANK_DISC; + } else if (streql(optstr, "fast")) { + *typep = BLANK_MINIMAL; + } else if (streql(optstr, "minimal")) { + *typep = BLANK_MINIMAL; + } else if (streql(optstr, "track")) { + *typep = BLANK_TRACK; + } else if (streql(optstr, "unreserve")) { + *typep = BLANK_UNRESERVE; + } else if (streql(optstr, "trtail")) { + *typep = BLANK_TAIL; + } else if (streql(optstr, "unclose")) { + *typep = BLANK_UNCLOSE; + } else if (streql(optstr, "session")) { + *typep = BLANK_SESSION; + } else if (streql(optstr, "help")) { + blusage(0); + } else { + fprintf(stderr, "Illegal blanking type '%s'.\n", optstr); + blusage(EX_BAD); + return (-1); + } + return (TRUE); +} + +static int +getformattype(char *optstr, long *typep) +{ + if (streql(optstr, "full")) { + *typep = FULL_FORMAT; + } else if (streql(optstr, "background")) { + *typep = BACKGROUND_FORMAT; + } else if (streql(optstr, "force")) { + *typep = FORCE_FORMAT; + } else if (streql(optstr, "help")) { + formattypeusage(0); + } else { + fprintf(stderr, "Illegal blanking type '%s'.\n", optstr); + formattypeusage(EX_BAD); + return (-1); + } + return (TRUE); +} +static void +print_drflags(cdr_t *dp) +{ + printf("Driver flags : "); + + if ((dp->cdr_flags & CDR_DVD) != 0) + printf("DVD "); + + if ((dp->cdr_flags & CDR_MMC3) != 0) + printf("MMC-3 "); + else if ((dp->cdr_flags & CDR_MMC2) != 0) + printf("MMC-2 "); + else if ((dp->cdr_flags & CDR_MMC) != 0) + printf("MMC "); + + if ((dp->cdr_flags & CDR_SWABAUDIO) != 0) + printf("SWABAUDIO "); + if ((dp->cdr_flags & CDR_BURNFREE) != 0) + printf("BURNFREE "); + if ((dp->cdr_flags & CDR_VARIREC) != 0) + printf("VARIREC "); + if ((dp->cdr_flags & CDR_GIGAREC) != 0) + printf("GIGAREC "); + if ((dp->cdr_flags & CDR_AUDIOMASTER) != 0) + printf("AUDIOMASTER "); + if ((dp->cdr_flags & CDR_FORCESPEED) != 0) + printf("FORCESPEED "); + if ((dp->cdr_flags & CDR_SPEEDREAD) != 0) + printf("SPEEDREAD "); + if ((dp->cdr_flags & CDR_DISKTATTOO) != 0) + printf("DISKTATTOO "); + if ((dp->cdr_flags & CDR_SINGLESESS) != 0) + printf("SINGLESESSION "); + if ((dp->cdr_flags & CDR_HIDE_CDR) != 0) + printf("HIDECDR "); + printf("\n"); +} + +static void +print_wrmodes(cdr_t *dp) +{ + BOOL needblank = FALSE; + + printf("Supported modes: "); + if ((dp->cdr_flags & CDR_TAO) != 0) { + printf("TAO"); + needblank = TRUE; + } + if ((dp->cdr_flags & CDR_PACKET) != 0) { + printf("%sPACKET", needblank?" ":""); + needblank = TRUE; + } + if ((dp->cdr_flags & CDR_SAO) != 0) { + printf("%sSAO", needblank?" ":""); + needblank = TRUE; + } +#ifdef __needed__ + if ((dp->cdr_flags & (CDR_SAO|CDR_SRAW16)) == (CDR_SAO|CDR_SRAW16)) { + printf("%sSAO/R16", needblank?" ":""); + needblank = TRUE; + } +#endif + if ((dp->cdr_flags & (CDR_SAO|CDR_SRAW96P)) == (CDR_SAO|CDR_SRAW96P)) { + printf("%sSAO/R96P", needblank?" ":""); + needblank = TRUE; + } + if ((dp->cdr_flags & (CDR_SAO|CDR_SRAW96R)) == (CDR_SAO|CDR_SRAW96R)) { + printf("%sSAO/R96R", needblank?" ":""); + needblank = TRUE; + } + if ((dp->cdr_flags & (CDR_RAW|CDR_RAW16)) == (CDR_RAW|CDR_RAW16)) { + printf("%sRAW/R16", needblank?" ":""); + needblank = TRUE; + } + if ((dp->cdr_flags & (CDR_RAW|CDR_RAW96P)) == (CDR_RAW|CDR_RAW96P)) { + printf("%sRAW/R96P", needblank?" ":""); + needblank = TRUE; + } + if ((dp->cdr_flags & (CDR_RAW|CDR_RAW96R)) == (CDR_RAW|CDR_RAW96R)) { + printf("%sRAW/R96R", needblank?" ":""); + needblank = TRUE; + } + printf("\n"); +} + +static BOOL +check_wrmode(cdr_t *dp, int wmode, int tflags) +{ + int cdflags = dp->cdr_flags; + + if ((tflags & TI_PACKET) != 0 && (cdflags & CDR_PACKET) == 0) { + errmsgno(EX_BAD, "Drive does not support PACKET recording.\n"); + return (FALSE); + } + if ((tflags & TI_TAO) != 0 && (cdflags & CDR_TAO) == 0) { + errmsgno(EX_BAD, "Drive does not support TAO recording.\n"); + return (FALSE); + } + if ((wmode & F_SAO) != 0) { + if ((cdflags & CDR_SAO) == 0) { + errmsgno(EX_BAD, "Drive does not support SAO recording.\n"); + if ((cdflags & CDR_RAW) != 0) + errmsgno(EX_BAD, "Try -raw option.\n"); + return (FALSE); + } +#ifdef __needed__ + if ((tflags & TI_RAW16) != 0 && (cdflags & CDR_SRAW16) == 0) { + errmsgno(EX_BAD, "Drive does not support SAO/RAW16.\n"); + goto badsecs; + } +#endif + if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW && (cdflags & CDR_SRAW96P) == 0) { + errmsgno(EX_BAD, "Drive does not support SAO/RAW96P.\n"); + goto badsecs; + } + if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R) && (cdflags & CDR_SRAW96R) == 0) { + errmsgno(EX_BAD, "Drive does not support SAO/RAW96R.\n"); + goto badsecs; + } + } + if ((wmode & F_RAW) != 0) { + if ((cdflags & CDR_RAW) == 0) { + errmsgno(EX_BAD, "Drive does not support RAW recording.\n"); + return (FALSE); + } + if ((tflags & TI_RAW16) != 0 && (cdflags & CDR_RAW16) == 0) { + errmsgno(EX_BAD, "Drive does not support RAW/RAW16.\n"); + goto badsecs; + } + if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW && (cdflags & CDR_RAW96P) == 0) { + errmsgno(EX_BAD, "Drive does not support RAW/RAW96P.\n"); + goto badsecs; + } + if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R) && (cdflags & CDR_RAW96R) == 0) { + errmsgno(EX_BAD, "Drive does not support RAW/RAW96R.\n"); + goto badsecs; + } + } + return (TRUE); + +badsecs: + if ((wmode & F_SAO) != 0) + cdflags &= ~(CDR_RAW16|CDR_RAW96P|CDR_RAW96R); + if ((wmode & F_RAW) != 0) + cdflags &= ~(CDR_SRAW96P|CDR_SRAW96R); + + if ((cdflags & (CDR_SRAW96R|CDR_RAW96R)) != 0) + errmsgno(EX_BAD, "Try -raw96r option.\n"); + else if ((cdflags & (CDR_SRAW96P|CDR_RAW96P)) != 0) + errmsgno(EX_BAD, "Try -raw96p option.\n"); + else if ((cdflags & CDR_RAW16) != 0) + errmsgno(EX_BAD, "Try -raw16 option.\n"); + return (FALSE); +} + +static void +set_wrmode(cdr_t *dp, int wmode, int tflags) +{ + dstat_t *dsp = dp->cdr_dstat; + + if ((tflags & TI_PACKET) != 0) { + dsp->ds_wrmode = WM_PACKET; + return; + } + if ((tflags & TI_TAO) != 0) { + dsp->ds_wrmode = WM_TAO; + return; + } + if ((wmode & F_SAO) != 0) { + if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == 0) { + dsp->ds_wrmode = WM_SAO; + return; + } + if ((tflags & TI_RAW16) != 0) { /* Is this needed? */ + dsp->ds_wrmode = WM_SAO_RAW16; + return; + } + if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW) { + dsp->ds_wrmode = WM_SAO_RAW96P; + return; + } + if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R)) { + dsp->ds_wrmode = WM_SAO_RAW96R; + return; + } + } + if ((wmode & F_RAW) != 0) { + if ((tflags & TI_RAW16) != 0) { + dsp->ds_wrmode = WM_RAW_RAW16; + return; + } + if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW) { + dsp->ds_wrmode = WM_RAW_RAW96P; + return; + } + if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R)) { + dsp->ds_wrmode = WM_RAW_RAW96R; + return; + } + } + dsp->ds_wrmode = WM_NONE; +} + +#if defined(linux) || defined(__linux) || defined(__linux__) +#ifdef HAVE_UNAME +#include +#endif +#endif + +#ifdef __linux__ +static int +get_cap(cap_value_t cap_array) +{ + int ret; + cap_t capa; + capa = cap_get_proc(); + cap_set_flag(capa, CAP_EFFECTIVE, 1, &cap_array, CAP_SET); + ret = cap_set_proc(capa); + cap_free(capa); + return ret; +} +#endif