--- src/burn-medium-scsi.c.orig 2008-02-06 01:53:39.000000000 -0500 +++ src/burn-medium-scsi.c 2008-02-06 01:54:19.000000000 -0500 @@ -0,0 +1,2070 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * brasero + * Copyright (C) Philippe Rouquier 2007 + * + * brasero is free software. + * + * You may 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. + * + * brasero 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 brasero. If not, write to: + * The Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include +#include + +#include + +#include "burn-basics.h" +#include "burn-debug.h" +#include "burn-medium.h" +#include "scsi-mmc1.h" +#include "scsi-mmc2.h" +#include "scsi-mmc3.h" +#include "scsi-spc1.h" +#include "scsi-utils.h" +#include "scsi-mode-pages.h" +#include "scsi-status-page.h" +#include "scsi-q-subchannel.h" +#include "scsi-dvd-structures.h" +#include "burn-volume.h" +#include "brasero-ncb.h" + +const gchar *icons [] = { "gnome-dev-removable", + "gnome-dev-cdrom", + "gnome-dev-disc-cdr", + "gnome-dev-disc-cdrw", + "gnome-dev-disc-dvdrom", + "gnome-dev-disc-dvdr", + "gnome-dev-disc-dvdrw", + "gnome-dev-disc-dvdr-plus", + "gnome-dev-disc-dvdram", + NULL }; +const gchar *types [] = { N_("file"), + N_("CDROM"), + N_("CD-R"), + N_("CD-RW"), + N_("DVDROM"), + N_("DVD-R"), + N_("DVD-RW"), + N_("DVD+R"), + N_("DVD+RW"), + N_("DVD+R dual layer"), + N_("DVD+RW dual layer"), + N_("DVD-R dual layer"), + N_("DVD-RAM"), + N_("Blu-ray disc"), + N_("Writable Blu-ray disc"), + N_("Rewritable Blu-ray disc"), + NULL }; + + +typedef struct _BraseroMediumPrivate BraseroMediumPrivate; +struct _BraseroMediumPrivate +{ + gint retry_id; + + GSList * tracks; + + const gchar *type; + const gchar *icon; + + gint max_rd; + gint max_wrt; + + gint *rd_speeds; + gint *wr_speeds; + + gint64 block_num; + gint64 block_size; + + guint64 next_wr_add; + BraseroMedia info; + NautilusBurnDrive * drive; +}; + +#define BRASERO_MEDIUM_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_MEDIUM, BraseroMediumPrivate)) + +/** + * Try to open the drive exclusively but don't block; if drive can't be opened + * exclusively then retry every second until we're shut or the drive state + * changes to not busy. + * No exclusive at the moment since when the medium is mounted we can't use excl + */ + +#define OPEN_FLAGS O_RDONLY /*|O_EXCL */|O_NONBLOCK +#define BUSY_RETRY_TIME 1000 + +enum +{ + PROP_0, + PROP_DRIVE +}; + +static GObjectClass* parent_class = NULL; + +const gchar * +brasero_medium_get_type_string (BraseroMedium *medium) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + return priv->type; +} + +const gchar * +brasero_medium_get_icon (BraseroMedium *medium) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + return priv->icon; +} + +BraseroMedia +brasero_medium_get_status (BraseroMedium *medium) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + return priv->info; +} + +GSList * +brasero_medium_get_tracks (BraseroMedium *medium) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + return g_slist_copy (priv->tracks); +} + +gboolean +brasero_medium_get_last_data_track_address (BraseroMedium *medium, + gint64 *byte, + gint64 *sector) +{ + GSList *iter; + BraseroMediumPrivate *priv; + BraseroMediumTrack *track = NULL; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + + for (iter = priv->tracks; iter; iter = iter->next) { + BraseroMediumTrack *current; + + current = iter->data; + if (current->type & BRASERO_MEDIUM_TRACK_DATA) + track = current; + } + + if (!track) { + if (byte) + *byte = -1; + if (sector) + *sector = -1; + return FALSE; + } + + if (byte) + *byte = track->start * priv->block_size; + + if (sector) + *sector = track->start; + + return TRUE; +} + +gboolean +brasero_medium_get_last_data_track_space (BraseroMedium *medium, + gint64 *size, + gint64 *blocks) +{ + GSList *iter; + BraseroMediumPrivate *priv; + BraseroMediumTrack *track = NULL; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + + for (iter = priv->tracks; iter; iter = iter->next) { + BraseroMediumTrack *current; + + current = iter->data; + if (current->type & BRASERO_MEDIUM_TRACK_DATA) + track = current; + } + + if (!track) { + if (size) + *size = -1; + if (blocks) + *blocks = -1; + return FALSE; + } + + if (size) + *size = track->blocks_num * priv->block_size; + if (blocks) + *blocks = track->blocks_num; + + return TRUE; +} + +guint +brasero_medium_get_track_num (BraseroMedium *medium) +{ + guint retval = 0; + GSList *iter; + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + for (iter = priv->tracks; iter; iter = iter->next) { + BraseroMediumTrack *current; + + current = iter->data; + if (current->type & BRASERO_MEDIUM_TRACK_LEADOUT) + break; + + retval ++; + } + + return retval; +} + +static BraseroMediumTrack * +brasero_medium_get_track (BraseroMedium *medium, + guint num) +{ + guint i = 1; + GSList *iter; + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + + for (iter = priv->tracks; iter; iter = iter->next) { + BraseroMediumTrack *current; + + current = iter->data; + if (current->type == BRASERO_MEDIUM_TRACK_LEADOUT) + break; + + if (i == num) + return current; + + i++; + } + + return NULL; +} + +gboolean +brasero_medium_get_track_space (BraseroMedium *medium, + guint num, + gint64 *size, + gint64 *blocks) +{ + BraseroMediumPrivate *priv; + BraseroMediumTrack *track; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + + track = brasero_medium_get_track (medium, num); + if (!track) { + if (size) + *size = -1; + if (blocks) + *blocks = -1; + return FALSE; + } + + if (size) + *size = track->blocks_num * priv->block_size; + if (blocks) + *blocks = track->blocks_num; + + return TRUE; +} + +gboolean +brasero_medium_get_track_address (BraseroMedium *medium, + guint num, + gint64 *byte, + gint64 *sector) +{ + BraseroMediumPrivate *priv; + BraseroMediumTrack *track; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + + track = brasero_medium_get_track (medium, num); + if (!track) { + if (byte) + *byte = -1; + if (sector) + *sector = -1; + return FALSE; + } + + if (byte) + *byte = track->start * priv->block_size; + if (sector) + *sector = track->start; + + return TRUE; +} + +gint64 +brasero_medium_get_next_writable_address (BraseroMedium *medium) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + return priv->next_wr_add; +} + +gint64 +brasero_medium_get_max_write_speed (BraseroMedium *medium) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + return priv->max_wrt * 1024; +} + +/** + * NOTEs about the following functions: + * for all closed media (including ROM types) capacity == size of data and + * should be the size of all data on the disc, free space is 0 + * for all blank -R types capacity == free space and size of data == 0 + * for all multisession -R types capacity == free space since having the real + * capacity of the media would be useless as we can only use this type of media + * to append more data + * for all -RW types capacity = free space + size of data. Here they can be + * appended (use free space) or rewritten (whole capacity). + * + * Usually: + * the free space is the size of the leadout track + * the size of data is the sum of track sizes (excluding leadout) + * the capacity depends on the media: + * for closed discs == sum of track sizes + * for multisession discs == free space (leadout size) + * for blank discs == (free space) leadout size + * for rewritable/blank == use SCSI functions to get capacity (see below) + * + * In fact we should really need the size of data in DVD+/-RW cases since the + * session is always equal to the size of the disc. + */ + +void +brasero_medium_get_data_size (BraseroMedium *medium, + gint64 *size, + gint64 *blocks) +{ + GSList *iter; + BraseroMediumPrivate *priv; + BraseroMediumTrack *track = NULL; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + + if (!priv->tracks) { + /* that's probably because it wasn't possible to retrieve info */ + if (size) + *size = 0; + + if (blocks) + *blocks = 0; + + return; + } + + for (iter = priv->tracks; iter; iter = iter->next) { + BraseroMediumTrack *tmp; + + tmp = iter->data; + if (tmp->type == BRASERO_MEDIUM_TRACK_LEADOUT) + break; + + track = iter->data; + } + + if (size) + *size = track ? (track->start + track->blocks_num) * priv->block_size: 0; + + if (blocks) + *blocks = track ? track->start + track->blocks_num: 0; +} + +void +brasero_medium_get_free_space (BraseroMedium *medium, + gint64 *size, + gint64 *blocks) +{ + GSList *iter; + BraseroMediumPrivate *priv; + BraseroMediumTrack *track = NULL; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + + if (!priv->tracks) { + /* that's probably because it wasn't possible to retrieve info. + * maybe it also happens with unformatted DVD+RW */ + + if (priv->info & BRASERO_MEDIUM_CLOSED) { + if (size) + *size = 0; + + if (blocks) + *blocks = 0; + } + else { + if (size) + *size = priv->block_num * priv->block_size; + + if (blocks) + *blocks = priv->block_num; + } + + return; + } + + for (iter = priv->tracks; iter; iter = iter->next) { + BraseroMediumTrack *tmp; + + tmp = iter->data; + if (tmp->type == BRASERO_MEDIUM_TRACK_LEADOUT) { + track = iter->data; + break; + } + } + + if (size) { + if (!track) { + /* No leadout was found so the disc is probably closed: + * no free space left. */ + *size = 0; + } + else if (track->blocks_num <= 0) + *size = (priv->block_num - track->start) * priv->block_size; + else + *size = track->blocks_num * priv->block_size; + } + + if (blocks) { + if (!track) { + /* No leadout was found so the disc is probably closed: + * no free space left. */ + *blocks = 0; + } + else if (track->blocks_num <= 0) + *blocks = priv->block_num - track->blocks_num; + else + *blocks = track->blocks_num; + } +} + +void +brasero_medium_get_capacity (BraseroMedium *medium, + gint64 *size, + gint64 *blocks) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (medium); + + if (priv->info & BRASERO_MEDIUM_REWRITABLE) { + if (size) + *size = priv->block_num * priv->block_size; + + if (blocks) + *blocks = priv->block_num; + } + else if (priv->info & BRASERO_MEDIUM_CLOSED) + brasero_medium_get_data_size (medium, size, blocks); + else + brasero_medium_get_free_space (medium, size, blocks); +} + +/** + * Function to retrieve the capacity of a media + */ + +static BraseroBurnResult +brasero_medium_get_capacity_CD_RW (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + BraseroScsiAtipData *atip_data = NULL; + BraseroMediumPrivate *priv; + BraseroScsiResult result; + int size = 0; + + priv = BRASERO_MEDIUM_PRIVATE (self); + + BRASERO_BURN_LOG ("Retrieving capacity from atip"); + + result = brasero_mmc1_read_atip (fd, + &atip_data, + &size, + NULL); + + if (result != BRASERO_SCSI_OK) { + BRASERO_BURN_LOG ("READ ATIP failed (scsi error)"); + return BRASERO_BURN_ERR; + } + + /* check the size of the structure: it must be at least 16 bytes long */ + if (size < 16) { + if (size) + g_free (atip_data); + + BRASERO_BURN_LOG ("READ ATIP failed (wrong size)"); + return BRASERO_BURN_ERR; + } + + priv->block_num = BRASERO_MSF_TO_LBA (atip_data->desc->leadout_mn, + atip_data->desc->leadout_sec, + atip_data->desc->leadout_frame); + g_free (atip_data); + + BRASERO_BURN_LOG ("Format capacity %lli %lli", + priv->block_num, + priv->block_size); + + return BRASERO_BURN_OK; +} + +static BraseroBurnResult +brasero_medium_get_capacity_DVD_RW (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + BraseroScsiFormatCapacitiesHdr *hdr = NULL; + BraseroScsiMaxCapacityDesc *current; + BraseroMediumPrivate *priv; + BraseroScsiResult result; + gint size; + + BRASERO_BURN_LOG ("Retrieving format capacity"); + + priv = BRASERO_MEDIUM_PRIVATE (self); + result = brasero_mmc2_read_format_capacities (fd, + &hdr, + &size, + code); + if (result != BRASERO_SCSI_OK) { + g_free (hdr); + + BRASERO_BURN_LOG ("READ FORMAT CAPACITIES failed"); + return BRASERO_BURN_ERR; + } + + current = hdr->max_caps; + + /* see if the media is already formatted */ + if (current->type != BRASERO_SCSI_DESC_FORMATTED) { + int i, max; + BraseroScsiFormattableCapacityDesc *desc; + + max = (hdr->len - + sizeof (BraseroScsiMaxCapacityDesc)) / + sizeof (BraseroScsiFormattableCapacityDesc); + + desc = hdr->desc; + for (i = 0; i < max; i ++, desc ++) { + /* search for the correct descriptor */ + if (BRASERO_MEDIUM_IS (priv->info, BRASERO_MEDIUM_DVDRW_PLUS)) { + if (desc->format_type == BRASERO_SCSI_DVDRW_PLUS) { + priv->block_num = BRASERO_GET_32 (desc->blocks_num); + priv->block_size = BRASERO_GET_24 (desc->type_param); + + /* that can happen */ + if (!priv->block_size) + priv->block_size = 2048; + break; + } + } + else if (desc->format_type == BRASERO_SCSI_BLOCK_SIZE_DEFAULT_AND_DB) { + priv->block_num = BRASERO_GET_32 (desc->blocks_num); + priv->block_size = BRASERO_GET_24 (desc->type_param); + break; + } + } + } + else { + priv->block_num = BRASERO_GET_32 (current->blocks_num); + priv->block_size = BRASERO_GET_24 (current->block_size); + } + + BRASERO_BURN_LOG ("Format capacity %lli %lli", + priv->block_num, + priv->block_size); + + g_free (hdr); + return BRASERO_BURN_OK; +} + +static BraseroBurnResult +brasero_medium_get_capacity_by_type (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (self); + + priv->block_size = 2048; + + if (!(priv->info & BRASERO_MEDIUM_REWRITABLE)) + return BRASERO_BURN_OK; + + if (priv->info & BRASERO_MEDIUM_CD) + brasero_medium_get_capacity_CD_RW (self, fd, code); + else + brasero_medium_get_capacity_DVD_RW (self, fd, code); + + return BRASERO_BURN_OK; +} + +/** + * Functions to retrieve the speed + */ + +static BraseroBurnResult +brasero_medium_get_speed_mmc3 (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + int size; + int num_desc, i; + gint max_rd, max_wrt; + BraseroScsiResult result; + BraseroMediumPrivate *priv; + BraseroScsiWrtSpdDesc *desc; + BraseroScsiGetPerfData *wrt_perf = NULL; + + BRASERO_BURN_LOG ("Retrieving speed (Get Performance)"); + + /* NOTE: this only work if there is RT streaming feature with + * wspd bit set to 1. At least an MMC3 drive. */ + priv = BRASERO_MEDIUM_PRIVATE (self); + result = brasero_mmc3_get_performance_wrt_spd_desc (fd, + &wrt_perf, + &size, + code); + + if (result != BRASERO_SCSI_OK) { + g_free (wrt_perf); + + BRASERO_BURN_LOG ("GET PERFORMANCE failed"); + return BRASERO_BURN_ERR; + } + + num_desc = (size - sizeof (BraseroScsiGetPerfHdr)) / + sizeof (BraseroScsiWrtSpdDesc); + + if (num_desc <= 0) + goto end; + + priv->rd_speeds = g_new0 (gint, num_desc + 1); + priv->wr_speeds = g_new0 (gint, num_desc + 1); + + max_rd = 0; + max_wrt = 0; + + desc = (BraseroScsiWrtSpdDesc*) &wrt_perf->data; + for (i = 0; i < num_desc; i ++, desc ++) { + priv->rd_speeds [i] = BRASERO_GET_32 (desc->rd_speed); + priv->wr_speeds [i] = BRASERO_GET_32 (desc->wr_speed); + + max_rd = MAX (max_rd, priv->rd_speeds [i]); + max_wrt = MAX (max_wrt, priv->wr_speeds [i]); + } + + priv->max_rd = max_rd; + priv->max_wrt = max_wrt; + +end: + + g_free (wrt_perf); + + /* strangely there are so drives (I know one case) which support this + * function but don't report any speed. So if our top speed is 0 then + * use the other way to get the speed. It was a Teac */ + if (!priv->max_wrt) + return BRASERO_BURN_ERR; + + return BRASERO_BURN_OK; +} + +static BraseroBurnResult +brasero_medium_get_page_2A_write_speed_desc (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + BraseroScsiStatusPage *page_2A = NULL; + BraseroScsiStatusWrSpdDesc *desc; + BraseroScsiModeData *data = NULL; + BraseroMediumPrivate *priv; + BraseroScsiResult result; + gint desc_num, i; + gint max_wrt = 0; + gint max_num; + int size = 0; + + BRASERO_BURN_LOG ("Retrieving speed (2A speeds)"); + + priv = BRASERO_MEDIUM_PRIVATE (self); + result = brasero_spc1_mode_sense_get_page (fd, + BRASERO_SPC_PAGE_STATUS, + &data, + &size, + code); + if (result != BRASERO_SCSI_OK) { + g_free (data); + + BRASERO_BURN_LOG ("MODE SENSE failed"); + return BRASERO_BURN_ERR; + } + + page_2A = (BraseroScsiStatusPage *) &data->page; + + /* FIXME: the following is not necessarily true */ + if (size < sizeof (BraseroScsiStatusPage)) { + g_free (data); + + BRASERO_BURN_LOG ("wrong size in page"); + return BRASERO_BURN_ERR; + } + + desc_num = BRASERO_GET_16 (page_2A->wr_speed_desc_num); + max_num = size - + sizeof (BraseroScsiStatusPage) - + sizeof (BraseroScsiModeHdr); + max_num /= sizeof (BraseroScsiWrtSpdDesc); + + if (max_num < 0) + max_num = 0; + + if (desc_num > max_num) + desc_num = max_num; + + priv->wr_speeds = g_new0 (gint, desc_num + 1); + desc = page_2A->wr_spd_desc; + for (i = 0; i < desc_num; i ++, desc ++) { + priv->wr_speeds [i] = BRASERO_GET_16 (desc->speed); + max_wrt = MAX (max_wrt, priv->wr_speeds [i]); + } + + if (!max_wrt) + priv->max_wrt = BRASERO_GET_16 (page_2A->wr_max_speed); + else + priv->max_wrt = max_wrt; + + priv->max_rd = BRASERO_GET_16 (page_2A->rd_max_speed); + g_free (data); + + return BRASERO_BURN_OK; +} + +static BraseroBurnResult +brasero_medium_get_page_2A_max_speed (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + BraseroScsiStatusPage *page_2A = NULL; + BraseroScsiModeData *data = NULL; + BraseroMediumPrivate *priv; + BraseroScsiResult result; + int size = 0; + + BRASERO_BURN_LOG ("Retrieving speed (2A max)"); + + priv = BRASERO_MEDIUM_PRIVATE (self); + + result = brasero_spc1_mode_sense_get_page (fd, + BRASERO_SPC_PAGE_STATUS, + &data, + &size, + code); + if (result != BRASERO_SCSI_OK) { + g_free (data); + + BRASERO_BURN_LOG ("MODE SENSE failed"); + return BRASERO_BURN_ERR; + } + + page_2A = (BraseroScsiStatusPage *) &data->page; + + if (size < 0x14) { + g_free (data); + + BRASERO_BURN_LOG ("wrong page size"); + return BRASERO_BURN_ERR; + } + + priv->max_rd = BRASERO_GET_16 (page_2A->rd_max_speed); + priv->max_wrt = BRASERO_GET_16 (page_2A->wr_max_speed); + + g_free (data); + return BRASERO_BURN_OK; +} + +static BraseroBurnResult +brasero_medium_get_medium_type (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + BraseroScsiGetConfigHdr *hdr = NULL; + BraseroMediumPrivate *priv; + BraseroScsiResult result; + int size; + + BRASERO_BURN_LOG ("Retrieving media profile"); + + priv = BRASERO_MEDIUM_PRIVATE (self); + result = brasero_mmc2_get_configuration_feature (fd, + BRASERO_SCSI_FEAT_REAL_TIME_STREAM, + &hdr, + &size, + code); + if (result != BRASERO_SCSI_OK) { + BraseroScsiAtipData *data = NULL; + int size = 0; + + BRASERO_BURN_LOG ("GET CONFIGURATION failed"); + + /* This could be a MMC1 drive since this command was + * introduced in MMC2 and is supported onward. So it + * has to be a CD (R/RW). The rest of the information + * will be provided by read_disc_information. */ + + /* The only thing here left to determine is if that's a WRITABLE + * or a REWRITABLE. To determine that information, we need to + * read TocPmaAtip. It if fails that's a ROM, if it succeeds. + * No need to set error code since we consider that it's a ROM + * if a failure happens. */ + result = brasero_mmc1_read_atip (fd, + &data, + &size, + NULL); + if (result != BRASERO_SCSI_OK) { + /* CDROM */ + priv->info = BRASERO_MEDIUM_CDROM; + priv->type = types [1]; + priv->icon = icons [1]; + } + else { + /* check the size of the structure: it must be at least 8 bytes long */ + if (size < 8) { + if (size) + g_free (data); + + BRASERO_BURN_LOG ("READ ATIP failed (wrong size)"); + return BRASERO_BURN_ERR; + } + + if (data->desc->erasable) { + /* CDRW */ + priv->info = BRASERO_MEDIUM_CDRW; + priv->type = types [3]; + priv->icon = icons [3]; + } + else { + /* CDR */ + priv->info = BRASERO_MEDIUM_CDR; + priv->type = types [2]; + priv->icon = icons [2]; + } + + g_free (data); + } + + /* retrieve the speed */ + result = brasero_medium_get_page_2A_max_speed (self, + fd, + code); + return result; + } + + switch (BRASERO_GET_16 (hdr->current_profile)) { + case BRASERO_SCSI_PROF_CDROM: + priv->info = BRASERO_MEDIUM_CDROM; + priv->type = types [1]; + priv->icon = icons [1]; + break; + + case BRASERO_SCSI_PROF_CDR: + priv->info = BRASERO_MEDIUM_CDR; + priv->type = types [2]; + priv->icon = icons [2]; + break; + + case BRASERO_SCSI_PROF_CDRW: + priv->info = BRASERO_MEDIUM_CDRW; + priv->type = types [3]; + priv->icon = icons [3]; + break; + + case BRASERO_SCSI_PROF_DVD_ROM: + priv->info = BRASERO_MEDIUM_DVD_ROM; + priv->type = types [4]; + priv->icon = icons [4]; + break; + + case BRASERO_SCSI_PROF_DVD_R: + priv->info = BRASERO_MEDIUM_DVDR; + priv->type = types [5]; + priv->icon = icons [5]; + break; + + case BRASERO_SCSI_PROF_DVD_RW_RESTRICTED: + priv->info = BRASERO_MEDIUM_DVDRW_RESTRICTED; + priv->type = types [6]; + priv->icon = icons [6]; + break; + + case BRASERO_SCSI_PROF_DVD_RW_SEQUENTIAL: + priv->info = BRASERO_MEDIUM_DVDRW; + priv->type = types [6]; + priv->icon = icons [6]; + break; + + case BRASERO_SCSI_PROF_DVD_R_PLUS: + priv->info = BRASERO_MEDIUM_DVDR_PLUS; + priv->type = types [7]; + priv->icon = icons [7]; + break; + + case BRASERO_SCSI_PROF_DVD_RW_PLUS: + priv->info = BRASERO_MEDIUM_DVDRW_PLUS; + priv->type = types [8]; + priv->icon = icons [7]; + break; + + /* WARNING: these types are recognized, no more */ + case BRASERO_SCSI_PROF_DVD_R_PLUS_DL: + priv->info = BRASERO_MEDIUM_DVDR_PLUS_DL; + priv->type = types [9]; + priv->icon = icons [7]; + break; + + case BRASERO_SCSI_PROF_DVD_RW_PLUS_DL: + priv->info = BRASERO_MEDIUM_DVDRW_PLUS_DL; + priv->type = types [10]; + priv->icon = icons [7]; + break; + + case BRASERO_SCSI_PROF_DVD_R_DL_SEQUENTIAL: + priv->info = BRASERO_MEDIUM_DVDR_DL; + priv->type = types [11]; + priv->icon = icons [5]; + break; + + case BRASERO_SCSI_PROF_DVD_R_DL_JUMP: + priv->info = BRASERO_MEDIUM_DVDR_JUMP_DL; + priv->type = types [11]; + priv->icon = icons [5]; + break; + + case BRASERO_SCSI_PROF_DVD_RAM: + priv->info = BRASERO_MEDIUM_DVD_RAM; + priv->type = types [12]; + priv->icon = icons [8]; + break; + + case BRASERO_SCSI_PROF_BD_ROM: + priv->info = BRASERO_MEDIUM_BD_ROM; + priv->type = types [13]; + priv->icon = icons [4]; + break; + + case BRASERO_SCSI_PROF_BR_R_SEQUENTIAL: + priv->info = BRASERO_MEDIUM_BDR; + priv->type = types [14]; + priv->icon = icons [5]; + break; + + case BRASERO_SCSI_PROF_BR_R_RANDOM: + priv->info = BRASERO_MEDIUM_BDR_RANDOM; + priv->type = types [14]; + priv->icon = icons [5]; + break; + + case BRASERO_SCSI_PROF_BD_RW: + priv->info = BRASERO_MEDIUM_BDRW; + priv->type = types [15]; + priv->icon = icons [6]; + break; + + case BRASERO_SCSI_PROF_NON_REMOVABLE: + case BRASERO_SCSI_PROF_REMOVABLE: + case BRASERO_SCSI_PROF_MO_ERASABLE: + case BRASERO_SCSI_PROF_MO_WRITE_ONCE: + case BRASERO_SCSI_PROF_MO_ADVANCED_STORAGE: + case BRASERO_SCSI_PROF_DDCD_ROM: + case BRASERO_SCSI_PROF_DDCD_R: + case BRASERO_SCSI_PROF_DDCD_RW: + case BRASERO_SCSI_PROF_HD_DVD_ROM: + case BRASERO_SCSI_PROF_HD_DVD_R: + case BRASERO_SCSI_PROF_HD_DVD_RAM: + priv->info = BRASERO_MEDIUM_UNSUPPORTED; + priv->icon = icons [0]; + g_free (hdr); + return BRASERO_BURN_NOT_SUPPORTED; + } + + /* try all SCSI functions to get write/read speeds in order */ + if (hdr->desc->add_len >= sizeof (BraseroScsiRTStreamDesc)) { + BraseroScsiRTStreamDesc *stream; + + /* means it's at least an MMC3 drive */ + stream = (BraseroScsiRTStreamDesc *) hdr->desc->data; + if (stream->wrt_spd) { + result = brasero_medium_get_speed_mmc3 (self, fd, code); + if (result == BRASERO_BURN_OK) + goto end; + } + + if (stream->mp2a) { + result = brasero_medium_get_page_2A_write_speed_desc (self, fd, code); + if (result == BRASERO_BURN_OK) + goto end; + } + } + + /* fallback for speeds */ + result = brasero_medium_get_page_2A_max_speed (self, fd, code); + +end: + + g_free (hdr); + + if (result != BRASERO_BURN_OK) + return result; + + return BRASERO_BURN_OK; +} + +static BraseroBurnResult +brasero_medium_get_css_feature (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + BraseroScsiGetConfigHdr *hdr = NULL; + BraseroMediumPrivate *priv; + BraseroScsiResult result; + int size; + + priv = BRASERO_MEDIUM_PRIVATE (self); + + BRASERO_BURN_LOG ("Testing for Css encrypted media"); + result = brasero_mmc2_get_configuration_feature (fd, + BRASERO_SCSI_FEAT_DVD_CSS, + &hdr, + &size, + code); + if (result != BRASERO_SCSI_OK) { + g_free (hdr); + + BRASERO_BURN_LOG ("GET CONFIGURATION failed"); + return BRASERO_BURN_ERR; + } + + if (hdr->desc->add_len < sizeof (BraseroScsiDVDCssDesc)) { + g_free (hdr); + return BRASERO_BURN_OK; + } + + /* here we just need to see if this feature is current or not */ + if (hdr->desc->current) { + priv->info |= BRASERO_MEDIUM_PROTECTED; + BRASERO_BURN_LOG ("media is Css protected"); + } + + g_free (hdr); + return BRASERO_BURN_OK; +} + +/** + * Functions to get information about disc contents + */ + +static void +brasero_medium_set_track_type (BraseroMedium *self, + BraseroMediumTrack *track, + guchar control) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (self); + + if (control & BRASERO_SCSI_TRACK_COPY) + track->type |= BRASERO_MEDIUM_TRACK_COPY; + + if (!(control & BRASERO_SCSI_TRACK_DATA)) { + track->type |= BRASERO_MEDIUM_TRACK_AUDIO; + priv->info |= BRASERO_MEDIUM_HAS_AUDIO; + + if (control & BRASERO_SCSI_TRACK_PREEMP) + track->type |= BRASERO_MEDIUM_TRACK_PREEMP; + + if (control & BRASERO_SCSI_TRACK_4_CHANNELS) + track->type |= BRASERO_MEDIUM_TRACK_4_CHANNELS; + } + else { + track->type |= BRASERO_MEDIUM_TRACK_DATA; + priv->info |= BRASERO_MEDIUM_HAS_DATA; + + if (control & BRASERO_SCSI_TRACK_DATA_INCREMENTAL) + track->type |= BRASERO_MEDIUM_TRACK_INCREMENTAL; + } +} + +static BraseroBurnResult +brasero_medium_track_volume_size (BraseroMedium *self, + BraseroMediumTrack *track, + int fd) +{ + BraseroMediumPrivate *priv; + BraseroBurnResult res; + GError *error = NULL; + gint64 nb_blocks; + + if (!track) + return BRASERO_BURN_ERR; + + priv = BRASERO_MEDIUM_PRIVATE (self); + + /* This is a special case. For DVD+RW and DVD-RW in restricted + * mode, there is only one session that takes the whole disc size + * once formatted. That doesn't necessarily means they have data + * Note also that they are reported as complete though you can + * still add data (with growisofs). It is nevertheless on the + * condition that the fs is valid. + * So we check if their first and only volume is valid. + * That's also used when the track size is reported a 300 Kio + * see below */ + res = brasero_volume_get_size_fd (fd, + track->start, + &nb_blocks, + NULL); + if (!res) { + BRASERO_BURN_LOG ("Failed to retrieve the volume size: %s", + error && error->message ? + error->message:"unknown error"); + + if (error) + g_error_free (error); + return BRASERO_BURN_ERR; + } + + track->blocks_num = nb_blocks; + return BRASERO_BURN_OK; +} + +static BraseroBurnResult +brasero_medium_track_get_info (BraseroMedium *self, + BraseroMediumTrack *track, + int track_num, + int fd, + BraseroScsiErrCode *code) +{ + BraseroScsiTrackInfo track_info; + BraseroMediumPrivate *priv; + BraseroScsiResult result; + int size; + + BRASERO_BURN_LOG ("Retrieving track information for %i", track_num); + + priv = BRASERO_MEDIUM_PRIVATE (self); + + /* at this point we know the type of the disc that's why we set the + * size according to this type. That may help to avoid outrange address + * errors. */ + if (BRASERO_MEDIUM_IS (priv->info, BRASERO_MEDIUM_DL|BRASERO_MEDIUM_WRITABLE)) + size = 48; + else if (BRASERO_MEDIUM_IS (priv->info, BRASERO_MEDIUM_PLUS|BRASERO_MEDIUM_WRITABLE)) + size = 40; + else + size = 36; + + result = brasero_mmc1_read_track_info (fd, + track_num, + &track_info, + &size, + code); + + if (result != BRASERO_SCSI_OK) { + BRASERO_BURN_LOG ("READ TRACK INFO failed"); + return BRASERO_BURN_ERR; + } + + track->blocks_num = BRASERO_GET_32 (track_info.track_size); + track->session = BRASERO_SCSI_SESSION_NUM (track_info); + + /* Now here is a potential bug: we can write tracks (data or not) + * shorter than 300 Kio /2 sec but they will be padded to reach this + * floor value. That means that is blocks_num is 300 blocks that may + * mean that the data length on the track is actually shorter. + * So we read the volume descriptor. If it works, good otherwise + * use the old value. + * That's important for checksuming to have a perfect account of the + * data size. */ + if (track->blocks_num <= 300) { + BRASERO_BURN_LOG ("300 sectors size. Checking for real size"); + brasero_medium_track_volume_size (self, track, fd); + } + + if (track_info.next_wrt_address_valid) + priv->next_wr_add = BRASERO_GET_32 (track_info.next_wrt_address); + + BRASERO_BURN_LOG ("Track %i (session %i): type = %i start = %llu size = %llu", + track_num, + track->session, + track->type, + track->start, + track->blocks_num); + + return BRASERO_BURN_OK; +} + +/** + * return : + * 0 when it's not possible to determine (fallback to formatted toc) + * -1 for BCD + * 1 for HEX */ +static guint +brasero_medium_check_BCD_use (BraseroMedium *self, + int fd, + BraseroScsiRawTocDesc *desc, + guint num, + BraseroScsiErrCode *code) +{ + guint i; + int size; + guint leadout = 0; + guint track_num = 0; + gboolean use_BCD = TRUE; + gboolean use_HEX = TRUE; + BraseroScsiResult result; + BraseroScsiTrackInfo track_info; + guint start_BCD, start_LBA, track_start; + + /* first check if all values are valid BCD numbers in the descriptors */ + for (i = 0; i < num; i++) { + if (desc [i].adr == 1 && desc [i].point <= BRASERO_SCSI_Q_SUB_CHANNEL_TRACK_START) { + if (!BRASERO_IS_BCD_VALID (desc [i].p_min) + || !BRASERO_IS_BCD_VALID (desc [i].p_sec) + || !BRASERO_IS_BCD_VALID (desc [i].p_frame)) { + use_BCD = FALSE; + break; + } + } + else if (desc [i].point == BRASERO_SCSI_Q_SUB_CHANNEL_LEADOUT_START) { + if (!BRASERO_IS_BCD_VALID (desc [i].p_min) + || !BRASERO_IS_BCD_VALID (desc [i].p_sec) + || !BRASERO_IS_BCD_VALID (desc [i].p_frame)) { + use_BCD = FALSE; + break; + } + } + } + + /* then check if there are valid Hex values */ + for (i = 0; i < num; i++) { + if (desc [i].adr != 1 || desc [i].point > BRASERO_SCSI_Q_SUB_CHANNEL_TRACK_START) + continue; + + if (desc [i].p_min > 99 + || desc [i].p_sec > 59 + || desc [i].p_frame > 74) { + use_HEX = FALSE; + break; + } + } + + if (use_BCD != use_HEX) { + if (use_BCD) + return -1; + + return 1; + } + + /* To check if the drive uses BCD values or HEX values we ask for the + * track information that contains also the start for the track but in + * HEX values. If values are the same then it works. */ + + /* NOTE: there could be another way to do it: get first track, in LBA + * and BCD it must be 150. */ + + /* First find the first track and get track start address in BCD */ + BRASERO_BURN_LOG ("Retrieving track information to determine number format"); + + for (i = 0; i < num; i++) { + if (desc [i].adr == BRASERO_SCSI_Q_SUB_CHANNEL_LEADIN_MODE5 + && desc [i].point == BRASERO_SCSI_Q_SUB_CHANNEL_MULTI_NEXT_SESSION) { + /* store the leadout number just in case */ + leadout = i; + continue; + } + + if (desc [i].adr != 1 || desc [i].point > BRASERO_SCSI_Q_SUB_CHANNEL_TRACK_START) + continue; + + track_num ++; + + start_BCD = BRASERO_MSF_TO_LBA (BRASERO_GET_BCD (desc [i].p_min), + BRASERO_GET_BCD (desc [i].p_sec), + BRASERO_GET_BCD (desc [i].p_frame)); + + start_LBA = BRASERO_MSF_TO_LBA (desc [i].p_min, + desc [i].p_sec, + desc [i].p_frame); + + BRASERO_BURN_LOG ("Comparing to track information from READ TRACK INFO for track %i", track_num); + + size = 36; + start_LBA -= 150; + start_BCD -= 150; + + result = brasero_mmc1_read_track_info (fd, + track_num, + &track_info, + &size, + code); + + if (result != BRASERO_SCSI_OK) { + BRASERO_BURN_LOG ("READ TRACK INFO failed"); + /* Fallback to formatted toc */ + return 0; + } + + track_start = BRASERO_GET_32 (track_info.start_lba); + BRASERO_BURN_LOG ("comparing DCB %i and LBA %i to real start address %i", + start_BCD, start_LBA, track_start); + + /* try to find a conclusive match */ + if (track_start == start_BCD && track_start != start_LBA) + return -1; + + if (track_start == start_LBA && track_start != start_BCD) + return 1; + } + + /* Our last chance, the leadout. + * NOTE: no need to remove 150 sectors here. */ + start_BCD = BRASERO_MSF_TO_LBA (BRASERO_GET_BCD (desc [leadout].min), + BRASERO_GET_BCD (desc [leadout].sec), + BRASERO_GET_BCD (desc [leadout].frame)); + + start_LBA = BRASERO_MSF_TO_LBA (desc [leadout].min, + desc [leadout].sec, + desc [leadout].frame); + + BRASERO_BURN_LOG ("Comparing to track information from READ TRACK INFO for leadout"); + + size = 36; + + /* leadout number is number of tracks + 1 */ + result = brasero_mmc1_read_track_info (fd, + track_num + 1, + &track_info, + &size, + code); + + if (result != BRASERO_SCSI_OK) { + BRASERO_BURN_LOG ("READ TRACK INFO failed for leadout"); + /* Fallback to formatted toc */ + return 0; + } + + track_start = BRASERO_GET_32 (track_info.start_lba); + BRASERO_BURN_LOG ("comparing DCB %i and LBA %i to real start address %i", + start_BCD, start_LBA, track_start); + + /* try to find a conclusive match */ + if (track_start == start_BCD && track_start != start_LBA) + return -1; + + if (track_start == start_LBA && track_start != start_BCD) + return 1; + + /* fallback to formatted toc */ + return 0; +} + +/** + * The reason why we use this perhaps more lengthy method is that with + * multisession discs, the first track is reported to be two sectors shorter + * than it should. As I don't know why and since the following works we use + * this one. */ +static BraseroBurnResult +brasero_medium_get_CD_sessions_info (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + gint use_bcd; + GSList *iter; + int num, i, size; + gint leadout_start = 0; + BraseroScsiResult result; + BraseroMediumPrivate *priv; + BraseroScsiRawTocDesc *desc; + BraseroScsiRawTocData *toc = NULL; + + BRASERO_BURN_LOG ("Reading Raw Toc"); + + priv = BRASERO_MEDIUM_PRIVATE (self); + + size = 0; + result = brasero_mmc1_read_toc_raw (fd, + 0, + &toc, + &size, + code); + if (result != BRASERO_SCSI_OK) { + BRASERO_BURN_LOG ("READ TOC failed"); + return BRASERO_BURN_ERR; + } + + num = (size - sizeof (BraseroScsiRawTocData)) / + sizeof (BraseroScsiRawTocDesc); + + BRASERO_BURN_LOG ("%i track(s) found", num); + + desc = toc->desc; + use_bcd = brasero_medium_check_BCD_use (self, fd, desc, num, code); + if (!use_bcd) { + g_free (toc); + + BRASERO_BURN_LOG ("Fallback to formatted toc"); + return BRASERO_BURN_ERR; + } + + if (use_bcd > 0) + use_bcd = 0; + + if (use_bcd) { + BRASERO_BURN_LOG ("Using BCD format"); + } + else { + BRASERO_BURN_LOG ("Using HEX format"); + } + + for (i = 0; i < num; i++, desc ++) { + BraseroMediumTrack *track; + + track = NULL; + if (desc->adr == 1 && desc->point <= BRASERO_SCSI_Q_SUB_CHANNEL_TRACK_START) { + track = g_new0 (BraseroMediumTrack, 1); + track->session = desc->session_num; + + brasero_medium_set_track_type (self, track, desc->control); + if (use_bcd) + track->start = BRASERO_MSF_TO_LBA (BRASERO_GET_BCD (desc->p_min), + BRASERO_GET_BCD (desc->p_sec), + BRASERO_GET_BCD (desc->p_frame)); + else + track->start = BRASERO_MSF_TO_LBA (desc->p_min, + desc->p_sec, + desc->p_frame); + + track->start -= 150; + + /* if there are tracks and the last previously added track is in + * the same session then set the size */ + if (priv->tracks) { + BraseroMediumTrack *last_track; + + last_track = priv->tracks->data; + if (last_track->session == track->session) + last_track->blocks_num = track->start - last_track->start; + } + + priv->tracks = g_slist_prepend (priv->tracks, track); + } + else if (desc->point == BRASERO_SCSI_Q_SUB_CHANNEL_LEADOUT_START) { + /* NOTE: the leadout session is first in the list. So if + * we have tracks in the list set the last session track + * size when we reach a new leadout (and therefore a new + * session). */ + + if (priv->tracks) { + BraseroMediumTrack *last_track; + + last_track = priv->tracks->data; + last_track->blocks_num = leadout_start - last_track->start; + } + + if (use_bcd) + leadout_start = BRASERO_MSF_TO_LBA (BRASERO_GET_BCD (desc->p_min), + BRASERO_GET_BCD (desc->p_sec), + BRASERO_GET_BCD (desc->p_frame)); + else + leadout_start = BRASERO_MSF_TO_LBA (desc->p_min, + desc->p_sec, + desc->p_frame); + leadout_start -= 150; + } + } + + if (priv->tracks) { + BraseroMediumTrack *last_track; + + /* set the last found track size */ + last_track = priv->tracks->data; + last_track->blocks_num = leadout_start - last_track->start; + } + + /* Add a leadout */ + if (!(priv->info & BRASERO_MEDIUM_CLOSED)) { + BraseroMediumTrack *track; + + /* we shouldn't request info on leadout if the disc is closed */ + track = g_new0 (BraseroMediumTrack, 1); + priv->tracks = g_slist_prepend (priv->tracks, track); + track->start = leadout_start; + track->type = BRASERO_MEDIUM_TRACK_LEADOUT; + + brasero_medium_track_get_info (self, track, g_slist_length (priv->tracks), fd, code); + } + + priv->tracks = g_slist_reverse (priv->tracks); + + for (iter = priv->tracks; iter; iter = iter->next) { + BraseroMediumTrack *track; + + track = iter->data; + + /* check for tracks less that 300 sectors */ + if (track->blocks_num <= 300 && track->type != BRASERO_MEDIUM_TRACK_LEADOUT) { + BRASERO_BURN_LOG ("300 sectors size. Checking for real size"); + brasero_medium_track_volume_size (self, track, fd); + } + + BRASERO_BURN_LOG ("Track %i: type = %i start = %llu size = %llu", + g_slist_index (priv->tracks, track), + track->type, + track->start, + track->blocks_num); + } + + g_free (toc); + return BRASERO_BURN_OK; +} + +/** + * NOTE: for DVD-R multisession we lose 28688 blocks for each session + * so the capacity is the addition of all session sizes + 28688 for each + * For all multisession DVD-/+R and CDR-RW the remaining size is given + * in the leadout. One exception though with DVD+/-RW. + */ + +static void +brasero_medium_add_DVD_plus_RW_leadout (BraseroMedium *self, + gint32 start) +{ + BraseroMediumTrack *leadout; + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (self); + + leadout = g_new0 (BraseroMediumTrack, 1); + priv->tracks = g_slist_append (priv->tracks, leadout); + + leadout->start = start; + leadout->type = BRASERO_MEDIUM_TRACK_LEADOUT; + + /* we fabricate the leadout here. We don't really need one in + * fact since it is always at the last sector whatever the + * amount of data written. So we need in fact to read the file + * system and get the last sector from it. Hopefully it won't be + * buggy */ + priv->next_wr_add = 0; + + leadout->blocks_num = priv->block_num; + if (g_slist_length (priv->tracks) > 1) { + BraseroMediumTrack *track; + + track = priv->tracks->data; + leadout->blocks_num -= ((track->blocks_num > 300) ? track->blocks_num : 300); + } + BRASERO_BURN_LOG ("Adding fabricated leadout start = %llu length = %llu", + leadout->start, + leadout->blocks_num); +} + +static BraseroBurnResult +brasero_medium_get_sessions_info (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + int num, i, size; + BraseroScsiResult result; + BraseroScsiTocDesc *desc; + BraseroMediumPrivate *priv; + BraseroScsiFormattedTocData *toc = NULL; + + BRASERO_BURN_LOG ("Reading Toc"); + + priv = BRASERO_MEDIUM_PRIVATE (self); + result = brasero_mmc1_read_toc_formatted (fd, + 0, + &toc, + &size, + code); + if (result != BRASERO_SCSI_OK) { + g_free (toc); + + BRASERO_BURN_LOG ("READ TOC failed"); + return BRASERO_BURN_ERR; + } + + num = (size - sizeof (BraseroScsiFormattedTocData)) / + sizeof (BraseroScsiTocDesc); + + BRASERO_BURN_LOG ("%i track(s) found", num); + + desc = toc->desc; + for (i = 0; i < num; i ++, desc ++) { + BraseroMediumTrack *track; + + if (desc->track_num == BRASERO_SCSI_TRACK_LEADOUT_START) + break; + + track = g_new0 (BraseroMediumTrack, 1); + priv->tracks = g_slist_prepend (priv->tracks, track); + track->start = BRASERO_GET_32 (desc->track_start); + + /* we shouldn't request info on a track if the disc is closed */ + brasero_medium_track_get_info (self, + track, + g_slist_length (priv->tracks), + fd, + code); + + if (desc->control & BRASERO_SCSI_TRACK_COPY) + track->type |= BRASERO_MEDIUM_TRACK_COPY; + + if (!(desc->control & BRASERO_SCSI_TRACK_DATA)) { + track->type |= BRASERO_MEDIUM_TRACK_AUDIO; + priv->info |= BRASERO_MEDIUM_HAS_AUDIO; + + if (desc->control & BRASERO_SCSI_TRACK_PREEMP) + track->type |= BRASERO_MEDIUM_TRACK_PREEMP; + + if (desc->control & BRASERO_SCSI_TRACK_4_CHANNELS) + track->type |= BRASERO_MEDIUM_TRACK_4_CHANNELS; + } + else if (BRASERO_MEDIUM_IS (priv->info, BRASERO_MEDIUM_DVDRW_PLUS) + || BRASERO_MEDIUM_IS (priv->info, BRASERO_MEDIUM_DVDRW_RESTRICTED)) { + BraseroBurnResult result; + + /* a special case for these two kinds of media (DVD+RW) + * which have only one track: the first. */ + result = brasero_medium_track_volume_size (self, + track, + fd); + if (result == BRASERO_BURN_OK) { + track->type |= BRASERO_MEDIUM_TRACK_DATA; + priv->info |= BRASERO_MEDIUM_HAS_DATA; + + priv->next_wr_add = 0; + + if (desc->control & BRASERO_SCSI_TRACK_DATA_INCREMENTAL) + track->type |= BRASERO_MEDIUM_TRACK_INCREMENTAL; + } + else { + priv->tracks = g_slist_remove (priv->tracks, track); + g_free (track); + + priv->info |= BRASERO_MEDIUM_BLANK; + priv->info &= ~BRASERO_MEDIUM_CLOSED; + } + } + else { + track->type |= BRASERO_MEDIUM_TRACK_DATA; + priv->info |= BRASERO_MEDIUM_HAS_DATA; + + if (desc->control & BRASERO_SCSI_TRACK_DATA_INCREMENTAL) + track->type |= BRASERO_MEDIUM_TRACK_INCREMENTAL; + } + } + + /* put the tracks in the right order */ + priv->tracks = g_slist_reverse (priv->tracks); + + if (BRASERO_MEDIUM_IS (priv->info, BRASERO_MEDIUM_DVDRW_PLUS) + || BRASERO_MEDIUM_IS (priv->info, BRASERO_MEDIUM_DVDRW_RESTRICTED)) + brasero_medium_add_DVD_plus_RW_leadout (self, BRASERO_GET_32 (desc->track_start)); + else if (!(priv->info & BRASERO_MEDIUM_CLOSED)) { + BraseroMediumTrack *track; + + /* we shouldn't request info on leadout if the disc is closed + * (except for DVD+/- (restricted) RW (see above) */ + track = g_new0 (BraseroMediumTrack, 1); + priv->tracks = g_slist_append (priv->tracks, track); + track->start = BRASERO_GET_32 (desc->track_start); + track->type = BRASERO_MEDIUM_TRACK_LEADOUT; + + brasero_medium_track_get_info (self, + track, + g_slist_length (priv->tracks), + fd, + code); + } + + g_free (toc); + + return BRASERO_BURN_OK; +} + +static BraseroBurnResult +brasero_medium_get_contents (BraseroMedium *self, + int fd, + BraseroScsiErrCode *code) +{ + int size; + BraseroScsiResult result; + BraseroMediumPrivate *priv; + BraseroScsiDiscInfoStd *info = NULL; + + BRASERO_BURN_LOG ("Retrieving media status"); + + priv = BRASERO_MEDIUM_PRIVATE (self); + + result = brasero_mmc1_read_disc_information_std (fd, + &info, + &size, + code); + if (result != BRASERO_SCSI_OK) { + g_free (info); + + BRASERO_BURN_LOG ("READ DISC INFORMATION failed"); + return BRASERO_BURN_ERR; + } + + if (info->erasable) + priv->info |= BRASERO_MEDIUM_REWRITABLE; + + if (info->status == BRASERO_SCSI_DISC_EMPTY) { + BraseroMediumTrack *track; + + BRASERO_BURN_LOG ("Empty media"); + + priv->info |= BRASERO_MEDIUM_BLANK; + priv->block_size = 2048; + + if (BRASERO_MEDIUM_IS (priv->info, BRASERO_MEDIUM_DVDRW_PLUS) + || BRASERO_MEDIUM_IS (priv->info, BRASERO_MEDIUM_DVDRW_RESTRICTED)) + brasero_medium_add_DVD_plus_RW_leadout (self, 0); + else { + track = g_new0 (BraseroMediumTrack, 1); + track->start = 0; + track->type = BRASERO_MEDIUM_TRACK_LEADOUT; + priv->tracks = g_slist_prepend (priv->tracks, track); + + brasero_medium_track_get_info (self, + track, + 1, + fd, + code); + } + goto end; + } + + if (info->status == BRASERO_SCSI_DISC_INCOMPLETE) { + priv->info |= BRASERO_MEDIUM_APPENDABLE; + BRASERO_BURN_LOG ("Appendable media"); + } + else if (info->status == BRASERO_SCSI_DISC_FINALIZED) { + priv->info |= BRASERO_MEDIUM_CLOSED; + BRASERO_BURN_LOG ("Closed media"); + } + + if (priv->info & BRASERO_MEDIUM_CD) { + result = brasero_medium_get_CD_sessions_info (self, fd, code); + if (result != BRASERO_BURN_OK) + result = brasero_medium_get_sessions_info (self, fd, code); + } + else + result = brasero_medium_get_sessions_info (self, fd, code); + + if (result != BRASERO_BURN_OK) + goto end; + +end: + + g_free (info); + return BRASERO_BURN_OK; +} + +static void +brasero_medium_init_real (BraseroMedium *object, int fd) +{ + gchar *name; + BraseroBurnResult result; + BraseroMediumPrivate *priv; + BraseroScsiErrCode code = 0; + + priv = BRASERO_MEDIUM_PRIVATE (object); + + name = nautilus_burn_drive_get_name_for_display (priv->drive); + BRASERO_BURN_LOG ("Initializing information for medium in %s", name); + g_free (name); + + result = brasero_medium_get_medium_type (object, fd, &code); + if (result != BRASERO_BURN_OK) + return; + + brasero_medium_get_capacity_by_type (object, fd, &code); + + result = brasero_medium_get_contents (object, fd, &code); + if (result != BRASERO_BURN_OK) + return; + + /* assume that css feature is only for DVD-ROM which might be wrong but + * some drives wrongly reports that css is enabled for blank DVD+R/W */ + if (BRASERO_MEDIUM_IS (priv->info, (BRASERO_MEDIUM_DVD|BRASERO_MEDIUM_ROM))) + brasero_medium_get_css_feature (object, fd, &code); + + BRASERO_BURN_LOG_DISC_TYPE (priv->info, "media is "); +} + +static gboolean +brasero_medium_retry_open (gpointer object) +{ + int fd; + const gchar *path; + BraseroMedium *self; + BraseroMediumPrivate *priv; + + self = BRASERO_MEDIUM (object); + priv = BRASERO_MEDIUM_PRIVATE (object); + path = nautilus_burn_drive_get_device (priv->drive); + + BRASERO_BURN_LOG ("Retrying to open device %s", path); + fd = open (path, OPEN_FLAGS); + if (fd < 0) { + if (errno == EBUSY + || errno == EAGAIN + || errno == EWOULDBLOCK) { + BRASERO_BURN_LOG ("Device busy"); + /* we'll retry in a second */ + return TRUE; + } + + BRASERO_BURN_LOG ("Open () failed"); + priv->info = BRASERO_MEDIUM_UNSUPPORTED; + priv->retry_id = 0; + return FALSE; + } + + BRASERO_BURN_LOG ("Open () succeeded\n"); + priv->info = BRASERO_MEDIUM_NONE; + priv->icon = icons [0]; + + priv->retry_id = 0; + + brasero_medium_init_real (self, fd); + close (fd); + + return FALSE; +} + +static void +brasero_medium_try_open (BraseroMedium *self) +{ + int fd; + const gchar *path; + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (self); + path = nautilus_burn_drive_get_device (priv->drive); + + /* the drive might be busy (a burning is going on) so we don't block + * but we re-try to open it every second */ + BRASERO_BURN_LOG ("Trying to open device %s", path); + fd = open (path, OPEN_FLAGS); + if (fd < 0) { + if (errno == EAGAIN + || errno == EWOULDBLOCK + || errno == EBUSY) { + BRASERO_BURN_LOG ("Device busy"); + priv->info = BRASERO_MEDIUM_BUSY; + priv->icon = icons [0]; + + priv->retry_id = g_timeout_add (BUSY_RETRY_TIME, + brasero_medium_retry_open, + self); + } + + BRASERO_BURN_LOG ("Open () failed"); + return; + } + + BRASERO_BURN_LOG ("Open () succeeded"); + brasero_medium_init_real (self, fd); + close (fd); +} + +static void +brasero_medium_init (BraseroMedium *object) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (object); + priv->next_wr_add = -1; + + /* we can't do anything here since properties haven't been set yet */ +} + +static void +brasero_medium_finalize (GObject *object) +{ + BraseroMediumPrivate *priv; + + priv = BRASERO_MEDIUM_PRIVATE (object); + + if (priv->retry_id) { + g_source_remove (priv->retry_id); + priv->retry_id = 0; + } + + g_free (priv->rd_speeds); + priv->rd_speeds = NULL; + + g_free (priv->wr_speeds); + priv->wr_speeds = NULL; + + g_slist_foreach (priv->tracks, (GFunc) g_free, NULL); + g_slist_free (priv->tracks); + priv->tracks = NULL; + + nautilus_burn_drive_unref (priv->drive); + priv->drive = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +brasero_medium_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + BraseroMediumPrivate *priv; + + g_return_if_fail (BRASERO_IS_MEDIUM (object)); + + priv = BRASERO_MEDIUM_PRIVATE (object); + + switch (prop_id) + { + case PROP_DRIVE: + priv->drive = g_value_get_object (value); + nautilus_burn_drive_ref (priv->drive); + brasero_medium_try_open (BRASERO_MEDIUM (object)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +brasero_medium_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + BraseroMediumPrivate *priv; + + g_return_if_fail (BRASERO_IS_MEDIUM (object)); + + priv = BRASERO_MEDIUM_PRIVATE (object); + + switch (prop_id) + { + case PROP_DRIVE: + nautilus_burn_drive_ref (priv->drive); + g_value_set_object (value, priv->drive); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +brasero_medium_class_init (BraseroMediumClass *klass) +{ + GObjectClass* object_class = G_OBJECT_CLASS (klass); + parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); + + g_type_class_add_private (klass, sizeof (BraseroMediumPrivate)); + + object_class->finalize = brasero_medium_finalize; + object_class->set_property = brasero_medium_set_property; + object_class->get_property = brasero_medium_get_property; + + g_object_class_install_property (object_class, + PROP_DRIVE, + g_param_spec_object ("drive", + "drive", + "drive in which medium is inserted", + NAUTILUS_BURN_TYPE_DRIVE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +GType +brasero_medium_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + { + static const GTypeInfo our_info = + { + sizeof (BraseroMediumClass), /* class_size */ + (GBaseInitFunc) NULL, /* base_init */ + (GBaseFinalizeFunc) NULL, /* base_finalize */ + (GClassInitFunc) brasero_medium_class_init, /* class_init */ + (GClassFinalizeFunc) NULL, /* class_finalize */ + NULL /* class_data */, + sizeof (BraseroMedium), /* instance_size */ + 0, /* n_preallocs */ + (GInstanceInitFunc) brasero_medium_init, /* instance_init */ + NULL /* value_table */ + }; + + our_type = g_type_register_static (G_TYPE_OBJECT, "BraseroMedium", + &our_info, 0); + } + + return our_type; +} + +BraseroMedium * +brasero_medium_new (NautilusBurnDrive *drive) +{ + g_return_val_if_fail (drive != NULL, NULL); + return BRASERO_MEDIUM (g_object_new (BRASERO_TYPE_MEDIUM, + "drive", drive, + NULL)); +}