You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
668 lines
18 KiB
668 lines
18 KiB
/*
|
|
* $Id$
|
|
*
|
|
* This file is part of WorkMan, the civilized CD player library
|
|
* (c) 1991-1997 by Steven Grimm (original author)
|
|
* (c) by Dirk Försterling (current 'author' = maintainer)
|
|
* The maintainer can be contacted by his e-mail address:
|
|
* milliByte@DeathsDoor.com
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*
|
|
* Frontend functions for sending raw SCSI commands to the CD-ROM drive.
|
|
* These depend on wm_scsi(), which should be defined in each platform
|
|
* module.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "include/wm_config.h"
|
|
#include "include/wm_struct.h"
|
|
#include "include/wm_scsi.h"
|
|
#include "include/wm_platform.h"
|
|
#include "include/wm_helpers.h"
|
|
#include "include/wm_cdrom.h"
|
|
#include "include/wm_cdtext.h"
|
|
|
|
#define SCMD_INQUIRY 0x12
|
|
#define SCMD_MODE_SELECT 0x15
|
|
#define SCMD_MODE_SENSE 0x1a
|
|
#define SCMD_START_STOP 0x1b
|
|
#define SCMD_PREVENT 0x1e
|
|
#define SCMD_READ_SUBCHANNEL 0x42
|
|
#define SCMD_READ_TOC 0x43
|
|
#define SCMD_PLAY_AUDIO_MSF 0x47
|
|
#define SCMD_PAUSE_RESUME 0x4b
|
|
|
|
#define SUBTQ_STATUS_INVALID 0x00
|
|
#define SUBTQ_STATUS_PLAY 0x11
|
|
#define SUBTQ_STATUS_PAUSE 0x12
|
|
#define SUBTQ_STATUS_DONE 0x13
|
|
#define SUBTQ_STATUS_ERROR 0x14
|
|
#define SUBTQ_STATUS_NONE 0x15
|
|
#define SUBTQ_STATUS_NO_DISC 0x17 /* Illegal, but Toshiba returns it. */
|
|
#define SUBTQ_ILLEGAL 0xff
|
|
|
|
#define PAGE_AUDIO 0x0e
|
|
#define LEADOUT 0xaa
|
|
|
|
#define WM_MSG_CLASS WM_MSG_CLASS_SCSI
|
|
|
|
/* local prototypes */
|
|
int wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len );
|
|
int wm_scsi2_pause_resume(struct wm_drive *d, int resume);
|
|
int wm_scsi2_prevent(struct wm_drive *d, int prevent);
|
|
int wm_scsi2_play(struct wm_drive *d, int sframe, int eframe);
|
|
int wm_scsi2_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe);
|
|
int wm_scsi2_get_cdlen(struct wm_drive *d, int frames);
|
|
int wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode,
|
|
int *mode, int *pos, int *track, int *ind);
|
|
int wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks);
|
|
int wm_scsi2_pause(struct wm_drive *d);
|
|
int wm_scsi2_resume(struct wm_drive *d);
|
|
int wm_scsi2_stop(struct wm_drive *d);
|
|
int wm_scsi2_eject(struct wm_drive *d);
|
|
int wm_scsi2_closetray(struct wm_drive *d);
|
|
int wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right);
|
|
int wm_scsi2_set_volume(struct wm_drive *d, int left, int right);
|
|
/* local prototypes END */
|
|
|
|
/*
|
|
* Send a SCSI command over the bus, with all the CDB bytes specified
|
|
* as unsigned char parameters. This doesn't use varargs because some
|
|
* systems have stdargs instead and the number of bytes in a CDB is
|
|
* limited to 12 anyway.
|
|
*
|
|
* d Drive structure
|
|
* buf Buffer for data, both sending and receiving
|
|
* len Size of buffer
|
|
* dir TRUE if the command expects data to be returned in the buffer.
|
|
* a0- CDB bytes. Either 6, 10, or 12 of them, depending on the command.
|
|
*/
|
|
/*VARARGS4*/
|
|
int
|
|
sendscsi( struct wm_drive *d, void *buf,
|
|
unsigned int len, int dir,
|
|
unsigned char a0, unsigned char a1,
|
|
unsigned char a2, unsigned char a3,
|
|
unsigned char a4, unsigned char a5,
|
|
unsigned char a6, unsigned char a7,
|
|
unsigned char a8, unsigned char a9,
|
|
unsigned char a10, unsigned char a11 )
|
|
|
|
{
|
|
int cdblen = 0;
|
|
unsigned char cdb[12];
|
|
|
|
cdb[0] = a0;
|
|
cdb[1] = a1;
|
|
cdb[2] = a2;
|
|
cdb[3] = a3;
|
|
cdb[4] = a4;
|
|
cdb[5] = a5;
|
|
|
|
switch ((a0 >> 5) & 7) {
|
|
case 0:
|
|
cdblen = 6;
|
|
break;
|
|
|
|
case 5:
|
|
cdb[10] = a10;
|
|
cdb[11] = a11;
|
|
cdblen = 12;
|
|
|
|
case 1:
|
|
case 2:
|
|
case 6: /* assume 10-byte vendor-specific codes for now */
|
|
cdb[6] = a6;
|
|
cdb[7] = a7;
|
|
cdb[8] = a8;
|
|
cdb[9] = a9;
|
|
if (! cdblen)
|
|
cdblen = 10;
|
|
break;
|
|
}
|
|
|
|
return (wm_scsi(d, cdb, cdblen, buf, len, dir));
|
|
}
|
|
|
|
/*
|
|
* Send a MODE SENSE command and return the results (minus header cruft)
|
|
* in a user buffer.
|
|
*
|
|
* d Drive structure
|
|
* page Number of page to query (plus page control bits, if any)
|
|
* buf Result buffer
|
|
*/
|
|
int
|
|
wm_scsi_mode_sense( struct wm_drive *d, unsigned char page, unsigned char *buf )
|
|
{
|
|
unsigned char pagebuf[255];
|
|
int status, i, len, offset;
|
|
|
|
status = sendscsi(d, pagebuf, sizeof(pagebuf), 1, SCMD_MODE_SENSE, 0,
|
|
page, 0, sizeof(pagebuf), 0,0,0,0,0,0,0);
|
|
if (status < 0)
|
|
return (status);
|
|
|
|
/*
|
|
* The first byte of the returned data is the transfer length. Then
|
|
* two more bytes and the length of whatever header blocks are in
|
|
* front of the page we want.
|
|
*/
|
|
len = pagebuf[0] - pagebuf[3] - 3;
|
|
offset = pagebuf[3] + 4;
|
|
for (i = 0; i < len; i++)
|
|
buf[i] = pagebuf[offset + i];
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Send a MODE SELECT command.
|
|
*
|
|
* d Drive structure
|
|
* buf Page buffer (no need to put on block descriptors)
|
|
* len Size of page
|
|
*/
|
|
int
|
|
wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len )
|
|
{
|
|
unsigned char pagebuf[255];
|
|
int i;
|
|
|
|
pagebuf[0] = pagebuf[1] = pagebuf[2] = pagebuf[3] = 0;
|
|
for (i = 0; i < (int) len; i++)
|
|
pagebuf[i + 4] = buf[i];
|
|
|
|
return (sendscsi(d, pagebuf, len + 4, 0, SCMD_MODE_SELECT, 0x10, 0,
|
|
0, len + 4, 0,0,0,0,0,0,0));
|
|
}
|
|
|
|
/*
|
|
* Send an INQUIRY command to get the drive type.
|
|
*
|
|
* d Drive structure
|
|
* vendor Buffer for vendor name (8 bytes + null)
|
|
* model Buffer for model name (16 bytes + null)
|
|
* rev Buffer for revision level (4 bytes + null)
|
|
*
|
|
* The above string lengths apply to the SCSI INQUIRY command. The
|
|
* actual WorkMan drive structure reserves 31 bytes + NULL per entry.
|
|
*
|
|
* If the model name begins with "CD-ROM" and zero or more spaces, that will
|
|
* all be stripped off since it's just extra junk to WorkMan.
|
|
*/
|
|
int
|
|
wm_scsi_get_drive_type( struct wm_drive *d, char *vendor,
|
|
char *model, char *rev )
|
|
{
|
|
/* removed unsigned*/
|
|
char *s, *t, buf[36];
|
|
memset(buf, 0, 36);
|
|
|
|
wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "Sending SCSI inquiry command...\n");
|
|
if (sendscsi(d, buf, 36, 1, SCMD_INQUIRY, 0, 0, 0, 36, 0,0,0,0,0,0,0))
|
|
{
|
|
sprintf( vendor, WM_STR_GENVENDOR);
|
|
sprintf( model, WM_STR_GENMODEL);
|
|
sprintf( rev, WM_STR_GENREV);
|
|
wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_ERROR, "SCSI Inquiry command not supported in this context\n");
|
|
return -1;
|
|
}
|
|
|
|
wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_DEBUG, "sent.\n");
|
|
|
|
memcpy(vendor, buf + 8, 8);
|
|
vendor[8] = '\0';
|
|
memcpy(model, buf + 16, 16);
|
|
model[16] = '\0';
|
|
memcpy(rev, buf + 32, 4);
|
|
rev[4] = '\0';
|
|
wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "SCSI Inquiry result: [%s|%s|%s]\n", vendor, model, rev);
|
|
|
|
|
|
/* Remove "CD-ROM " from the model. */
|
|
if (! strncmp(model, "CD-ROM", 6))
|
|
{
|
|
s = model + 6;
|
|
t = model;
|
|
while (*s == ' ' || *s == '\t')
|
|
s++;
|
|
while( (*t++ = *s++) )
|
|
;
|
|
}
|
|
wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "scsi: Cooked data: %s %s rev. %s\n", vendor, model,rev);
|
|
return (0);
|
|
} /* wm_scsi_get_drive_type() */
|
|
|
|
/*
|
|
* Send a SCSI-2 PAUSE/RESUME command. "resume" is 1 to resume, 0 to pause.
|
|
*/
|
|
int
|
|
wm_scsi2_pause_resume(struct wm_drive *d, int resume)
|
|
{
|
|
return (sendscsi(d, NULL, 0, 0, SCMD_PAUSE_RESUME, 0, 0, 0, 0, 0, 0,
|
|
0, resume ? 1 : 0, 0,0,0));
|
|
}
|
|
|
|
/*
|
|
* Send a SCSI-2 "prevent media removal" command. "prevent" is 1 to lock
|
|
* caddy in.
|
|
*/
|
|
int
|
|
wm_scsi2_prevent(struct wm_drive *d, int prevent)
|
|
{
|
|
return (sendscsi(d, NULL, 0, 0, SCMD_PREVENT, 0, 0, 0, 0, 0, 0,
|
|
0, prevent ? 1 : 0, 0,0,0));
|
|
}
|
|
|
|
/*
|
|
* Send a SCSI-2 PLAY AUDIO MSF command. Pass the starting and ending
|
|
* frame numbers.
|
|
*/
|
|
int
|
|
wm_scsi2_play(struct wm_drive *d, int sframe, int eframe)
|
|
{
|
|
return (sendscsi(d, NULL, 0, 0, SCMD_PLAY_AUDIO_MSF, 0, 0,
|
|
sframe / (60 * 75), (sframe / 75) % 60, sframe % 75,
|
|
eframe / (60 * 75), (eframe / 75) % 60, eframe % 75,
|
|
0,0,0));
|
|
}
|
|
|
|
/*
|
|
* Send a SCSI-2 READ TOC command to get the data for a particular track.
|
|
* Fill in track information from the returned data.
|
|
*/
|
|
int
|
|
wm_scsi2_get_trackinfo(struct wm_drive *d, int track,
|
|
int *data, int *startframe)
|
|
{
|
|
unsigned char buf[12]; /* one track's worth of info */
|
|
|
|
if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 2,
|
|
0, 0, 0, 0, track, sizeof(buf) / 256,
|
|
sizeof(buf) % 256, 0,0,0))
|
|
return (-1);
|
|
|
|
*data = buf[5] & 4 ? 1 : 0;
|
|
*startframe = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Get the starting frame for the leadout area (which should be the same as
|
|
* the length of the disc as far as WorkMan is concerned).
|
|
*/
|
|
int
|
|
wm_scsi2_get_cdlen(struct wm_drive *d, int frames)
|
|
{
|
|
int tmp;
|
|
return (wm_scsi2_get_trackinfo(d, LEADOUT, &tmp, &frames));
|
|
}
|
|
|
|
/*
|
|
* Get the current status of the drive by sending the appropriate SCSI-2
|
|
* READ SUB-CHANNEL command.
|
|
*/
|
|
int
|
|
wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode,
|
|
int *mode, int *pos, int *track, int *ind)
|
|
{
|
|
unsigned char buf[48];
|
|
|
|
/* If we can't get status, the CD is ejected, so default to that. */
|
|
*mode = WM_CDM_EJECTED;
|
|
|
|
/* Is the device open? */
|
|
if (d->fd < 0)
|
|
{
|
|
/*
|
|
* stupid somehow, but necessary this time
|
|
*/
|
|
switch( wmcd_open( d ) ) {
|
|
|
|
case -1: /* error */
|
|
return (-1);
|
|
|
|
case 1: /* retry */
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
/* If we can't read status, the CD has been ejected. */
|
|
buf[1] = SUBTQ_ILLEGAL;
|
|
if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_SUBCHANNEL, 2, 64, 1,
|
|
0, 0, 0, sizeof(buf) / 256, sizeof(buf) % 256, 0,0,0))
|
|
return (0);
|
|
|
|
switch (buf[1]) {
|
|
case SUBTQ_STATUS_PLAY:
|
|
*mode = WM_CDM_PLAYING;
|
|
*track = buf[6];
|
|
*ind = buf[7];
|
|
*pos = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
|
|
break;
|
|
|
|
case SUBTQ_STATUS_PAUSE:
|
|
if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
|
|
{
|
|
*mode = WM_CDM_PAUSED;
|
|
*track = buf[6];
|
|
*ind = buf[7];
|
|
*pos = buf[9] * 60 * 75 +
|
|
buf[10] * 75 +
|
|
buf[11];
|
|
}
|
|
else
|
|
*mode = WM_CDM_STOPPED;
|
|
break;
|
|
|
|
/*
|
|
* SUBTQ_STATUS_DONE is sometimes returned when the CD is idle,
|
|
* even though the spec says it should only be returned when an
|
|
* audio play operation finishes.
|
|
*/
|
|
case SUBTQ_STATUS_DONE:
|
|
case SUBTQ_STATUS_NONE:
|
|
case SUBTQ_STATUS_INVALID:
|
|
if (oldmode == WM_CDM_PLAYING)
|
|
*mode = WM_CDM_TRACK_DONE;
|
|
else
|
|
*mode = WM_CDM_STOPPED;
|
|
break;
|
|
|
|
/*
|
|
* This usually means there's no disc in the drive.
|
|
*/
|
|
case SUBTQ_STATUS_NO_DISC:
|
|
break;
|
|
|
|
/*
|
|
* This usually means the user ejected the CD manually.
|
|
*/
|
|
case SUBTQ_STATUS_ERROR:
|
|
break;
|
|
|
|
case SUBTQ_ILLEGAL: /* call didn't really succeed */
|
|
break;
|
|
|
|
default:
|
|
*mode = WM_CDM_UNKNOWN;
|
|
#ifndef NDEBUG
|
|
if( getenv( "WORKMAN_DEBUG" ) != NULL )
|
|
printf("wm_scsi2_get_drive_status: status is 0x%x\n",
|
|
buf[1]);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Get the number of tracks on the CD using the SCSI-2 READ TOC command.
|
|
*/
|
|
int
|
|
wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks)
|
|
{
|
|
unsigned char buf[4];
|
|
|
|
if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 0,
|
|
0, 0, 0, 0, 0, sizeof(buf) / 256,
|
|
sizeof(buf) % 256, 0,0,0))
|
|
return (-1);
|
|
|
|
*tracks = buf[3] - buf[2] + 1;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Pause the CD.
|
|
*/
|
|
int
|
|
wm_scsi2_pause(struct wm_drive *d)
|
|
{
|
|
return (wm_scsi2_pause_resume(d, 0));
|
|
}
|
|
|
|
/*
|
|
* Resume playing after a pause.
|
|
*/
|
|
int
|
|
wm_scsi2_resume(struct wm_drive *d)
|
|
{
|
|
return (wm_scsi2_pause_resume(d, 1));
|
|
}
|
|
|
|
/*
|
|
* Stop playing the CD by sending a START STOP UNIT command.
|
|
*/
|
|
int
|
|
wm_scsi2_stop(struct wm_drive *d)
|
|
{
|
|
return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 0, 0,0,0,0,0,0,0,0,0,0));
|
|
}
|
|
|
|
/*
|
|
* Eject the CD by sending a START STOP UNIT command.
|
|
*/
|
|
int
|
|
wm_scsi2_eject(struct wm_drive *d)
|
|
{
|
|
/* Unlock the disc (possibly unnecessary). */
|
|
if (wm_scsi2_prevent(d, 0))
|
|
return (-1);
|
|
wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for ejecting...\n");
|
|
return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0));
|
|
}
|
|
|
|
/*
|
|
* Something like a dummy. The SCSI-2 specs are too hard for me to
|
|
* understand here...
|
|
*
|
|
* If you have the correct command handy, please send the code to
|
|
* milliByte@DeathsDoor.com
|
|
*/
|
|
int
|
|
wm_scsi2_closetray(struct wm_drive *d)
|
|
{
|
|
wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for closing...\n");
|
|
return (sendscsi(d, NULL, 0,0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0));
|
|
}
|
|
|
|
/*
|
|
* Get the volume by doing a MODE SENSE command.
|
|
*/
|
|
int
|
|
wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right)
|
|
{
|
|
unsigned char mode[16];
|
|
|
|
*left = *right = -1;
|
|
|
|
/* Get the current audio parameters first. */
|
|
if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
|
|
return (-1);
|
|
|
|
*left = ((int) mode[9] * 100) / 255;
|
|
*right = ((int) mode[11] * 100) / 255;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Set the volume by doing a MODE SELECT command.
|
|
*/
|
|
int
|
|
wm_scsi2_set_volume(struct wm_drive *d, int left, int right)
|
|
{
|
|
unsigned char mode[16];
|
|
|
|
/* Get the current audio parameters first. */
|
|
if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
|
|
return (-1);
|
|
|
|
/* Tweak the volume part of the parameters. */
|
|
mode[9] = (left * 255) / 100;
|
|
mode[11] = (right * 255) / 100;
|
|
|
|
/* And send them back to the drive. */
|
|
return (wm_scsi_mode_select(d, mode, sizeof(mode)));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* wm_scsi_get_cdtext(drive, buffer, length)
|
|
*
|
|
* Return a buffer with cdtext-stream. buffer mus be allocated and filled
|
|
*
|
|
*
|
|
*------------------------------------------------------------------------*/
|
|
|
|
int
|
|
wm_scsi_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_length)
|
|
{
|
|
int ret;
|
|
unsigned char temp[8];
|
|
unsigned char *dynamic_temp;
|
|
int cdtext_possible;
|
|
unsigned short cdtext_data_length;
|
|
unsigned long feature_list_length;
|
|
#define IGNORE_FEATURE_LIST
|
|
#ifndef IGNORE_FEATURE_LIST
|
|
struct feature_list_header *pHeader;
|
|
struct feature_descriptor_cdread *pDescriptor;
|
|
#endif /* IGNORE_FEATURE_LIST */
|
|
|
|
dynamic_temp = NULL;
|
|
cdtext_possible = 0;
|
|
wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi_get_cdtext entered\n");
|
|
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: use GET_FEATURY_LIST(0x46)...\n");
|
|
ret = sendscsi(d, temp, 8, 1,
|
|
0x46, 0x02, 0x00, 0x1E, 0,
|
|
0, 0, 0, 8, 0, 0, 0);
|
|
|
|
if(ret)
|
|
{
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT ERROR: GET_FEATURY_LIST(0x46) not implemented or broken. ret = %i!\n", ret);
|
|
#ifndef IGNORE_FEATURE_LIST
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT ERROR: Try #define IGNORE_FEATURE_LIST in libwm/scsi.c\n");
|
|
#else
|
|
cdtext_possible = 1;
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: GET_FEATURY_LIST(0x46) ignored. It's OK, because many CDROMS don't implement this feature\n");
|
|
#endif /* IGNORE_FEATURE_LIST */
|
|
}
|
|
else
|
|
{
|
|
feature_list_length = temp[0]*0xFFFFFF + temp[1]*0xFFFF + temp[2]*0xFF + temp[3] + 4;
|
|
|
|
dynamic_temp = malloc(feature_list_length);
|
|
|
|
if(!dynamic_temp)
|
|
return -1;
|
|
|
|
memset(dynamic_temp, 0, feature_list_length);
|
|
ret = sendscsi(d, dynamic_temp, feature_list_length, 1,
|
|
0x46, 0x02, 0x00, 0x1E, 0, 0,
|
|
0, (feature_list_length>>8) & 0xFF, feature_list_length & 0xFF, 0, 0, 0);
|
|
|
|
|
|
#ifndef IGNORE_FEATURE_LIST
|
|
if(!ret)
|
|
{
|
|
pHeader = (struct feature_list_header*)dynamic_temp;
|
|
/* printf("length = %i, profile = 0x%02X%02X\n", pHeader->length_lsb, pHeader->profile_msb, pHeader->profile_lsb);*/
|
|
pDescriptor = (struct feature_descriptor_cdread*)(dynamic_temp + sizeof(struct feature_list_header));
|
|
/* printf("code = 0x%02X%02X, settings = 0x%02X, add_length = %i, add_settings = 0x%02X \n",
|
|
pDescriptor->feature_code_msb, pDescriptor->feature_code_lsb, pDescriptor->settings,
|
|
pDescriptor->add_length, pDescriptor->add_settings);*/
|
|
|
|
cdtext_possible = pDescriptor->add_settings & 0x01;
|
|
}
|
|
else
|
|
{
|
|
cdtext_possible = 0;
|
|
}
|
|
|
|
#else
|
|
cdtext_possible = 1;
|
|
#endif /* IGNORE_FEATURE_LIST */
|
|
|
|
free (dynamic_temp);
|
|
dynamic_temp = 0;
|
|
}
|
|
|
|
if(!cdtext_possible)
|
|
{
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: GET_FEATURY_LIST(0x46) says, CDTEXT is not present!\n");
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read, how long CDTEXT is?\n");
|
|
ret = sendscsi(d, temp, 4, 1,
|
|
SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0,
|
|
0, 0, 4, 0, 0, 0);
|
|
|
|
if(ret)
|
|
{
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
|
|
"CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret);
|
|
}
|
|
else
|
|
{
|
|
cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */
|
|
/* cdtext_data_length%18 == 0;? */
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: CDTEXT is %i byte(s) long\n", cdtext_data_length);
|
|
/* cdc_buffer[2]; cdc_buffer[3]; reserwed */
|
|
dynamic_temp = malloc(cdtext_data_length);
|
|
if(!dynamic_temp)
|
|
return -1;
|
|
|
|
memset(dynamic_temp, 0, cdtext_data_length);
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read CDTEXT\n");
|
|
ret = sendscsi(d, dynamic_temp, cdtext_data_length, 1,
|
|
SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0,
|
|
0, (cdtext_data_length>>8) & 0xFF, cdtext_data_length & 0xFF, 0, 0, 0);
|
|
|
|
if(ret)
|
|
{
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
|
|
"CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret);
|
|
}
|
|
else
|
|
{
|
|
cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */
|
|
wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: read %i byte(s) of CDTEXT\n", cdtext_data_length);
|
|
|
|
/* send cdtext only 18 bytes packs * ? */
|
|
*(p_buffer_length) = cdtext_data_length - 4;
|
|
*pp_buffer = malloc(*p_buffer_length);
|
|
if(!(*pp_buffer))
|
|
{
|
|
return -1;
|
|
}
|
|
memcpy(*pp_buffer, dynamic_temp + 4, *p_buffer_length);
|
|
}
|
|
free(dynamic_temp);
|
|
dynamic_temp = 0;
|
|
}
|
|
|
|
return ret;
|
|
} /* wm_scsi_get_cdtext() */
|