diff -rupN cam.orig/freebsd_dvd_rw_utils.h cam/freebsd_dvd_rw_utils.h --- /dev/null 1969-12-31 19:00:00.000000000 -0500 +++ src/cam/freebsd_dvd_rw_utils.h 2008-01-24 16:52:25.000000000 -0500 @@ -0,0 +1,49 @@ +// +// This is part of dvd+rw-tools by Andy Polyakov +// +// Use-it-on-your-own-risk, GPL bless... +// +// For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/ +// + +#ifndef FREEBSD_DVD_RW_UTILS_H +#define FREEBSD_DVD_RW_UTILS_H + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "cam-cdrom.h" + +#define DRIVE_CDROM_CAPS_DVDRW 1 +#define DRIVE_CDROM_CAPS_DVDPLUSR 2 +#define DRIVE_CDROM_CAPS_DVDPLUSRW 4 +#define DRIVE_CDROM_CAPS_DVDPLUSRWDL 8 +#define DRIVE_CDROM_CAPS_DVDPLUSRDL 16 +#define DRIVE_CDROM_CAPS_BDROM 32 +#define DRIVE_CDROM_CAPS_BDR 64 +#define DRIVE_CDROM_CAPS_BDRE 128 +#define DRIVE_CDROM_CAPS_HDDVDROM 256 +#define DRIVE_CDROM_CAPS_HDDVDR 512 +#define DRIVE_CDROM_CAPS_HDDVDRW 1024 + +int brasero_cdrom_get_dvd_r_rw_profile (BRASEROCDROM *cdrom); +int brasero_cdrom_get_read_write_speed (BRASEROCDROM *cdrom, int *read_speed, int *write_speed, char **write_speeds); +int brasero_cdrom_get_disc_capacity_for_type (BRASEROCDROM *cdrom, int type, guint64 *capacity); +int brasero_cdrom_get_disc_type (BRASEROCDROM *cdrom); +int brasero_cdrom_read_disc_information_std (BRASEROCDROM *cdrom, unsigned char *buf); +int brasero_cdrom_disc_is_appendable (BRASEROCDROM *cdrom); +int brasero_cdrom_disc_is_rewritable (BRASEROCDROM *cdrom); +int brasero_cdrom_read_atip (BRASEROCDROM *cdrom, unsigned char **buf); +int brasero_cdrom_read_format_capacities (BRASEROCDROM *cdrom, unsigned char **buf); +int brasero_cdrom_get_performance_wrt_spd_desc (BRASEROCDROM *cdrom, unsigned char **buf); +int brasero_cdrom_get_configuration_feature (BRASEROCDROM *cdrom, int feature, unsigned char **buf); +int brasero_cdrom_read_track_info (BRASEROCDROM *cdrom, int track_num, unsigned char *buf, int size); +int brasero_cdrom_read_toc_raw (BRASEROCDROM *cdrom, int track_num, unsigned char **buf); +int brasero_cdrom_read_toc_formatted (BRASEROCDROM *cdrom, int track_num, unsigned char **buf); +int brasero_read_disc_information_std (BRASEROCDROM *cdrom, unsigned char *buf); +int brasero_cdrom_read_format_capacities (BRASEROCDROM *cdrom, unsigned char **buf); + +#endif /* FREEBSD_DVD_RW_UTILS_H */ --- /dev/null 2008-02-03 02:26:39.000000000 -0500 +++ src/cam/cam-cdrom.h 2008-02-03 11:32:23.000000000 -0500 @@ -0,0 +1,68 @@ +/*************************************************************************** + * CVSID: $Id$ + * + * hfp-cdrom.h : SCSI CD-ROM abstraction layer + * + * Copyright (C) 2006 Jean-Yves Lefort + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + **************************************************************************/ + +#ifndef _BRASERO_CDROM_H +#define _BRASERO_CDROM_H + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include + +typedef struct _BRASEROCDROM BRASEROCDROM; + +typedef enum +{ + BRASERO_CDROM_DIRECTION_NONE, + BRASERO_CDROM_DIRECTION_IN, + BRASERO_CDROM_DIRECTION_OUT +} BRASEROCDROMDirection; + +/* ATAPI/SCSI commands */ +enum +{ + BRASERO_CDROM_TEST_UNIT_READY = 0x00, + BRASERO_CDROM_GET_EVENT_STATUS_NOTIFICATION = 0x4a, + BRASERO_CDROM_MODE_SENSE_BIG = 0x5a +}; + +BRASEROCDROM *brasero_cdrom_new (const char *path); + +gboolean brasero_cdrom_send_ccb (BRASEROCDROM *cdrom, + const char *ccb, + int ccb_len, + BRASEROCDROMDirection direction, + void *data, + int len, + char **err); + +gboolean brasero_cdrom_test_unit_ready (BRASEROCDROM *cdrom); + +int brasero_cdrom_get_fd (BRASEROCDROM *cdrom); + +void brasero_cdrom_free (BRASEROCDROM *cdrom); + +#endif /* _BRASERO_CDROM_H */ --- /dev/null 2008-02-03 13:11:45.000000000 -0500 +++ src/cam/cam-cdrom.c 2008-02-03 13:24:53.000000000 -0500 @@ -0,0 +1,156 @@ +/*************************************************************************** + * CVSID: $Id$ + * + * cam-cdrom.c : SCSI CD-ROM abstraction layer + * + * Copyright (C) 2006 Jean-Yves Lefort + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + **************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cam-cdrom.h" + +struct _BRASEROCDROM +{ + struct cam_device *cam; /* for SCSI drives */ + int fd; +}; + +BRASEROCDROM * +brasero_cdrom_new (const char *path) +{ + BRASEROCDROM *cdrom = NULL; + struct cam_device *cam; + int fd; + + g_assert(path != NULL); + + /* brasero_open_device() fails unless we use O_RDWR */ + cam = cam_open_device(path, O_RDWR); + fd = open(path, O_RDONLY | O_NONBLOCK); + if (cam && fd > -1) + { + cdrom = g_new0(BRASEROCDROM, 1); + cdrom->cam = cam; + cdrom->fd = fd; + } + + return cdrom; +} + +gboolean +brasero_cdrom_send_ccb (BRASEROCDROM *cdrom, + const char *ccb, + int ccb_len, + BRASEROCDROMDirection direction, + void *data, + int len, + char **err) +{ + int timeout; + + g_assert(cdrom != NULL); + g_assert(ccb != NULL); + g_assert(direction == BRASERO_CDROM_DIRECTION_NONE + || direction == BRASERO_CDROM_DIRECTION_IN + || direction == BRASERO_CDROM_DIRECTION_OUT); + g_assert(direction == BRASERO_CDROM_DIRECTION_NONE || data != NULL); + + timeout = 10; + + union ccb cam_ccb; + static int scsi_direction[] = { CAM_DIR_NONE, CAM_DIR_IN, CAM_DIR_OUT }; + + memset(&cam_ccb, 0, sizeof(cam_ccb)); + + cam_ccb.ccb_h.path_id = cdrom->cam->path_id; + cam_ccb.ccb_h.target_id = cdrom->cam->target_id; + cam_ccb.ccb_h.target_lun = cdrom->cam->target_lun; + + cam_fill_csio(&cam_ccb.csio, + 1, + NULL, + scsi_direction[direction], + MSG_SIMPLE_Q_TAG, + data, + len, + sizeof(cam_ccb.csio.sense_data), + ccb_len, + timeout * 1000); + + memcpy(cam_ccb.csio.cdb_io.cdb_bytes, ccb, 16); + + if (cam_send_ccb(cdrom->cam, &cam_ccb) == -1) + { + if (err) + *err = g_strdup_printf("cam_send_ccb() failure: %s", g_strerror(errno)); + } + if ((cam_ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) + { + if (err) + *err = g_strdup_printf("CCB request failed with status %i", cam_ccb.ccb_h.status & CAM_STATUS_MASK); + return FALSE; + } + + return TRUE; +} + +gboolean +brasero_cdrom_test_unit_ready (BRASEROCDROM *cdrom) +{ + static char ccb[16] = { BRASERO_CDROM_TEST_UNIT_READY }; + + g_assert(cdrom != NULL); + + return brasero_cdrom_send_ccb(cdrom, ccb, 6, BRASERO_CDROM_DIRECTION_NONE, NULL, 0, NULL); +} + +int +brasero_cdrom_get_fd (BRASEROCDROM *cdrom) +{ + g_assert(cdrom != NULL); + + return (cdrom->fd); +} + +void +brasero_cdrom_free (BRASEROCDROM *cdrom) +{ + g_assert(cdrom != NULL); + + if (cdrom->cam) + cam_close_device(cdrom->cam); + + close(cdrom->fd); + + g_free(cdrom); +} --- /dev/null 2008-02-03 13:11:45.000000000 -0500 +++ src/cam/freebsd_dvd_rw_utils.c 2008-02-03 13:30:36.000000000 -0500 @@ -0,0 +1,1075 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + * + * This is part of dvd+rw-tools by Andy Polyakov + * + * Use-it-on-your-own-risk, GPL bless... + * + * For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/ +*/ + +#include +#include +#include +#include + +#include + +#include "freebsd_dvd_rw_utils.h" + +typedef enum { + NONE = BRASERO_CDROM_DIRECTION_NONE, + READ = BRASERO_CDROM_DIRECTION_IN, + WRITE = BRASERO_CDROM_DIRECTION_OUT +} Direction; + +typedef struct ScsiCommand ScsiCommand; + +struct ScsiCommand { + BRASEROCDROM *cdrom; + char ccb[16]; + int len; +}; + +static ScsiCommand * +scsi_command_new_from_cdrom (BRASEROCDROM *cdrom) +{ + ScsiCommand *cmd; + + cmd = g_new0 (ScsiCommand, 1); + cmd->cdrom = cdrom; + + return cmd; +} + +static void +scsi_command_free (ScsiCommand * cmd) +{ + free (cmd); +} + +static void +scsi_command_init (ScsiCommand * cmd, size_t i, int arg) +{ + cmd->ccb[i] = arg; + if (i == 0 || i >= cmd->len) + cmd->len = i + 1; +} + +static int +scsi_command_transport (ScsiCommand * cmd, Direction dir, void *buf, + size_t sz) +{ + if (brasero_cdrom_send_ccb(cmd->cdrom, cmd->ccb, cmd->len, dir, buf, sz, NULL)) + return 0; + else + return -1; +} + +int +brasero_cdrom_get_dvd_r_rw_profile (BRASEROCDROM *cdrom) +{ + ScsiCommand *cmd; + int retval = 0; + unsigned char page[20]; + unsigned char *list; + int i, len; + + g_return_val_if_fail (cdrom != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + scsi_command_init (cmd, 0, 0x46); + scsi_command_init (cmd, 1, 2); + scsi_command_init (cmd, 8, 8); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, page, 8)) { + /* GET CONFIGURATION failed */ + scsi_command_free (cmd); + return -1; + } + + /* See if it's 2 gen drive by checking if DVD+R profile is an option */ + len = 4 + (page[0] << 24 | page[1] << 16 | page[2] << 8 | page[3]); + if (len > 264) { + scsi_command_free (cmd); + /* insane profile list length */ + return -1; + } + + list = g_new (unsigned char, len); + + scsi_command_init (cmd, 0, 0x46); + scsi_command_init (cmd, 1, 2); + scsi_command_init (cmd, 7, len >> 8); + scsi_command_init (cmd, 8, len); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, list, len)) { + /* GET CONFIGURATION failed */ + scsi_command_free (cmd); + free (list); + return -1; + } + + for (i = 12; i < list[11]; i += 4) { + int profile = (list[i] << 8 | list[i + 1]); + /* 0x13: DVD-RW Restricted Overwrite + * 0x14: DVD-RW Sequential + * 0x1B: DVD+R + * 0x1A: DVD+RW + * 0x2A: DVD+RW DL + * 0x2B: DVD+R DL + * 0x40: BD-ROM + * 0x41: BD-R SRM + * 0x42: BD-R RRM + * 0x43: BD-RE + * 0x50: HD DVD-ROM + * 0x51: HD DVD-R + * 0x52: HD DVD-Rewritable + */ + + switch (profile) { + case 0x13: + case 0x14: + retval |= DRIVE_CDROM_CAPS_DVDRW; + break; + case 0x1B: + retval |= DRIVE_CDROM_CAPS_DVDPLUSR; + break; + case 0x1A: + retval |= DRIVE_CDROM_CAPS_DVDPLUSRW; + break; + case 0x2A: + retval |= DRIVE_CDROM_CAPS_DVDPLUSRWDL; + break; + case 0x2B: + retval |= DRIVE_CDROM_CAPS_DVDPLUSRDL; + break; + case 0x40: + retval |= DRIVE_CDROM_CAPS_BDROM; + break; + case 0x41: + case 0x42: + retval |= DRIVE_CDROM_CAPS_BDR; + break; + case 0x43: + retval |= DRIVE_CDROM_CAPS_BDRE; + break; + case 0x50: + retval |= DRIVE_CDROM_CAPS_HDDVDROM; + break; + case 0x51: + retval |= DRIVE_CDROM_CAPS_HDDVDR; + break; + case 0x52: + retval |= DRIVE_CDROM_CAPS_HDDVDRW; + break; + default: + break; + } + } + + scsi_command_free (cmd); + free (list); + + return retval; + +} + +static unsigned char * +pull_page2a_from_cdrom (BRASEROCDROM *cdrom) +{ + ScsiCommand *cmd; + unsigned char header[12], *page2A; + unsigned int len, bdlen; + + g_return_val_if_fail (cdrom != NULL, NULL); + + cmd = scsi_command_new_from_cdrom (cdrom); + + scsi_command_init (cmd, 0, 0x5A); /* MODE SENSE */ + scsi_command_init (cmd, 1, 0x08); /* Disable Block Descriptors */ + scsi_command_init (cmd, 2, 0x2A); /* Capabilities and Mechanical Status */ + scsi_command_init (cmd, 8, sizeof (header)); /* header only to start with */ + scsi_command_init (cmd, 9, 0); + + if (scsi_command_transport (cmd, READ, header, sizeof (header))) { + /* MODE SENSE failed */ + scsi_command_free (cmd); + return NULL; + } + + len = (header[0] << 8 | header[1]) + 2; + bdlen = header[6] << 8 | header[7]; + + /* should never happen as we set "DBD" above */ + if (bdlen) { + if (len < (8 + bdlen + 30)) { + /* LUN impossible to bear with */ + scsi_command_free (cmd); + return NULL; + } + } else if (len < (8 + 2 + (unsigned int) header[9])) { + /* SANYO does this. */ + len = 8 + 2 + header[9]; + } + + page2A = g_new (unsigned char, len); + if (page2A == NULL) { + /* ENOMEM */ + scsi_command_free (cmd); + return NULL; + } + + scsi_command_init (cmd, 0, 0x5A); /* MODE SENSE */ + scsi_command_init (cmd, 1, 0x08); /* Disable Block Descriptors */ + scsi_command_init (cmd, 2, 0x2A); /* Capabilities and Mechanical Status */ + scsi_command_init (cmd, 7, len >> 8); + scsi_command_init (cmd, 8, len); /* Real length */ + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, page2A, len)) { + /* MODE SENSE failed */ + scsi_command_free (cmd); + free (page2A); + return NULL; + } + + scsi_command_free (cmd); + + len -= 2; + /* paranoia */ + if (len < ((unsigned int) page2A[0] << 8 | page2A[1])) { + page2A[0] = len >> 8; + page2A[1] = len; + } + + return page2A; +} + +static int +int_compare (const void *a, const void *b) +{ + /* descending order */ + return *((int *) b) - *((int *) a); +} + +/* gets the list of supported write speeds. in the event + * that anything goes wrong, returns NULL. + */ +static char * +get_write_speeds (const unsigned char *p, int length, int max_speed) +{ + char *result, *str; + int nr_records; + int *tmpspeeds; + int i, j; + + result = NULL; + + /* paranoia */ + if (length < 32) + return NULL; + + nr_records = p[30] << 8 | p[31]; + + /* paranoia */ + if (length < 32 + 4 * nr_records) + return NULL; + + tmpspeeds = g_new (int, nr_records); + + for (i = 0; i < nr_records; i++) + { + tmpspeeds[i] = p[4*i + 34] << 8 | p[4*i + 35]; + + /* i'm not sure how likely this is to show up, but it's + * definitely wrong. if we see it, abort. + */ + if (tmpspeeds[i] == 0) + goto free_tmpspeeds; + } + + /* sort */ + qsort (tmpspeeds, nr_records, sizeof (int), int_compare); + + /* uniq */ + for (i = j = 0; i < nr_records; i++) + { + tmpspeeds[j] = tmpspeeds[i]; + + /* make sure we don't look past the end of the array */ + if (i >= (nr_records - 1) || tmpspeeds[i+1] != tmpspeeds[i]) + j++; + } + + /* j is now the number of unique entries in the array */ + if (j == 0) + /* no entries? this isn't right. */ + goto free_tmpspeeds; + + /* sanity check: the first item in the descending order + * list ought to be the highest speed as detected through + * other means + */ + if (tmpspeeds[0] != max_speed) + /* sanity check failed. */ + goto free_tmpspeeds; + + /* our values are 16-bit. 8 bytes per value + * is more than enough including space for + * ',' and '\0'. we know j is not zero. + */ + result = str = g_new (char, 8 * j); + + for (i = 0; i < j; i++) + { + if (i > 0) + *(str++) = ','; + + str += sprintf (str, "%d", tmpspeeds[i]); + } + +free_tmpspeeds: + free (tmpspeeds); + + return result; +} + +int +brasero_cdrom_get_read_write_speed (BRASEROCDROM *cdrom, int *read_speed, int *write_speed, char **write_speeds) +{ + unsigned char *page2A; + int len, hlen; + unsigned char *p; + + g_return_val_if_fail (cdrom != NULL, -1); + + *read_speed = 0; + *write_speed = 0; + *write_speeds = NULL; + + page2A = pull_page2a_from_cdrom (cdrom); + if (page2A == NULL) { + printf ("Failed to get Page 2A\n"); + /* Failed to get Page 2A */ + return -1; + } + + len = (page2A[0] << 8 | page2A[1]) + 2; + hlen = 8 + (page2A[6] << 8 | page2A[7]); + p = page2A + hlen; + + /* Values guessed from the cd_mode_page_2A struct + * in cdrecord's libscg/scg/scsireg.h */ + if (len < (hlen + 30) || p[1] < (30 - 2)) { + /* no MMC-3 "Current Write Speed" present, + * try to use the MMC-2 one */ + if (len < (hlen + 20) || p[1] < (20 - 2)) + *write_speed = 0; + else + *write_speed = p[18] << 8 | p[19]; + } else { + *write_speed = p[28] << 8 | p[29]; + } + + if (len >= hlen+9) + *read_speed = p[8] << 8 | p[9]; + else + *read_speed = 0; + + *write_speeds = get_write_speeds (p, len, *write_speed); + + free (page2A); + + return 0; +} + + +static int +get_disc_capacity_cd (BRASEROCDROM *cdrom, guint64 *size) +{ + ScsiCommand *cmd; + int retval; + guint64 block_size; + guint64 num_blocks; + unsigned char header [8]; + + g_return_val_if_fail (cdrom != NULL, -1); + + retval = -1; + + cmd = scsi_command_new_from_cdrom (cdrom); + scsi_command_init (cmd, 0, 0x25); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, header, 8)) { + /* READ CDROM CAPACITY failed */ + goto done; + } + + num_blocks = (header [0] << 24) | (header [1] << 16) | (header [2] << 8) | header [3]; + num_blocks++; + block_size = header [4] << 24 | header [5] << 16 | header [6] << 8 | header [7]; + + if (size) { + *size = num_blocks * block_size; + } + retval = 0; + + done: + scsi_command_free (cmd); + + return retval; +} + +static int +get_disc_capacity_cdr (BRASEROCDROM *cdrom, guint64 *size) +{ + ScsiCommand *cmd; + int retval; + guint64 secs; + unsigned char toc [8]; + unsigned char *atip; + int len; + + g_return_val_if_fail (cdrom != NULL, -1); + + retval = -1; + + cmd = scsi_command_new_from_cdrom (cdrom); + /* READ_TOC */ + scsi_command_init (cmd, 0, 0x43); + /* FMT_ATIP */ + scsi_command_init (cmd, 2, 4 & 0x0F); + scsi_command_init (cmd, 6, 0); + scsi_command_init (cmd, 8, 4); + scsi_command_init (cmd, 9, 0); + + if (scsi_command_transport (cmd, READ, toc, 4)) { + /* READ TOC failed */ + goto done; + } + + len = 2 + (toc [0] << 8 | toc [1]); + + atip = g_new (unsigned char, len); + + scsi_command_init (cmd, 0, 0x43); + scsi_command_init (cmd, 2, 4 & 0x0F); + scsi_command_init (cmd, 6, 0); + scsi_command_init (cmd, 7, len >> 8); + scsi_command_init (cmd, 8, len); + scsi_command_init (cmd, 9, 0); + + if (scsi_command_transport (cmd, READ, atip, len)) { + /* READ TOC failed */ + free (atip); + goto done; + } + + secs = atip [12] * 60 + atip [13] + (atip [14] / 75 + 1); + + if (size) { + *size = (1 + secs * 7 / 48) * 1024 * 1024; + } + retval = 0; + + free (atip); + done: + scsi_command_free (cmd); + + return retval; +} + +static int +get_disc_capacity_dvdr_from_type (BRASEROCDROM *cdrom, int type, guint64 *size) +{ + ScsiCommand *cmd; + unsigned char formats [260]; + unsigned char buf [32]; + guint64 blocks; + guint64 nwa; + int i; + int len; + int obligatory; + int retval; + int next_track; + + g_return_val_if_fail (cdrom != NULL, -1); + + retval = -1; + blocks = 0; + next_track = 1; + + cmd = scsi_command_new_from_cdrom (cdrom); + + retry: + if (type == 0x1A || type == 0x14 || type == 0x13 || type == 0x12) { + + /* READ FORMAT CAPACITIES */ + scsi_command_init (cmd, 0, 0x23); + scsi_command_init (cmd, 8, 12); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, formats, 12)) { + /* READ FORMAT CAPACITIES failed */ + goto done; + } + + len = formats [3]; + if (len & 7 || len < 16) { + /* Length isn't sane */ + goto done; + } + + scsi_command_init (cmd, 0, 0x23); + scsi_command_init (cmd, 7, (4 + len) >> 8); + scsi_command_init (cmd, 8, (4 + len) & 0xFF); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, formats, 4 + len)) { + /* READ FORMAT CAPACITIES failed */ + goto done; + } + + if (len != formats [3]) { + /* Parameter length inconsistency */ + goto done; + } + } + + obligatory = 0x00; + + switch (type) { + case 0x1A: /* DVD+RW */ + obligatory = 0x26; + case 0x13: /* DVD-RW Restricted Overwrite */ + case 0x14: /* DVD-RW Sequential */ + for (i = 8, len = formats [3]; i < len; i += 8) { + if ((formats [4 + i + 4] >> 2) == obligatory) { + break; + } + } + + if (i == len) { + /* Can't find obligatory format descriptor */ + goto done; + } + + blocks = formats [4 + i + 0] << 24; + blocks |= formats [4 + i + 1] << 16; + blocks |= formats [4 + i + 2] << 8; + blocks |= formats [4 + i + 3]; + nwa = formats [4 + 5] << 16 | formats [4 + 6] << 8 | formats [4 + 7]; + if (nwa > 2048) { + blocks *= nwa / 2048; + } else if (nwa < 2048) { + blocks /= 2048 / nwa; + } + + retval = 0; + break; + + case 0x12: /* DVD-RAM */ + + blocks = formats [4 + 0] << 24; + blocks |= formats [4 + 1] << 16; + blocks |= formats [4 + 2] << 8; + blocks |= formats [4 + 3]; + nwa = formats [4 + 5] << 16 | formats [4 + 6] << 8 | formats [4 + 7]; + if (nwa > 2048) { + blocks *= nwa / 2048; + } else if (nwa < 2048) { + blocks /= 2048 / nwa; + } + + retval = 0; + break; + + case 0x11: /* DVD-R */ + case 0x1B: /* DVD+R */ + case 0x2B: /* DVD+R Double Layer */ + case 0x41: /* BD-R SRM */ + + /* READ TRACK INFORMATION */ + scsi_command_init (cmd, 0, 0x52); + scsi_command_init (cmd, 1, 1); + scsi_command_init (cmd, 4, next_track >> 8); + scsi_command_init (cmd, 5, next_track & 0xFF); + scsi_command_init (cmd, 8, sizeof (buf)); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, buf, sizeof (buf))) { + /* READ TRACK INFORMATION failed */ + if (next_track > 0) { + goto done; + } else { + next_track = 1; + goto retry; + } + } + + blocks = buf [24] << 24; + blocks |= buf [25] << 16; + blocks |= buf [26] << 8; + blocks |= buf [27]; + + retval = 0; + break; + case 0x43: /* DB-RE */ + /* Pull the formatted capacity */ + blocks = formats [4 + 0] << 24; + blocks |= formats [4 + 1] << 16; + blocks |= formats [4 + 2] << 8; + blocks |= formats [4 + 3]; + break; + default: + blocks = 0; + break; + } + + done: + scsi_command_free (cmd); + + if (size) { + *size = blocks * 2048; + } + + return retval; +} + +int +brasero_cdrom_get_disc_capacity_for_type (BRASEROCDROM *cdrom, int type, guint64 *size) +{ + int retval; + + g_return_val_if_fail (cdrom != NULL, -1); + + retval = -1; + + switch (type) { + case 0x8: + retval = get_disc_capacity_cd (cdrom, size); + break; + case 0x9: + case 0xa: + retval = get_disc_capacity_cdr (cdrom, size); + break; + case 0x10: + retval = get_disc_capacity_cd (cdrom, size); + break; + case 0x11: + case 0x13: + case 0x14: + case 0x1B: + case 0x2B: + case 0x1A: + case 0x12: + case 0x41: + case 0x43: + retval = get_disc_capacity_dvdr_from_type (cdrom, type, size); + break; + default: + retval = -1; + } + + return retval; +} + +int +brasero_cdrom_get_disc_type (BRASEROCDROM *cdrom) +{ + ScsiCommand *cmd; + int retval = -1; + unsigned char header[8]; + + g_return_val_if_fail (cdrom != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + scsi_command_init (cmd, 0, 0x46); + scsi_command_init (cmd, 1, 1); + scsi_command_init (cmd, 8, 8); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, header, 8)) { + /* GET CONFIGURATION failed */ + scsi_command_free (cmd); + return -1; + } + + retval = (header[6]<<8)|(header[7]); + + + scsi_command_free (cmd); + return retval; +} + +int +brasero_cdrom_get_configuration_feature (BRASEROCDROM *cdrom, + int feature, + unsigned char **buf) +{ + ScsiCommand *cmd; + int retval = 0; + int len; + unsigned char header[8]; + + g_return_val_if_fail (cdrom != NULL, -1); + g_return_val_if_fail (buf != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + scsi_command_init (cmd, 0, 0x46); + scsi_command_init (cmd, 1, 2); + scsi_command_init (cmd, 2, feature >> 8); + scsi_command_init (cmd, 3, feature); + scsi_command_init (cmd, 8, 8); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, header, 8)) { + scsi_command_free (cmd); + return -1; + } + + len = 4 + (header[0] << 24 | header[1] << 16 | header[2] << 8 | header[3]); + if (len > 264) { + scsi_command_free (cmd); + return -1; + } + + *buf = g_new (unsigned char, len); + + scsi_command_init (cmd, 0, 0x46); + scsi_command_init (cmd, 1, 2); + scsi_command_init (cmd, 2, feature >> 8); + scsi_command_init (cmd, 3, feature); + scsi_command_init (cmd, 7, len >> 8); + scsi_command_init (cmd, 8, len); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, *buf, len)) { + g_free (*buf); + *buf = NULL; + retval = -1; + } + + scsi_command_free (cmd); + + return retval; +} + +int +brasero_cdrom_read_disc_information_std (BRASEROCDROM *cdrom, + unsigned char *buf) +{ + ScsiCommand *cmd; + int retval = 0; + + g_return_val_if_fail (cdrom != NULL, -1); + g_return_val_if_fail (buf != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + /* see section 5.19 of MMC-3 from http://www.t10.org/drafts.htm#mmc3 */ + scsi_command_init (cmd, 0, 0x51); /* READ_DISC_INFORMATION */ + scsi_command_init (cmd, 8, 32); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, buf, 32)) { + retval = -1; + } + + scsi_command_free (cmd); + return retval; +} + +int +brasero_cdrom_read_track_info (BRASEROCDROM *cdrom, + int track_num, + unsigned char *buf, + int size) +{ + ScsiCommand *cmd; + int retval = 0; + + g_return_val_if_fail (cdrom != NULL, -1); + g_return_val_if_fail (buf != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + scsi_command_init (cmd, 0, 0x52); + scsi_command_init (cmd, 1, 1); + scsi_command_init (cmd, 4, track_num >> 8); + scsi_command_init (cmd, 5, track_num & 0xFF); + scsi_command_init (cmd, 8, size); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, buf, size)) { + retval = -1; + } + + scsi_command_free (cmd); + return retval; +} + +int +brasero_cdrom_read_toc_formatted (BRASEROCDROM *cdrom, + int track_num, + unsigned char **buf) +{ + ScsiCommand *cmd; + int retval = 0; + int len; + unsigned char header[4]; + + g_return_val_if_fail (cdrom != NULL, -1); + g_return_val_if_fail (buf != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + scsi_command_init (cmd, 0, 0x43); + scsi_command_init (cmd, 2, 0); + scsi_command_init (cmd, 6, track_num); + scsi_command_init (cmd, 8, 4); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, header, 4)) { + scsi_command_free (cmd); + *buf = NULL; + return -1; + } + + len = (header[0] << 8 | header[1]) + 2; + + *buf = g_malloc0 (len); + + scsi_command_init (cmd, 8, len); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, *buf, len)) { + g_free (*buf); + *buf = NULL; + retval = -1; + } + + scsi_command_free (cmd); + return retval; +} + +int +brasero_cdrom_read_toc_raw (BRASEROCDROM *cdrom, + int track_num, + unsigned char **buf) +{ + ScsiCommand *cmd; + int retval = 0; + int len; + unsigned char header[4]; + + g_return_val_if_fail (cdrom != NULL, -1); + g_return_val_if_fail (buf != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + scsi_command_init (cmd, 0, 0x43); + scsi_command_init (cmd, 2, 2); + scsi_command_init (cmd, 6, track_num); + scsi_command_init (cmd, 8, 4); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, header, 4)) { + scsi_command_free (cmd); + *buf = NULL; + return -1; + } + + len = (header[0] << 8 | header[1]) + 2; + + *buf = g_malloc0 (len); + + scsi_command_init (cmd, 8, len); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, *buf, len)) { + g_free (*buf); + *buf = NULL; + retval = -1; + } + + scsi_command_free (cmd); + return retval; +} + +int +brasero_cdrom_read_atip (BRASEROCDROM *cdrom, + unsigned char **buf) +{ + ScsiCommand *cmd; + int retval = 0; + int len; + unsigned char header[4]; + + g_return_val_if_fail (cdrom != NULL, -1); + g_return_val_if_fail (buf != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + scsi_command_init (cmd, 0, 0x43); + scsi_command_init (cmd, 2, 4); + scsi_command_init (cmd, 6, 0); + scsi_command_init (cmd, 8, 4); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, header, 4)) { + scsi_command_free (cmd); + *buf = NULL; + return -1; + } + + len = (header[0] << 8 | header[1]) + 2; + + *buf = g_malloc0 (len); + + scsi_command_init (cmd, 8, len); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, *buf, len)) { + g_free (*buf); + *buf = NULL; + retval = -1; + } + + scsi_command_free (cmd); + return retval; +} + +int +brasero_cdrom_read_format_capacities (BRASEROCDROM *cdrom, + unsigned char **buf) +{ + ScsiCommand *cmd; + int retval = 0; + int len; + unsigned char header[12]; + + g_return_val_if_fail (cdrom != NULL, -1); + g_return_val_if_fail (buf != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + scsi_command_init (cmd, 0, 0x23); + scsi_command_init (cmd, 8, 12); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, header, 12)) { + /* READ FORMAT CAPACITIES failed */ + return -1; + } + + len = header [3]; + if (len & 7 || len < 16) { + /* Length isn't sane */ + return -1; + } + + *buf = g_new (unsigned char, len + 4); + + scsi_command_init (cmd, 0, 0x23); + scsi_command_init (cmd, 7, (4 + len) >> 8); + scsi_command_init (cmd, 8, (4 + len) & 0xFF); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, *buf, 4 + len)) { + /* READ FORMAT CAPACITIES failed */ + g_free (*buf); + *buf = NULL; + retval = -1; + } + + return retval; +} + +int +brasero_cdrom_get_performance_wrt_spd_desc (BRASEROCDROM *cdrom, + unsigned char **buf) +{ + ScsiCommand *cmd; + int retval = 0; + int len; + int desc_num; + unsigned char header[8]; + + g_return_val_if_fail (cdrom != NULL, -1); + g_return_val_if_fail (buf != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + scsi_command_init (cmd, 0, 0xac); + scsi_command_init (cmd, 8, 0); + scsi_command_init (cmd, 9, 0); + scsi_command_init (cmd, 10, 3); + scsi_command_init (cmd, 11, 0); + if (scsi_command_transport (cmd, READ, header, 8)) { + scsi_command_free (cmd); + *buf = NULL; + return -1; + } + + len = (header[0] << 24 | header[1] << 16 | header[2] << 8 | header[3]) + 4; + if (len > 2048) { + len = 2048; + } + + desc_num = (len - 8) / 12; + + *buf = g_malloc0 (len); + + scsi_command_init (cmd, 8, desc_num >> 8); + scsi_command_init (cmd, 9, desc_num); + scsi_command_init (cmd, 11, 0); + if (scsi_command_transport (cmd, READ, *buf, len)) { + g_free (*buf); + *buf = NULL; + retval = -1; + } + + scsi_command_free (cmd); + return retval; +} + +int +brasero_cdrom_disc_is_appendable (BRASEROCDROM *cdrom) +{ + ScsiCommand *cmd; + int retval = -1; + unsigned char header[32]; + + g_return_val_if_fail (cdrom != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + /* see section 5.19 of MMC-3 from http://www.t10.org/drafts.htm#mmc3 */ + scsi_command_init (cmd, 0, 0x51); /* READ_DISC_INFORMATION */ + scsi_command_init (cmd, 8, 32); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, header, 32)) { + /* READ_DISC_INFORMATION failed */ + scsi_command_free (cmd); + return 0; + } + + retval = ((header[2]&0x03) == 0x01); + + scsi_command_free (cmd); + return retval; +} + +int +brasero_cdrom_disc_is_rewritable (BRASEROCDROM *cdrom) +{ + ScsiCommand *cmd; + int retval = -1; + unsigned char header[32]; + + g_return_val_if_fail (cdrom != NULL, -1); + + cmd = scsi_command_new_from_cdrom (cdrom); + + /* see section 5.19 of MMC-3 from http://www.t10.org/drafts.htm#mmc3 */ + scsi_command_init (cmd, 0, 0x51); /* READ_DISC_INFORMATION */ + scsi_command_init (cmd, 8, 32); + scsi_command_init (cmd, 9, 0); + if (scsi_command_transport (cmd, READ, header, 32)) { + /* READ_DISC_INFORMATION failed */ + scsi_command_free (cmd); + return 0; + } + + retval = ((header[2]&0x10) != 0); + + scsi_command_free (cmd); + return retval; +}