diff options
Diffstat (limited to 'wodim/drv_sony.c')
-rw-r--r-- | wodim/drv_sony.c | 1364 |
1 files changed, 1364 insertions, 0 deletions
diff --git a/wodim/drv_sony.c b/wodim/drv_sony.c new file mode 100644 index 0000000..af8af68 --- /dev/null +++ b/wodim/drv_sony.c @@ -0,0 +1,1364 @@ +/* + * 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_sony.c 1.72 05/05/16 Copyright 1997-2005 J. Schilling */ +/* + * CDR device implementation for + * Sony + * + * Copyright (c) 1997-2005 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 SONY_DEBUG*/ + +#include <mconfig.h> + +#include <stdio.h> +#include <stdxlib.h> +#include <unixstd.h> /* Include sys/types.h to make off_t available */ +#include <standard.h> +#include <fctldefs.h> +#include <errno.h> +#include <strdefs.h> +#include <timedefs.h> + +#include <utypes.h> +#include <btorder.h> +#include <intcvt.h> +#include <schily.h> + +#include <usal/usalcmd.h> +#include <usal/scsidefs.h> +#include <usal/scsireg.h> +#include <usal/scsitransp.h> + +#include "wodim.h" + +#ifdef SONY_DEBUG +# define inc_verbose() usalp->verbose++ +# define dec_verbose() usalp->verbose-- +#else +# define inc_verbose() +# define dec_verbose() +#endif + +extern int debug; +extern int lverbose; + +#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */ + +struct sony_924_mode_page_20 { /* mastering information */ + MP_P_CODE; /* parsave & pagecode */ + Uchar p_len; /* 0x06 = 6 Bytes */ + Uchar subcode_header_off; + Ucbit res3_0 : 1; + Ucbit speudo : 1; + Ucbit res3_2 : 1; + Ucbit c2po : 1; + Ucbit subcode_ecc : 1; + Ucbit res3_567 : 3; + Uchar res_4; + Uchar cue_sheet_opt; + Uchar res[2]; +}; + +#else /* Motorola byteorder */ + +struct sony_924_mode_page_20 { /* mastering information */ + MP_P_CODE; /* parsave & pagecode */ + Uchar p_len; /* 0x06 = 6 Bytes */ + Uchar subcode_header_off; + Ucbit res3_567 : 3; + Ucbit subcode_ecc : 1; + Ucbit c2po : 1; + Ucbit res3_2 : 1; + Ucbit speudo : 1; + Ucbit res3_0 : 1; + Uchar res_4; + Uchar cue_sheet_opt; + Uchar res[2]; +}; +#endif + +struct sony_924_mode_page_22 { /* disk information */ + MP_P_CODE; /* parsave & pagecode */ + Uchar p_len; /* 0x1E = 30 Bytes */ + Uchar disk_style; + Uchar disk_type; + Uchar first_track; + Uchar last_track; + Uchar numsess; + Uchar res_7; + Uchar disk_appl_code[4]; + Uchar last_start_time[4]; + Uchar disk_status; + Uchar num_valid_nra; + Uchar track_info_track; + Uchar post_gap; + Uchar disk_id_code[4]; + Uchar lead_in_start[4]; + Uchar res[4]; +}; + +struct sony_924_mode_page_23 { /* track information */ + MP_P_CODE; /* parsave & pagecode */ + Uchar p_len; /* 0x22 = 34 Bytes */ + Uchar res_2; + Uchar track_num; + Uchar data_form; + Uchar write_method; + Uchar session; + Uchar track_status; + Uchar start_lba[4]; + Uchar next_recordable_addr[4]; + Uchar blank_area_cap[4]; + Uchar fixed_packet_size[4]; + Uchar res_24; + Uchar starting_msf[3]; + Uchar res_28; + Uchar ending_msf[3]; + Uchar res_32; + Uchar next_rec_time[3]; +}; + +struct sony_924_mode_page_31 { /* drive speed */ + MP_P_CODE; /* parsave & pagecode */ + Uchar p_len; /* 0x02 = 2 Bytes */ + Uchar speed; + Uchar res; +}; + +struct cdd_52x_mode_data { + struct scsi_mode_header header; + union cdd_pagex { + struct sony_924_mode_page_20 page_s20; + struct sony_924_mode_page_22 page_s22; + struct sony_924_mode_page_23 page_s23; + struct sony_924_mode_page_31 page_s31; + } pagex; +}; + +struct sony_write_parameter { + Uchar res0; /* Reserved (must be zero) */ + Uchar len; /* Parameter length 0x32 == 52 */ + Uchar res2; /* Reserved (must be zero) */ +#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */ + Ucbit res3_05 : 6; /* Reserved */ + Ucbit ms : 2; /* Multi session mode */ +#else /* Motorola byteorder */ + Ucbit ms : 2; /* Multi session mode */ + Ucbit res3_05 : 6; /* Reserved */ +#endif + Uchar resx[12]; +#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */ + Ucbit res16_06 : 7; /* Reserved */ + Ucbit mcval : 1; /* MCN valid */ +#else /* Motorola byteorder */ + Ucbit mcval : 1; /* MCN valid */ + Ucbit res16_06 : 7; /* Reserved */ +#endif + Uchar mcn[15]; +#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */ + Ucbit res32_06 : 7; /* Reserved */ + Ucbit icval : 1; /* ISRC valid */ +#else /* Motorola byteorder */ + Ucbit icval : 1; /* ISRC valid */ + Ucbit res32_06 : 7; /* Reserved */ +#endif + Uchar isrc[15]; + Uchar subheader[4]; +}; + +struct sony_cue { + Uchar cs_ctladr; /* CTL/ADR for this track */ + Uchar cs_tno; /* This track number */ + Uchar cs_index; /* Index within this track */ + Uchar cs_dataform; /* Data form */ + /* Bit 0..5 Main channel Format */ + /* Bit 6..7 SubChannel format */ + Uchar cs_zero; /* Reserved or MCN/ISRC */ + Uchar cs_min; /* Absolute time minutes */ + Uchar cs_sec; /* Absolute time seconds */ + Uchar cs_frame; /* Absolute time frames */ +}; + + +#define strbeg(s1, s2) (strstr((s2), (s1)) == (s2)) + +static int write_start_sony(SCSI *usalp, caddr_t bp, int size); +static int write_continue_sony(SCSI *usalp, caddr_t bp, long sectaddr, + long size, int blocks, BOOL islast); +static int discontinue_sony(SCSI *usalp); +static int write_track_sony(SCSI *usalp, long track, int sectype); +static int close_track_sony(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int flush_sony(SCSI *usalp, int track); +static int finalize_sony(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int recover_sony(SCSI *usalp, cdr_t *dp, int track); +static int set_wr_parameter_sony(SCSI *usalp, caddr_t bp, int size); +static int next_wr_addr_sony(SCSI *usalp, track_t *trackp, long *ap); +static int reserve_track_sony(SCSI *usalp, unsigned long len); +static int init_sony(SCSI *usalp, cdr_t *dp); +static int getdisktype_sony(SCSI *usalp, cdr_t *dp); +static void di_to_dstat_sony(struct sony_924_mode_page_22 *dip, + dstat_t *dsp); +static int speed_select_sony(SCSI *usalp, cdr_t *dp, int *speedp); +static int next_writable_address_sony(SCSI *usalp, long *ap, int track, + int sectype, int tracktype); +static int new_track_sony(SCSI *usalp, int track, int sectype, + int tracktype); +static int open_track_sony(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int open_session_sony(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int abort_session_sony(SCSI *usalp, cdr_t *dp); +static int get_page22_sony(SCSI *usalp, char *mode); +static int gen_cue_sony(track_t *trackp, void *vcuep, BOOL needgap); +static void fillcue(struct sony_cue *cp, int ca, int tno, int idx, int dataform, int scms, msf_t *mp); +static int send_cue_sony(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int write_leadin_sony(SCSI *usalp, cdr_t *dp, track_t *trackp); +static int sony_attach(SCSI *usalp, cdr_t *dp); +#ifdef SONY_DEBUG +static void print_sony_mp22(struct sony_924_mode_page_22 *xp, int len); +static void print_sony_mp23(struct sony_924_mode_page_23 *xp, int len); +#endif +static int buf_cap_sony(SCSI *usalp, long *, long *); + +cdr_t cdr_sony_cdu924 = { + 0, 0, + CDR_TAO|CDR_SAO|CDR_CADDYLOAD|CDR_SWABAUDIO, + CDR_CDRW_NONE, + 2, 4, + "sony_cdu924", + "driver for Sony CDU-924 / CDU-948", + 0, + (dstat_t *)0, + drive_identify, + sony_attach, + init_sony, + getdisktype_sony, + scsi_load, + scsi_unload, + buf_cap_sony, + cmd_dummy, /* recovery_needed */ + recover_sony, + speed_select_sony, + select_secsize, + next_wr_addr_sony, + reserve_track_sony, + write_continue_sony, + gen_cue_sony, + send_cue_sony, + write_leadin_sony, + open_track_sony, + close_track_sony, + open_session_sony, + cmd_dummy, + abort_session_sony, + read_session_offset_philips, + finalize_sony, + cmd_dummy, /* stats */ + blank_dummy, + format_dummy, + (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */ + cmd_dummy, /* opt1 */ + cmd_dummy, /* opt2 */ +}; + +static int +write_start_sony(SCSI *usalp, caddr_t bp, int 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|SCG_CMD_RETRY; + scmd->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->sense_len = 26; + scmd->cdb.g1_cdb.cmd = 0xE0; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g0_cdbaddr(&scmd->cdb.g0_cdb, size); /* Hack, but Sony is silly */ + + usalp->cmdname = "write_start"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +static int +write_continue_sony(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 */) +{ + 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->cdb_len = SC_G1_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.g1_cdb.cmd = 0xE1; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g0_cdbaddr(&scmd->cdb.g0_cdb, size); /* Hack, but Sony is silly */ + + usalp->cmdname = "write_continue"; + + if (usal_cmd(usalp) < 0) { + /* + * XXX This seems to happen only sometimes. + */ + if (usal_sense_code(usalp) != 0x80) + return (-1); + } + return (size - usal_getresid(usalp)); +} + +static int +discontinue_sony(SCSI *usalp) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\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 = 0xE2; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + + usalp->cmdname = "discontinue"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +static int +write_track_sony(SCSI *usalp, + long track /* track number 0 == new track */, + int sectype /* no sectype for Sony write track */) +{ + register struct usal_cmd *scmd = usalp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\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 = 0xF5; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdbaddr(&scmd->cdb.g1_cdb, track); + + usalp->cmdname = "write_track"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +/* XXX NOCH NICHT FERTIG */ +static int +close_track_sony(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + register struct usal_cmd *scmd = usalp->scmd; + int track = 0; + + if (!is_tao(trackp) && !is_packet(trackp)) + return (0); + + 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 = 0xF0; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdbaddr(&scmd->cdb.g1_cdb, track); +/* XXX Padding ??? (bit 0 in addr[0] / CDB[2]) */ + + usalp->cmdname = "close_track"; + + if (usal_cmd(usalp) < 0) + return (-1); + + /* + * Clear the silly "error situation" from Sony´ dummy write end + * but notify if real errors occurred. + */ + usalp->silent++; + if (test_unit_ready(usalp) < 0 && usal_sense_code(usalp) != 0xD4) { + usalp->cmdname = "close_track/test_unit_ready"; + usal_printerr(usalp); + } + usalp->silent--; + + return (0); +} + +static int +finalize_sony(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + register struct usal_cmd *scmd = usalp->scmd; + int dummy = track_base(trackp)->tracktype & TOCF_DUMMY; + + if (!is_tao(trackp) && !is_packet(trackp)) { + wait_unit_ready(usalp, 240); + return (0); + } + if (dummy) { + printf("Fixating is not possible in dummy write mode.\n"); + return (0); + } + 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 = 0xF1; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + scmd->cdb.g1_cdb.count[1] = ((track_base(trackp)->tracktype & TOCF_MULTI) ? 1 : 0); +/* XXX Padding ??? (bit 0 in addr[0] / CDB[2]) */ + + usalp->cmdname = "finalize"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +static int +flush_sony(SCSI *usalp, int track) +{ + 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 = 0xF2; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + scmd->cdb.cmd_cdb[5] = track; +/* XXX POE ??? (bit 1 in addr[0] / CDB[2]) */ +/* XXX Padding ??? (bit 0 in addr[0] / CDB[2]) */ +/* XXX Partial flush ??? (CDB[3]) */ + + usalp->cmdname = "flush"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +static int +recover_sony(SCSI *usalp, cdr_t *dp, int track) +{ + 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 = 0xF6; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + scmd->cdb.g1_cdb.addr[3] = track; + + usalp->cmdname = "recover"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +static int +set_wr_parameter_sony(SCSI *usalp, caddr_t bp, int 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 = 0xF8; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + g1_cdblen(&scmd->cdb.g1_cdb, size); + + usalp->cmdname = "set_write_parameter"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +static int +next_wr_addr_sony(SCSI *usalp, track_t *trackp, long *ap) +{ + if (next_writable_address_sony(usalp, ap, 0, 0, 0) < 0) + return (-1); + return (0); +} + +static int +reserve_track_sony(SCSI *usalp, unsigned long len) +{ + 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 = 0xF3; + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + i_to_4_byte(&scmd->cdb.g1_cdb.addr[3], len); + + usalp->cmdname = "reserve_track"; + + if (usal_cmd(usalp) < 0) + return (-1); + return (0); +} + +static int +init_sony(SCSI *usalp, cdr_t *dp) +{ + return (speed_select_sony(usalp, dp, NULL)); +} + + +#define IS(what, flag) printf(" Is %s%s\n", flag?"":"not ", what); + +static int +getdisktype_sony(SCSI *usalp, cdr_t *dp) +{ + dstat_t *dsp = dp->cdr_dstat; + long dummy; + long lst; + msf_t msf; + + char mode[256]; + struct scsi_mode_page_header *mp; + struct sony_924_mode_page_22 *xp; + + dummy = get_page22_sony(usalp, mode); + if (dummy >= 0) { + mp = (struct scsi_mode_page_header *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); + + xp = (struct sony_924_mode_page_22 *)mp; + + if (xp->disk_appl_code[0] == 0xFF) + dummy = -1; + } else { + return (drive_getdisktype(usalp, dp)); + } + + if ((dp->cdr_dstat->ds_cdrflags & RF_PRATIP) != 0 && dummy >= 0) { + + printf("ATIP info from disk:\n"); + printf(" Indicated writing power: %d\n", + (unsigned)(xp->disk_appl_code[1] & 0x70) >> 4); + IS("unrestricted", xp->disk_appl_code[2] & 0x40); + printf(" Disk application code: %d\n", xp->disk_appl_code[2] & 0x3F); + msf.msf_min = xp->lead_in_start[1]; + msf.msf_sec = xp->lead_in_start[2]; + msf.msf_frame = xp->lead_in_start[3]; + lst = msf_to_lba(msf.msf_min, msf.msf_sec, msf.msf_frame, FALSE); + if (lst < -150) { + /* + * The Sony CDU 920 seems to deliver 00:00/00 for + * lead-in start time, dont use it. + */ + printf(" ATIP start of lead in: %ld (%02d:%02d/%02d)\n", + msf_to_lba(msf.msf_min, msf.msf_sec, msf.msf_frame, FALSE), + msf.msf_min, msf.msf_sec, msf.msf_frame); + } + msf.msf_min = xp->last_start_time[1]; + msf.msf_sec = xp->last_start_time[2]; + msf.msf_frame = xp->last_start_time[3]; + printf(" ATIP start of lead out: %ld (%02d:%02d/%02d)\n", + msf_to_lba(msf.msf_min, msf.msf_sec, msf.msf_frame, TRUE), + msf.msf_min, msf.msf_sec, msf.msf_frame); + if (lst < -150) { + /* + * The Sony CDU 920 seems to deliver 00:00/00 for + * lead-in start time, dont use it. + */ + msf.msf_min = xp->lead_in_start[1]; + msf.msf_sec = xp->lead_in_start[2]; + msf.msf_frame = xp->lead_in_start[3]; + pr_manufacturer(&msf, + FALSE, /* Always not erasable */ + (xp->disk_appl_code[2] & 0x40) != 0); + } + } + if (dummy >= 0) + di_to_dstat_sony(xp, dsp); + return (drive_getdisktype(usalp, dp)); +} + +static void +di_to_dstat_sony(struct sony_924_mode_page_22 *dip, dstat_t *dsp) +{ + msf_t msf; + + dsp->ds_diskid = a_to_u_4_byte(dip->disk_id_code); +#ifdef PROTOTYPES + if (dsp->ds_diskid != 0xFFFFFFFFUL) +#else + if (dsp->ds_diskid != (Ulong)0xFFFFFFFF) +#endif + dsp->ds_flags |= DSF_DID_V; + dsp->ds_diskstat = (dip->disk_status >> 6) & 0x03; +#ifdef XXX + /* + * There seems to be no MMC equivalent... + */ + dsp->ds_sessstat = dip->sess_status; +#endif + + dsp->ds_maxblocks = msf_to_lba(dip->last_start_time[1], + dip->last_start_time[2], + dip->last_start_time[3], TRUE); + /* + * Check for 0xFF:0xFF/0xFF which is an indicator for a complete disk + */ + if (dsp->ds_maxblocks == 716730) + dsp->ds_maxblocks = -1L; + + if (dsp->ds_first_leadin == 0) { + dsp->ds_first_leadin = msf_to_lba(dip->lead_in_start[1], + dip->lead_in_start[2], + dip->lead_in_start[3], FALSE); + /* + * Check for illegal values (> 0) + * or for empty field (-150) with CDU-920. + */ + if (dsp->ds_first_leadin > 0 || dsp->ds_first_leadin == -150) + dsp->ds_first_leadin = 0; + } + + if (dsp->ds_last_leadout == 0 && dsp->ds_maxblocks >= 0) + dsp->ds_last_leadout = dsp->ds_maxblocks; + + msf.msf_min = dip->lead_in_start[1]; + msf.msf_sec = dip->lead_in_start[2]; + msf.msf_frame = dip->lead_in_start[3]; + dsp->ds_maxrblocks = disk_rcap(&msf, dsp->ds_maxblocks, + FALSE, /* Always not erasable */ + (dip->disk_appl_code[2] & 0x40) != 0); +} + + +int sony_speeds[] = { + -1, /* Speed null is not allowed */ + 0, /* Single speed */ + 1, /* Double speed */ + -1, /* Three times */ + 3, /* Quad speed */ +}; + +static int +speed_select_sony(SCSI *usalp, cdr_t *dp, int *speedp) +{ + struct cdd_52x_mode_data md; + int count; + int err; + int speed = 1; + BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0; + + if (speedp) { + speed = *speedp; + if (speed < 1 || speed > 4 || sony_speeds[speed] < 0) + return (-1); + } + + fillbytes((caddr_t)&md, sizeof (md), '\0'); + + count = sizeof (struct scsi_mode_header) + + sizeof (struct sony_924_mode_page_20); + + md.pagex.page_s20.p_code = 0x20; + md.pagex.page_s20.p_len = 0x06; + md.pagex.page_s20.speudo = dummy?1:0; + + /* + * Set Cue sheet option. This is documented for the 924 and + * seems to be supported for the 948 too. + */ + md.pagex.page_s20.cue_sheet_opt = 0x03; + + err = mode_select(usalp, (Uchar *)&md, count, 0, 1); + if (err < 0) + return (err); + + if (speedp == 0) + return (0); + + fillbytes((caddr_t)&md, sizeof (md), '\0'); + + count = sizeof (struct scsi_mode_header) + + sizeof (struct sony_924_mode_page_31); + + md.pagex.page_s31.p_code = 0x31; + md.pagex.page_s31.p_len = 0x02; + md.pagex.page_s31.speed = sony_speeds[speed]; + + return (mode_select(usalp, (Uchar *)&md, count, 0, 1)); +} + +static int +next_writable_address_sony(SCSI *usalp, long *ap, int track, int sectype, + int tracktype) +{ + struct scsi_mode_page_header *mp; + char mode[256]; + int len = 0x30; + int page = 0x23; + struct sony_924_mode_page_23 *xp; + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + inc_verbose(); + if (!get_mode_params(usalp, page, "CD track information", + (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) { + dec_verbose(); + return (-1); + } + dec_verbose(); + if (len == 0) + return (-1); + + mp = (struct scsi_mode_page_header *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); + + + xp = (struct sony_924_mode_page_23 *)mp; + +#ifdef SONY_DEBUG + print_sony_mp23(xp, len); +#endif + if (ap) + *ap = a_to_4_byte(xp->next_recordable_addr); + return (0); +} + + +static int +new_track_sony(SCSI *usalp, int track, int sectype, int tracktype) +{ + struct scsi_mode_page_header *mp; + char mode[256]; + int len = 0x30; + int page = 0x23; + struct sony_924_mode_page_23 *xp; + int i; + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + get_page22_sony(usalp, mode); + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + inc_verbose(); + if (!get_mode_params(usalp, page, "CD track information", + (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) { + dec_verbose(); + return (-1); + } + dec_verbose(); + if (len == 0) + return (-1); + + mp = (struct scsi_mode_page_header *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); + + + xp = (struct sony_924_mode_page_23 *)mp; + +#ifdef SONY_DEBUG + print_sony_mp23(xp, len); +#endif + + xp->write_method = 0; /* Track at one recording */ + + if (sectype & ST_AUDIOMASK) { + xp->data_form = (sectype & ST_MASK) == ST_AUDIO_PRE ? 0x02 : 0x00; + } else { + if (tracktype == TOC_ROM) { + xp->data_form = (sectype & ST_MASK) == ST_ROM_MODE1 ? 0x10 : 0x11; + } else if (tracktype == TOC_XA1) { + xp->data_form = 0x12; + } else if (tracktype == TOC_XA2) { + xp->data_form = 0x12; + } else if (tracktype == TOC_CDI) { + xp->data_form = 0x12; + } + } + + ((struct scsi_modesel_header *)mode)->sense_data_len = 0; + ((struct scsi_modesel_header *)mode)->res2 = 0; + + i = ((struct scsi_mode_header *)mode)->blockdesc_len; + if (i > 0) { + i_to_3_byte( + ((struct scsi_mode_data *)mode)->blockdesc.nlblock, + 0); + } + + if (mode_select(usalp, (Uchar *)mode, len, 0, usalp->inq->data_format >= 2) < 0) { + return (-1); + } + + return (0); +} + +static int +open_track_sony(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + 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); + } + + if (select_secsize(usalp, trackp->secsize) < 0) + return (-1); + + if (new_track_sony(usalp, trackp->trackno, trackp->sectype, trackp->tracktype & TOC_MASK) < 0) + return (-1); + + if (write_track_sony(usalp, 0L, trackp->sectype) < 0) + return (-1); + + return (0); +} + +static int +open_session_sony(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + struct scsi_mode_page_header *mp; + char mode[256]; + int i; + int len = 0x30; + struct sony_924_mode_page_22 *xp; + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + if ((len = get_page22_sony(usalp, mode)) < 0) + return (-1); + + mp = (struct scsi_mode_page_header *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); + + xp = (struct sony_924_mode_page_22 *)mp; + + xp->disk_type = toc2sess[track_base(trackp)->tracktype & TOC_MASK]; + + if (is_tao(track_base(trackp))) { +#ifdef __needed__ + if ((track_base(trackp)->tracktype & TOC_MASK) == TOC_DA) + xp->disk_style = 0x80; + else + xp->disk_style = 0xC0; +#endif + } else if (is_sao(track_base(trackp))) { + /* + * We may only change this value if the disk is empty. + * i.e. when disk_status & 0xC0 == 0x00 + */ + if ((xp->disk_status & 0xC0) != 0) { + if (xp->disk_style != 0x00) + errmsgno(EX_BAD, "Cannot change disk stile for recorded disk.\n"); + } + xp->disk_style = 0x00; + } + + ((struct scsi_modesel_header *)mode)->sense_data_len = 0; + ((struct scsi_modesel_header *)mode)->res2 = 0; + + i = ((struct scsi_mode_header *)mode)->blockdesc_len; + if (i > 0) { + i_to_3_byte( + ((struct scsi_mode_data *)mode)->blockdesc.nlblock, + 0); + } + + if (mode_select(usalp, (Uchar *)mode, len, 0, usalp->inq->data_format >= 2) < 0) { + return (-1); + } +/* + * XXX set write parameter für SAO mit Multi Session (948 only?) + * XXX set_wr_parameter_sony(usalp, bp, size); + */ + return (0); +} + +static int +abort_session_sony(SCSI *usalp, cdr_t *dp) +{ + return (discontinue_sony(usalp)); +} + +static int +get_page22_sony(SCSI *usalp, char *mode) +{ + struct scsi_mode_page_header *mp; + int len = 0x30; + int page = 0x22; + struct sony_924_mode_page_22 *xp; + + fillbytes((caddr_t)mode, sizeof (mode), '\0'); + + inc_verbose(); + if (!get_mode_params(usalp, page, "CD disk information", + (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) { + dec_verbose(); + return (-1); + } + dec_verbose(); + if (len == 0) + return (-1); + + mp = (struct scsi_mode_page_header *) + (mode + sizeof (struct scsi_mode_header) + + ((struct scsi_mode_header *)mode)->blockdesc_len); + + xp = (struct sony_924_mode_page_22 *)mp; + +#ifdef SONY_DEBUG + print_sony_mp22(xp, len); +#endif + return (len); +} + +/*--------------------------------------------------------------------------*/ + +static Uchar db2df[] = { + 0x01, /* 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 */ + 0x11, /* 8 2048 bytes Mode 1 (ISO/IEC 10149) */ + 0xFF, /* 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_sony(track_t *trackp, void *vcuep, BOOL needgap) +{ + int tracks = trackp->tracks; + int i; + struct sony_cue **cuep = vcuep; + struct sony_cue *cue; + struct sony_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; + df = db2df[trackp[i].dbtype & 0x0F]; + +#ifdef __supported__ + 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); + } + } +#endif + if (i == 0) { /* Lead in */ + df &= ~7; + if (trackp[0].flags & TI_TEXT) /* CD-Text in Lead-in*/ + df |= 0xC0; + 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; + df = db2df[trackp[tracks+1].dbtype & 0x0F]; + df &= ~7; + 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 sony_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; + if (tno <= 99) + cp->cs_tno = to_bcd(tno); + else + cp->cs_tno = tno; + cp->cs_index = to_bcd(idx); + cp->cs_dataform = dataform; + cp->cs_zero = scms; + cp->cs_min = to_bcd(mp->msf_min); + cp->cs_sec = to_bcd(mp->msf_sec); + cp->cs_frame = to_bcd(mp->msf_frame); +} + +static int +send_cue_sony(SCSI *usalp, cdr_t *dp, track_t *trackp) +{ + struct sony_cue *cp; + int ncue; + int ret; + Uint i; + struct timeval starttime; + struct timeval stoptime; + int disktype; + + disktype = toc2sess[track_base(trackp)->tracktype & TOC_MASK]; + + 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); + + starttime.tv_sec = 0; + starttime.tv_usec = 0; + stoptime = starttime; + gettimeofday(&starttime, (struct timezone *)0); + + usalp->silent++; + ret = write_start_sony(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 = write_start_sony(usalp, (caddr_t)cp, ncue*8); + free(cp); + } + if (ret >= 0 && lverbose) { + gettimeofday(&stoptime, (struct timezone *)0); + prtimediff("Write Lead-in time: ", &starttime, &stoptime); + } + return (ret); +} + +static int +write_leadin_sony(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 (trackp[0].flags & TI_TEXT) { + if (dp->cdr_speeddef != 4) { + errmsgno(EX_BAD, + "The CDU-924 does not support CD-Text, disabling.\n"); + + trackp[0].flags &= ~TI_TEXT; + } + } + if ((*dp->cdr_send_cue)(usalp, dp, trackp) < 0) { + errmsgno(EX_BAD, "Cannot send CUE sheet.\n"); + return (-1); + } + + if (trackp[0].flags & TI_TEXT) { + startsec = dp->cdr_dstat->ds_first_leadin; + printf("SAO startsec: %ld\n", startsec); + } else { + 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; + } + } + return (0); +} + +/*--------------------------------------------------------------------------*/ + +static const char *sd_cdu_924_error_str[] = { + + "\200\000write complete", /* 80 00 */ + "\201\000logical unit is reserved", /* 81 00 */ + "\205\000audio address not valid", /* 85 00 */ + "\210\000illegal cue sheet", /* 88 00 */ + "\211\000inappropriate command", /* 89 00 */ + + "\266\000media load mechanism failed", /* B6 00 */ + "\271\000audio play operation aborted", /* B9 00 */ + "\277\000buffer overflow for read all subcodes command", /* BF 00 */ + "\300\000unrecordable disk", /* C0 00 */ + "\301\000illegal track status", /* C1 00 */ + "\302\000reserved track present", /* C2 00 */ + "\303\000buffer data size error", /* C3 00 */ + "\304\001illegal data form for reserve track command", /* C4 01 */ + "\304\002unable to reserve track, because track mode has been changed", /* C4 02 */ + "\305\000buffer error during at once recording", /* C5 00 */ + "\306\001unwritten area encountered", /* C6 01 */ + "\306\002link blocks encountered", /* C6 02 */ + "\306\003nonexistent block encountered", /* C6 03 */ + "\307\000disk style mismatch", /* C7 00 */ + "\310\000no table of contents", /* C8 00 */ + "\311\000illegal block length for write command", /* C9 00 */ + "\312\000power calibration error", /* CA 00 */ + "\313\000write error", /* CB 00 */ + "\313\001write error track recovered", /* CB 01 */ + "\314\000not enough space", /* CC 00 */ + "\315\000no track present to finalize", /* CD 00 */ + "\316\000unrecoverable track descriptor encountered", /* CE 00 */ + "\317\000damaged track present", /* CF 00 */ + "\320\000pma area full", /* D0 00 */ + "\321\000pca area full", /* D1 00 */ + "\322\000unrecoverable damaged track cause too small writing area", /* D2 00 */ + "\323\000no bar code", /* D3 00 */ + "\323\001not enough bar code margin", /* D3 01 */ + "\323\002no bar code start pattern", /* D3 02 */ + "\323\003illegal bar code length", /* D3 03 */ + "\323\004illegal bar code format", /* D3 04 */ + "\324\000exit from pseudo track at once recording", /* D4 00 */ + NULL +}; + +static int +sony_attach(SCSI *usalp, cdr_t *dp) +{ + if (usalp->inq != NULL) { + if (strbeg("CD-R CDU94", usalp->inq->prod_ident)) { + dp->cdr_speeddef = 4; + } + } + usal_setnonstderrs(usalp, sd_cdu_924_error_str); + return (0); +} + +#ifdef SONY_DEBUG +static void +print_sony_mp22(struct sony_924_mode_page_22 *xp, int len) +{ + printf("disk style: %X\n", xp->disk_style); + printf("disk type: %X\n", xp->disk_type); + printf("first track: %X\n", xp->first_track); + printf("last track: %X\n", xp->last_track); + printf("numsess: %X\n", xp->numsess); + printf("disk appl code: %lX\n", a_to_u_4_byte(xp->disk_appl_code)); + printf("last start time: %lX\n", a_to_u_4_byte(xp->last_start_time)); + printf("disk status: %X\n", xp->disk_status); + printf("num valid nra: %X\n", xp->num_valid_nra); + printf("track info track: %X\n", xp->track_info_track); + printf("post gap: %X\n", xp->post_gap); + printf("disk id code: %lX\n", a_to_u_4_byte(xp->disk_id_code)); + printf("lead in start: %lX\n", a_to_u_4_byte(xp->lead_in_start)); +} + +static void +print_sony_mp23(struct sony_924_mode_page_23 *xp, int len) +{ + printf("len: %d\n", len); + + printf("track num: %X\n", xp->track_num); + printf("data form: %X\n", xp->data_form); + printf("write method: %X\n", xp->write_method); + printf("session: %X\n", xp->session); + printf("track status: %X\n", xp->track_status); + +/* + * XXX Check for signed/unsigned a_to_*() conversion. + */ + printf("start lba: %lX\n", a_to_4_byte(xp->start_lba)); + printf("next recordable addr: %lX\n", a_to_4_byte(xp->next_recordable_addr)); + printf("blank area cap: %lX\n", a_to_u_4_byte(xp->blank_area_cap)); + printf("fixed packet size: %lX\n", a_to_u_4_byte(xp->fixed_packet_size)); + printf("starting msf: %lX\n", a_to_u_4_byte(xp->starting_msf)); + printf("ending msf: %lX\n", a_to_u_4_byte(xp->ending_msf)); + printf("next rec time: %lX\n", a_to_u_4_byte(xp->next_rec_time)); +} +#endif + +static int +buf_cap_sony(SCSI *usalp, long *sp, long *fp) +{ + char resp[8]; + 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 = 0xEC; /* Read buffer cap */ + scmd->cdb.g1_cdb.lun = usal_lun(usalp); + + usalp->cmdname = "read buffer cap sony"; + + if (usal_cmd(usalp) < 0) + return (-1); + + bufsize = a_to_u_3_byte(&resp[1]); + freespace = a_to_u_3_byte(&resp[5]); + 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); +} |