Applied cleaned up patch originally by Ziusudra as part of ticket #3473:

* Changed the session module to create an extra session for existing audio
  tracks.
* Let cdda also recognize "audio partitions" as published by the session module.
* If there is only a single session, the session module now gives file system
  drivers the chance to play with the device directly.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41288 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2011-04-28 18:27:12 +00:00
parent f505da5e20
commit 3b8f747060
4 changed files with 186 additions and 51 deletions
@@ -544,9 +544,9 @@ is_data_track(const scsi_toc_track& track)
uint32
count_audio_tracks(scsi_toc_toc* toc)
{
uint32 lastTrack = toc->last_track + 1 - toc->first_track;
uint32 trackCount = toc->last_track + 1 - toc->first_track;
uint32 count = 0;
for (uint32 i = 0; i < lastTrack; i++) {
for (uint32 i = 0; i < trackCount; i++) {
if (!is_data_track(toc->tracks[i]))
count++;
}
@@ -1330,8 +1330,10 @@ Inode::RemoveAttribute(const char* name, bool checkNamespace)
AttrCookieList::Iterator i = fAttrCookies.GetIterator();
while (i.HasNext()) {
attr_cookie* cookie = i.Next();
if (cookie->current == attribute)
cookie->current = attribute->GetDoublyLinkedListLink()->next;
if (cookie->current == attribute) {
cookie->current
= attribute->GetDoublyLinkedListLink()->next;
}
}
iterator.Remove();
@@ -1378,11 +1380,41 @@ cdda_identify_partition(int fd, partition_data* partition, void** _cookie)
status_t status = read_table_of_contents(fd, toc, 2048);
// there has to be at least a single audio track
if (status == B_OK && count_audio_tracks(toc) == 0)
status = B_BAD_TYPE;
// If we succeeded in reading the toc, check the tracks in the
// partition, which may not be the whole CD, and if any are audio,
// claim the partition.
if (status == B_OK) {
uint32 trackCount = toc->last_track + (uint32)1 - toc->first_track;
uint64 sessionStartLBA = partition->offset / partition->block_size;
uint64 sessionEndLBA = sessionStartLBA
+ (partition->size / partition->block_size);
TRACE(("cdda_identify_partition: session at %lld-%lld\n",
sessionStartLBA, sessionEndLBA));
status = B_ENTRY_NOT_FOUND;
for (uint32 i = 0; i < trackCount; i++) {
// We have to get trackLBA from track.start.time since
// track.start.lba is useless for this.
// This is how session gets it.
uint64 trackLBA
= ((toc->tracks[i].start.time.minute * kFramesPerMinute)
+ (toc->tracks[i].start.time.second * kFramesPerSecond)
+ toc->tracks[i].start.time.frame - 150);
if (trackLBA >= sessionStartLBA && trackLBA < sessionEndLBA) {
if (is_data_track(toc->tracks[i])) {
TRACE(("cdda_identify_partition: track %ld at %lld is "
"data\n", i + 1, trackLBA));
status = B_BAD_TYPE;
} else {
TRACE(("cdda_identify_partition: track %ld at %lld is "
"audio\n", i + 1, trackLBA));
status = B_OK;
break;
}
}
}
}
if (status < B_OK) {
if (status != B_OK) {
free(toc);
return status;
}
@@ -1740,7 +1772,8 @@ cdda_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
TRACE(("cdda_read_stat: vnode %p (0x%Lx), stat %p\n", inode, inode->ID(), stat));
TRACE(("cdda_read_stat: vnode %p (0x%Lx), stat %p\n", inode, inode->ID(),
stat));
fill_stat_buffer(volume, inode, NULL, *stat);
@@ -1819,7 +1852,8 @@ cdda_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
TRACE(("cdda_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %ld, num = %p\n", _node, _cookie, dirent, bufferSize,_num));
TRACE(("cdda_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %ld,"
" num = %p\n", _node, _cookie, buffer, bufferSize,_num));
if ((Inode*)_node->private_node != &volume->RootNode())
return B_BAD_VALUE;
@@ -1915,7 +1949,7 @@ cdda_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
{
dir_cookie* cookie = (dir_cookie*)_cookie;
TRACE(("cdda_freecookie: entry vnode %p, cookie %p\n", _vnode, cookie));
TRACE(("cdda_freecookie: entry vnode %p, cookie %p\n", _node, cookie));
free(cookie);
return 0;
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2009, Haiku, Inc.
* Copyright 2003-2011, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Authors:
@@ -86,9 +86,9 @@ public:
off_t start_lba;
uint8 control;
//!< only used to give what are probably useless warnings
// Used to check for Yellow/Red Book mixed-mode CDs.
uint8 adr;
//!< only used to give what are probably useless warnings
// only used to give what are probably useless warnings
};
@@ -548,12 +548,13 @@ Disc::Disc(int fd)
int count;
header = (cdrom_table_of_contents_header*)data;
entries = (cdrom_full_table_of_contents_entry*)(data+4);
entries = (cdrom_full_table_of_contents_entry*)(data + 4);
header->length = B_BENDIAN_TO_HOST_INT16(header->length);
count = (header->length - 2)
/ sizeof(cdrom_full_table_of_contents_entry);
count = _AdjustForYellowBook(entries, count);
error = _ParseTableOfContents(entries, count);
// Dump();
if (!error) {
@@ -696,6 +697,92 @@ Disc::Dump()
}
/*! \brief Checks for Yellow Book data tracks in audio sessions and if found
inserts them as a new data session.
*/
uint32
Disc::_AdjustForYellowBook(cdrom_full_table_of_contents_entry entries[],
uint32 count)
{
uint8 foundCount = 0;
uint8 endLBAEntry = 0;
uint8 trackTwo = 0;
// Make sure TOC has only one session and that it is audio.
bool sessionIsAudio = true;
for (uint32 i = 0; i < count; i++) {
if (entries[i].point == 0xa2) {
if ((entries[i].control & kControlDataTrack) != 0) {
sessionIsAudio = false;
break;
}
foundCount++;
endLBAEntry = i;
}
}
if (!sessionIsAudio || foundCount != 1)
return count;
TRACE(("%s: Single audio session, checking for data track\n",
kModuleDebugName));
// See if there are any data tracks.
for (uint32 i = 0; i < count; i++) {
if (entries[i].point > 0 && entries[i].point < 100
&& (entries[i].control & kControlDataTrack) != 0) {
if (entries[i].point == 1) {
// Create a new endLBA point for session one.
entries[count] = entries[endLBAEntry];
entries[count].control = entries[i].control;
// Get track two and use it's start as
// the end of our new session.
for (uint8 j = 0; j < count; j++) {
if (entries[j].point == 2) {
trackTwo = j;
break;
}
}
entries[count].pminutes = entries[trackTwo].pminutes;
entries[count].pseconds = entries[trackTwo].pseconds;
entries[count].pframes = entries[trackTwo].pframes;
// Change the other points to session two.
for (uint32 j = 0; j < count; j++) {
entries[j].session = 2;
}
entries[i].session = 1;
count++;
TRACE(("%s: first track is data, adjusted TOC\n",
kModuleDebugName));
break;
} else {
// Change the track to session two.
entries[i].session = 2;
// Create a new endLBA point for session two.
entries[count] = entries[endLBAEntry];
entries[count].session = 2;
entries[count].control = entries[i].control;
// Use the beginning of the data track as the
// end of the previous session.
entries[endLBAEntry].pminutes = entries[i].pminutes;
entries[endLBAEntry].pseconds = entries[i].pseconds;
entries[endLBAEntry].pframes = entries[i].pframes;
count++;
TRACE(("%s: last track is data, adjusted TOC\n",
kModuleDebugName));
break;
}
}
}
return count;
}
/*! \brief Reads through the given table of contents data and creates an
unsorted, unverified (i.e. non-error-checked) list of sessions and tracks.
*/
@@ -813,7 +900,7 @@ Disc::_ParseTableOfContents(cdrom_full_table_of_contents_entry entries[],
session->track_list.Add(track);
} else {
WARN(("%s: warning: illegal point 0x%2x found in table of "
"contents\n", kModuleDebugName, entries[i].point));
"contents\n", kModuleDebugName, point));
}
break;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2009, Haiku, Inc.
* Copyright 2003-2011, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Authors:
@@ -32,27 +32,33 @@ class Session;
*/
class Disc {
public:
Disc(int fd);
~Disc();
Disc(int fd);
~Disc();
status_t InitCheck();
Session* GetSession(int32 index);
status_t InitCheck();
Session* GetSession(int32 index);
void Dump();
void Dump();
// CDs and DVDs are required to have a block size of 2K by
// the SCSI-3 standard
static const int kBlockSize = 2048;
static const int kBlockSize = 2048;
private:
status_t _ParseTableOfContents(
cdrom_full_table_of_contents_entry entries[],
uint32 count);
void _SortAndRemoveDuplicates();
status_t _CheckForErrorsAndWarnings();
uint32 _AdjustForYellowBook(
cdrom_full_table_of_contents_entry
entries[],
uint32 count);
status_t _ParseTableOfContents(
cdrom_full_table_of_contents_entry
entries[],
uint32 count);
void _SortAndRemoveDuplicates();
status_t _CheckForErrorsAndWarnings();
status_t fInitStatus;
List* fSessionList;
private:
status_t fInitStatus;
List* fSessionList;
};
@@ -61,32 +67,35 @@ private:
*/
class Session {
public:
~Session();
~Session();
off_t Offset() { return fOffset; }
off_t Size() { return fSize; }
uint32 BlockSize() { return fBlockSize; }
int32 Index() { return fIndex; }
uint32 Flags() { return fFlags; }
const char* Type() { return fType; }
off_t Offset() { return fOffset; }
off_t Size() { return fSize; }
uint32 BlockSize() { return fBlockSize; }
int32 Index() { return fIndex; }
uint32 Flags() { return fFlags; }
const char* Type() { return fType; }
private:
friend class Disc;
Session(off_t offset, off_t size, uint32 blockSize,
int32 index, uint32 flags, const char* type);
Session(off_t offset, off_t size,
uint32 blockSize, int32 index, uint32 flags,
const char* type);
Session(const Session& ref);
// not implemented
Session& operator=(const Session& ref);
// not implemented
Session(const Session& other);
// not implemented
Session& operator=(const Session& other);
// not implemented
off_t fOffset;
off_t fSize;
uint32 fBlockSize;
int32 fIndex;
uint32 fFlags;
char* fType;
private:
off_t fOffset;
off_t fSize;
uint32 fBlockSize;
int32 fIndex;
uint32 fFlags;
char* fType;
};
#endif // _DISC_H
@@ -52,12 +52,17 @@ identify_partition(int fd, partition_data *partition, void **cookie)
&& geometry.device_type == B_CD) {
Disc *disc = new(std::nothrow) Disc(fd);
if (disc != NULL && disc->InitCheck() == B_OK) {
// If we have only a single session then we can let the file system
// drivers play directly with the device.
if (disc->GetSession(1) != NULL)
result = 0.9f;
else
result = 0.1f;
*cookie = static_cast<void*>(disc);
result = 0.7;
} else
delete disc;
}
PRINT(("returning %ld\n", int32(result * 10000)));
PRINT(("returning %g\n", result));
return result;
}