2 * Copyright 2007-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
9 #include <KernelExport.h>
10 #include <device/scsi.h>
20 struct cdtext_pack_data
{
24 uint8 character_position
: 4;
25 uint8 block_number
: 3;
26 uint8 double_byte
: 1;
37 static const uint32 kBufferSize
= 16384;
38 static const uint32 kSenseSize
= 1024;
41 // #pragma mark - string functions
45 copy_string(const char *string
)
47 if (string
== NULL
|| !string
[0])
50 return strdup(string
);
55 to_utf8(const char* string
)
60 // TODO: assume CP1252 or ISO-8859-1 character set for now
61 while (uint32 c
= (uint8
)string
[0]) {
64 if (out
>= sizeof(buffer
) - 1)
66 // ASCII character: no change needed
70 // Windows CP-1252 - Use a lookup table
71 static const uint32 lookup
[] = {
72 0x20AC, 0, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
73 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0, 0x017D, 0,
74 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
75 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0, 0x017E, 0x0178
81 // Convert to 2 or 3-byte representation
83 // invalid character, ignore
84 } else if (c
< 0x800) {
85 if (out
>= sizeof(buffer
) - 2)
87 buffer
[out
++] = 0xc0 | (c
>> 6);
88 buffer
[out
++] = 0x80 | (c
& 0x3f);
90 if (out
>= sizeof(buffer
) - 3)
92 buffer
[out
++] = 0xe0 | (c
>> 12);
93 buffer
[out
++] = 0x80 | ((c
>> 6) & 0x3f);
94 buffer
[out
++] = 0x80 | (c
& 0x3f);
100 buffer
[out
++] = '\0';
102 char *copy
= (char *)malloc(out
);
106 memcpy(copy
, buffer
, out
);
114 return isspace(c
) || c
== '-' || c
== '/' || c
== '\\';
119 sanitize_string(char *&string
)
124 // strip garbage at the start
126 uint32 length
= strlen(string
);
128 while (is_garbage(string
[garbage
])) {
134 memmove(string
, string
+ garbage
, length
+ 1);
136 // strip garbage from the end
138 while (length
> 1 && isspace(string
[length
- 1])) {
139 string
[--length
] = '\0';
143 // free string if it's empty
150 //! Finds the first occurrence of \a find in \a string, ignores case.
152 find_string(const char *string
, const char *find
)
154 if (string
== NULL
|| find
== NULL
)
157 char first
= tolower(find
[0]);
159 return (char *)string
;
161 int32 findLength
= strlen(find
) - 1;
164 for (; string
[0]; string
++) {
165 if (tolower(string
[0]) != first
)
167 if (strncasecmp(string
+ 1, find
, findLength
) == 0)
168 return (char *)string
;
176 cut_string(char *string
, const char *cut
)
178 if (string
== NULL
|| cut
== NULL
)
181 char *found
= find_string(string
, cut
);
183 uint32 foundLength
= strlen(found
);
184 uint32 cutLength
= strlen(cut
);
185 memmove(found
, found
+ cutLength
, foundLength
+ 1 - cutLength
);
191 sanitize_album(cdtext
&text
)
193 cut_string(text
.album
, text
.artist
);
194 sanitize_string(text
.album
);
196 if (text
.album
!= NULL
&& !strcasecmp(text
.album
, "My CD")) {
197 // don't laugh, people really do that!
202 if ((text
.artist
== NULL
|| text
.artist
[0] == '\0') && text
.album
!= NULL
) {
203 // try to extract artist from album
204 char *space
= strstr(text
.album
, " ");
207 text
.artist
= text
.album
;
208 text
.album
= copy_string(space
+ 2);
210 sanitize_string(text
.artist
);
211 sanitize_string(text
.album
);
218 sanitize_titles(cdtext
&text
)
220 for (uint8 i
= 0; i
< text
.track_count
; i
++) {
221 cut_string(text
.titles
[i
], "(Album Version)");
222 sanitize_string(text
.titles
[i
]);
223 sanitize_string(text
.artists
[i
]);
225 if (text
.artists
[i
] != NULL
&& text
.artist
!= NULL
226 && !strcasecmp(text
.artists
[i
], text
.artist
)) {
227 // if the title artist is the same as the main artist, remove it
228 free(text
.artists
[i
]);
229 text
.artists
[i
] = NULL
;
232 if (text
.titles
[i
] != NULL
&& text
.titles
[i
][0] == '\t' && i
> 0)
233 text
.titles
[i
] = copy_string(text
.titles
[i
- 1]);
239 single_case(const char *string
, bool &upper
, bool &first
)
245 while (!isalpha(string
[0])) {
250 upper
= isupper(string
[0]) != 0;
252 } else if ((isupper(string
[0]) != 0) ^ upper
)
263 capitalize_string(char *string
)
268 bool newWord
= isalpha(string
[0]) || isspace(string
[0]);
270 if (isalpha(string
[0])) {
272 string
[0] = toupper(string
[0]);
275 string
[0] = tolower(string
[0]);
276 } else if (string
[0] != '\'')
285 correct_case(cdtext
&text
)
287 // check if all titles share a single case
290 if (!single_case(text
.album
, upper
, first
)
291 || !single_case(text
.artist
, upper
, first
))
294 for (int32 i
= 0; i
< text
.track_count
; i
++) {
295 if (!single_case(text
.titles
[i
], upper
, first
)
296 || !single_case(text
.artists
[i
], upper
, first
))
300 // If we get here, everything has a single case; we fix that
301 // and capitalize each word
303 capitalize_string(text
.album
);
304 capitalize_string(text
.artist
);
305 for (int32 i
= 0; i
< text
.track_count
; i
++) {
306 capitalize_string(text
.titles
[i
]);
307 capitalize_string(text
.artists
[i
]);
312 // #pragma mark - CD-Text
322 memset(titles
, 0, sizeof(titles
));
323 memset(artists
, 0, sizeof(artists
));
333 for (uint8 i
= 0; i
< track_count
; i
++) {
341 is_string_id(uint8 id
)
343 return id
>= kTrackID
&& id
<= kMessageID
;
347 /*! Parses a \a pack data into the provided text buffer; the corresponding
348 track number will be left in \a track, and the type of the data in \a id.
349 The pack data is explained in SCSI MMC-3.
351 \a id, \a track, and \a state must stay constant between calls to this
352 function. \a state must be initialized to zero for the first call.
355 parse_pack_data(cdtext_pack_data
*&pack
, uint32
&packLeft
,
356 cdtext_pack_data
*&lastPack
, uint8
&id
, uint8
&track
, uint8
&state
,
357 char *buffer
, size_t &length
)
359 if (packLeft
< sizeof(cdtext_pack_data
))
362 uint8 number
= pack
->number
;
363 size_t size
= length
;
366 // we had a terminated string and a missing track
369 memcpy(buffer
, lastPack
->text
+ state
, 12 - state
);
370 if (pack
->track
- track
== 1)
373 state
+= strnlen(buffer
, 12 - state
);
383 size_t position
= pack
->character_position
;
384 if (position
> 0 && lastPack
!= NULL
) {
385 memcpy(buffer
, &lastPack
->text
[12 - position
], position
);
389 while (id
== pack
->id
&& track
== pack
->track
) {
391 dprintf("%u.%u.%u, %u.%u.%u, ", pack
->id
, pack
->track
, pack
->number
,
392 pack
->double_byte
, pack
->block_number
, pack
->character_position
);
393 for (int32 i
= 0; i
< 12; i
++) {
394 if (isprint(pack
->text
[i
]))
395 dprintf("%c", pack
->text
[i
]);
401 if (is_string_id(id
)) {
402 // TODO: support double byte characters
403 if (length
+ 12 < size
) {
404 memcpy(buffer
+ length
, pack
->text
, 12);
409 packLeft
-= sizeof(cdtext_pack_data
);
410 if (packLeft
< sizeof(cdtext_pack_data
))
417 if (pack
->number
!= number
)
421 if (id
== pack
->id
) {
422 length
-= pack
->character_position
;
425 buffer
[length
] = '\0';
427 if (pack
->track
> lastPack
->track
+ 1) {
428 // there is a missing track
429 for (int32 i
= 0; i
< 12; i
++) {
430 if (lastPack
->text
[i
] == '\0') {
431 state
= i
+ (lastPack
->double_byte
? 2 : 1);
443 dump_cdtext(cdtext
&text
)
446 dprintf("Album: \"%s\"\n", text
.album
);
448 dprintf("Artist: \"%s\"\n", text
.artist
);
449 for (uint8 i
= 0; i
< text
.track_count
; i
++) {
450 dprintf("Track %02u: \"%s\"%s%s%s\n", i
+ 1, text
.titles
[i
],
451 text
.artists
[i
] ? " (" : "", text
.artists
[i
] ? text
.artists
[i
] : "",
452 text
.artists
[i
] ? ")" : "");
458 dump_toc(scsi_toc_toc
*toc
)
460 int32 numTracks
= toc
->last_track
+ 1 - toc
->first_track
;
462 for (int32 i
= 0; i
< numTracks
; i
++) {
463 scsi_toc_track
& track
= toc
->tracks
[i
];
464 scsi_cd_msf
& next
= toc
->tracks
[i
+ 1].start
.time
;
465 // the last track is always lead-out
466 scsi_cd_msf
& start
= toc
->tracks
[i
].start
.time
;
469 uint64 diff
= next
.minute
* kFramesPerMinute
470 + next
.second
* kFramesPerSecond
+ next
.frame
471 - start
.minute
* kFramesPerMinute
472 - start
.second
* kFramesPerSecond
- start
.frame
;
473 length
.minute
= diff
/ kFramesPerMinute
;
474 length
.second
= (diff
% kFramesPerMinute
) / kFramesPerSecond
;
475 length
.frame
= diff
% kFramesPerSecond
;
477 dprintf("%02u. %02u:%02u.%02u (length %02u:%02u.%02u)\n",
478 track
.track_number
, start
.minute
, start
.second
, start
.frame
,
479 length
.minute
, length
.second
, length
.frame
);
485 read_frames(int fd
, off_t firstFrame
, uint8
*buffer
, size_t count
)
487 size_t framesLeft
= count
;
489 while (framesLeft
> 0) {
490 // If the initial count was >= 32, and not a multiple of 8, and the
491 // ioctl fails, we switch to reading 8 frames at a time. However the
492 // last read can read between 1 and 7 frames only, to not overflow
494 count
= std::min(count
, framesLeft
);
497 read
.start_m
= firstFrame
/ kFramesPerMinute
;
498 read
.start_s
= (firstFrame
/ kFramesPerSecond
) % 60;
499 read
.start_f
= firstFrame
% kFramesPerSecond
;
501 read
.length_m
= count
/ kFramesPerMinute
;
502 read
.length_s
= (count
/ kFramesPerSecond
) % 60;
503 read
.length_f
= count
% kFramesPerSecond
;
505 read
.buffer_length
= count
* kFrameSize
;
506 read
.buffer
= (char *)buffer
;
509 if (ioctl(fd
, B_SCSI_READ_CD
, &read
) < 0) {
510 // drive couldn't read data - try again to read with a smaller block size
522 buffer
+= count
* kFrameSize
;
532 read_table_of_contents(int fd
, uint32 track
, uint8 format
, uint8
*buffer
,
535 raw_device_command raw
;
536 uint8
*senseData
= (uint8
*)malloc(kSenseSize
);
537 if (senseData
== NULL
)
540 memset(&raw
, 0, sizeof(raw_device_command
));
541 memset(senseData
, 0, kSenseSize
);
542 memset(buffer
, 0, bufferSize
);
544 scsi_cmd_read_toc
&toc
= *(scsi_cmd_read_toc
*)&raw
.command
;
545 toc
.opcode
= SCSI_OP_READ_TOC
;
549 toc
.allocation_length
= B_HOST_TO_BENDIAN_INT16(bufferSize
);
551 raw
.command_length
= 10;
552 raw
.flags
= B_RAW_DEVICE_DATA_IN
| B_RAW_DEVICE_REPORT_RESIDUAL
553 | B_RAW_DEVICE_SHORT_READ_VALID
;
557 raw
.data_length
= bufferSize
;
558 raw
.timeout
= 10000000LL; // 10 secs
559 raw
.sense_data
= senseData
;
560 raw
.sense_data_length
= sizeof(kSenseSize
);
562 if (ioctl(fd
, B_RAW_DEVICE_COMMAND
, &raw
) == 0
563 && raw
.scsi_status
== 0 && raw
.cam_status
== 1) {
573 // #pragma mark - exported functions
577 read_cdtext(int fd
, struct cdtext
&cdtext
)
579 uint8
*buffer
= (uint8
*)malloc(kBufferSize
);
583 // do it twice, just in case...
584 // (at least my CD-ROM sometimes returned broken data on first try)
585 read_table_of_contents(fd
, 1, SCSI_TOC_FORMAT_CD_TEXT
, buffer
,
587 if (read_table_of_contents(fd
, 1, SCSI_TOC_FORMAT_CD_TEXT
, buffer
,
588 kBufferSize
) != B_OK
) {
593 scsi_toc_general
*header
= (scsi_toc_general
*)buffer
;
595 uint32 packLength
= B_BENDIAN_TO_HOST_INT16(header
->data_length
) - 2;
596 cdtext_pack_data
*pack
= (cdtext_pack_data
*)(header
+ 1);
597 cdtext_pack_data
*lastPack
= NULL
;
603 // TODO: determine encoding!
606 size_t length
= sizeof(text
);
608 if (!parse_pack_data(pack
, packLength
, lastPack
, id
, track
,
609 state
, text
, length
))
615 if (cdtext
.album
== NULL
)
616 cdtext
.album
= to_utf8(text
);
617 } else if (track
<= kMaxTracks
) {
618 if (cdtext
.titles
[track
- 1] == NULL
)
619 cdtext
.titles
[track
- 1] = to_utf8(text
);
620 if (track
> cdtext
.track_count
)
621 cdtext
.track_count
= track
;
627 if (cdtext
.artist
== NULL
)
628 cdtext
.artist
= to_utf8(text
);
629 } else if (track
<= kMaxTracks
) {
630 if (cdtext
.artists
[track
- 1] == NULL
)
631 cdtext
.artists
[track
- 1] = to_utf8(text
);
636 if (is_string_id(id
))
637 dprintf("UNKNOWN %u: \"%s\"\n", id
, text
);
644 if (cdtext
.artist
== NULL
&& cdtext
.album
== NULL
)
647 for (int i
= 0; i
< cdtext
.track_count
; i
++) {
648 if (cdtext
.titles
[i
] == NULL
)
652 sanitize_string(cdtext
.artist
);
653 sanitize_album(cdtext
);
654 sanitize_titles(cdtext
);
655 correct_case(cdtext
);
663 read_table_of_contents(int fd
, scsi_toc_toc
*toc
, size_t length
)
665 status_t status
= read_table_of_contents(fd
, 1, SCSI_TOC_FORMAT_TOC
,
666 (uint8
*)toc
, length
);
670 // make sure the values in the TOC make sense
672 int32 lastTrack
= toc
->last_track
+ 1 - toc
->first_track
;
673 size_t dataLength
= B_BENDIAN_TO_HOST_INT16(toc
->data_length
) + 2;
674 if (dataLength
< sizeof(scsi_toc_toc
) || lastTrack
<= 0)
677 if (length
> dataLength
)
680 length
-= sizeof(scsi_toc_general
);
682 if (lastTrack
* sizeof(scsi_toc_track
) > length
)
683 toc
->last_track
= length
/ sizeof(scsi_toc_track
) + toc
->first_track
;
691 read_cdda_data(int fd
, off_t endFrame
, off_t offset
, void *data
, size_t length
,
692 off_t bufferOffset
, void *buffer
, size_t bufferSize
)
694 if (bufferOffset
>= 0 && bufferOffset
<= offset
+ (off_t
)length
695 && bufferOffset
+ (off_t
)bufferSize
> offset
) {
696 if (offset
>= bufferOffset
) {
697 // buffer reaches into the beginning of the request
698 off_t dataOffset
= offset
- bufferOffset
;
699 size_t bytes
= min_c(bufferSize
- dataOffset
, length
);
700 if (user_memcpy(data
, (uint8
*)buffer
+ dataOffset
, bytes
) < B_OK
)
701 return B_BAD_ADDRESS
;
703 data
= (void *)((uint8
*)data
+ bytes
);
706 } else if (offset
< bufferOffset
707 && offset
+ length
< bufferOffset
+ bufferSize
) {
708 // buffer overlaps at the end of the request
709 off_t dataOffset
= bufferOffset
- offset
;
710 size_t bytes
= length
- dataOffset
;
711 if (user_memcpy((uint8
*)data
+ dataOffset
, buffer
, bytes
) < B_OK
)
712 return B_BAD_ADDRESS
;
716 // we don't handle the case where we would need to split the request
720 off_t frame
= offset
/ kFrameSize
;
721 uint32 count
= bufferSize
/ kFrameSize
;
722 if (frame
+ count
> endFrame
)
723 count
= endFrame
- frame
;
725 status_t status
= read_frames(fd
, frame
, (uint8
*)buffer
, count
);
729 off_t dataOffset
= offset
% kFrameSize
;
730 size_t bytes
= bufferSize
- dataOffset
;
734 if (user_memcpy(data
, (uint8
*)buffer
+ dataOffset
, bytes
) < B_OK
)
735 return B_BAD_ADDRESS
;
737 data
= (void *)((uint8
*)data
+ bytes
);