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 uint32
count_audio_tracks(scsi_toc_toc* toc) 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; uint32 count = 0;
for (uint32 i = 0; i < lastTrack; i++) { for (uint32 i = 0; i < trackCount; i++) {
if (!is_data_track(toc->tracks[i])) if (!is_data_track(toc->tracks[i]))
count++; count++;
} }
@@ -1330,8 +1330,10 @@ Inode::RemoveAttribute(const char* name, bool checkNamespace)
AttrCookieList::Iterator i = fAttrCookies.GetIterator(); AttrCookieList::Iterator i = fAttrCookies.GetIterator();
while (i.HasNext()) { while (i.HasNext()) {
attr_cookie* cookie = i.Next(); attr_cookie* cookie = i.Next();
if (cookie->current == attribute) if (cookie->current == attribute) {
cookie->current = attribute->GetDoublyLinkedListLink()->next; cookie->current
= attribute->GetDoublyLinkedListLink()->next;
}
} }
iterator.Remove(); 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); status_t status = read_table_of_contents(fd, toc, 2048);
// there has to be at least a single audio track // If we succeeded in reading the toc, check the tracks in the
if (status == B_OK && count_audio_tracks(toc) == 0) // partition, which may not be the whole CD, and if any are audio,
status = B_BAD_TYPE; // 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); free(toc);
return status; return status;
} }
@@ -1740,7 +1772,8 @@ cdda_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
Volume* volume = (Volume*)_volume->private_volume; Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node; 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); 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; Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node; 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()) if ((Inode*)_node->private_node != &volume->RootNode())
return B_BAD_VALUE; 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; 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); free(cookie);
return 0; return 0;
@@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2009, Haiku, Inc. * Copyright 2003-2011, Haiku, Inc.
* Distributed under the terms of the MIT license. * Distributed under the terms of the MIT license.
* *
* Authors: * Authors:
@@ -86,9 +86,9 @@ public:
off_t start_lba; off_t start_lba;
uint8 control; uint8 control;
//!< only used to give what are probably useless warnings // Used to check for Yellow/Red Book mixed-mode CDs.
uint8 adr; 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; int count;
header = (cdrom_table_of_contents_header*)data; 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); header->length = B_BENDIAN_TO_HOST_INT16(header->length);
count = (header->length - 2) count = (header->length - 2)
/ sizeof(cdrom_full_table_of_contents_entry); / sizeof(cdrom_full_table_of_contents_entry);
count = _AdjustForYellowBook(entries, count);
error = _ParseTableOfContents(entries, count); error = _ParseTableOfContents(entries, count);
// Dump(); // Dump();
if (!error) { 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 /*! \brief Reads through the given table of contents data and creates an
unsorted, unverified (i.e. non-error-checked) list of sessions and tracks. 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); session->track_list.Add(track);
} else { } else {
WARN(("%s: warning: illegal point 0x%2x found in table of " WARN(("%s: warning: illegal point 0x%2x found in table of "
"contents\n", kModuleDebugName, entries[i].point)); "contents\n", kModuleDebugName, point));
} }
break; break;
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2009, Haiku, Inc. * Copyright 2003-2011, Haiku, Inc.
* Distributed under the terms of the MIT license. * Distributed under the terms of the MIT license.
* *
* Authors: * Authors:
@@ -32,27 +32,33 @@ class Session;
*/ */
class Disc { class Disc {
public: public:
Disc(int fd); Disc(int fd);
~Disc(); ~Disc();
status_t InitCheck(); status_t InitCheck();
Session* GetSession(int32 index); Session* GetSession(int32 index);
void Dump(); void Dump();
// CDs and DVDs are required to have a block size of 2K by // CDs and DVDs are required to have a block size of 2K by
// the SCSI-3 standard // the SCSI-3 standard
static const int kBlockSize = 2048; static const int kBlockSize = 2048;
private: private:
status_t _ParseTableOfContents( uint32 _AdjustForYellowBook(
cdrom_full_table_of_contents_entry entries[], cdrom_full_table_of_contents_entry
uint32 count); entries[],
void _SortAndRemoveDuplicates(); uint32 count);
status_t _CheckForErrorsAndWarnings(); status_t _ParseTableOfContents(
cdrom_full_table_of_contents_entry
entries[],
uint32 count);
void _SortAndRemoveDuplicates();
status_t _CheckForErrorsAndWarnings();
status_t fInitStatus; private:
List* fSessionList; status_t fInitStatus;
List* fSessionList;
}; };
@@ -61,32 +67,35 @@ private:
*/ */
class Session { class Session {
public: public:
~Session(); ~Session();
off_t Offset() { return fOffset; } off_t Offset() { return fOffset; }
off_t Size() { return fSize; } off_t Size() { return fSize; }
uint32 BlockSize() { return fBlockSize; } uint32 BlockSize() { return fBlockSize; }
int32 Index() { return fIndex; } int32 Index() { return fIndex; }
uint32 Flags() { return fFlags; } uint32 Flags() { return fFlags; }
const char* Type() { return fType; } const char* Type() { return fType; }
private: private:
friend class Disc; friend class Disc;
Session(off_t offset, off_t size, uint32 blockSize, Session(off_t offset, off_t size,
int32 index, uint32 flags, const char* type); uint32 blockSize, int32 index, uint32 flags,
const char* type);
Session(const Session& ref); Session(const Session& other);
// not implemented // not implemented
Session& operator=(const Session& ref); Session& operator=(const Session& other);
// not implemented // not implemented
off_t fOffset; private:
off_t fSize; off_t fOffset;
uint32 fBlockSize; off_t fSize;
int32 fIndex; uint32 fBlockSize;
uint32 fFlags; int32 fIndex;
char* fType; uint32 fFlags;
char* fType;
}; };
#endif // _DISC_H #endif // _DISC_H
@@ -52,12 +52,17 @@ identify_partition(int fd, partition_data *partition, void **cookie)
&& geometry.device_type == B_CD) { && geometry.device_type == B_CD) {
Disc *disc = new(std::nothrow) Disc(fd); Disc *disc = new(std::nothrow) Disc(fd);
if (disc != NULL && disc->InitCheck() == B_OK) { 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); *cookie = static_cast<void*>(disc);
result = 0.7;
} else } else
delete disc; delete disc;
} }
PRINT(("returning %ld\n", int32(result * 10000))); PRINT(("returning %g\n", result));
return result; return result;
} }