File indexing completed on 2024-04-21 08:44:25

0001 /*
0002  * This file is part of WorkMan, the civilized CD player library
0003  * Copyright (C) 1991-1997 by Steven Grimm <koreth@midwinter.com>
0004  * Copyright (C) by Dirk Försterling <milliByte@DeathsDoor.com>
0005  * Copyright (C) 2004-2006 Alexander Kern <alex.kern@gmx.de>
0006  *
0007  * This library is free software; you can redistribute it and/or
0008  * modify it under the terms of the GNU Library General Public
0009  * License as published by the Free Software Foundation; either
0010  * version 2 of the License, or (at your option) any later version.
0011  *
0012  * This library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Library General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Library General Public
0018  * License along with this library; if not, write to the Free
0019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0020  *
0021  *
0022  * Frontend functions for sending raw SCSI commands to the CD-ROM drive.
0023  * These depend on wm_scsi(), which should be defined in each platform
0024  * module.
0025  */
0026 
0027 #include <stdio.h>
0028 #include <stdlib.h>
0029 #include <string.h>
0030 #include "include/wm_config.h"
0031 #include "include/wm_struct.h"
0032 #include "include/wm_scsi.h"
0033 #include "include/wm_platform.h"
0034 #include "include/wm_helpers.h"
0035 #include "include/wm_cdrom.h"
0036 #include "include/wm_cdtext.h"
0037 
0038 #define SCMD_INQUIRY        0x12
0039 #define SCMD_MODE_SELECT    0x15
0040 #define SCMD_MODE_SENSE     0x1a
0041 #define SCMD_START_STOP     0x1b
0042 #define SCMD_PREVENT        0x1e
0043 #define SCMD_READ_SUBCHANNEL    0x42
0044 #define SCMD_READ_TOC       0x43
0045 #define SCMD_PLAY_AUDIO_MSF 0x47
0046 #define SCMD_PAUSE_RESUME   0x4b
0047 #define SCMD_SET_CD_SPEED       0xbb
0048 
0049 #define SUBQ_STATUS_INVALID 0x00
0050 #define SUBQ_STATUS_PLAY    0x11
0051 #define SUBQ_STATUS_PAUSE   0x12
0052 #define SUBQ_STATUS_DONE    0x13
0053 #define SUBQ_STATUS_ERROR   0x14
0054 #define SUBQ_STATUS_NONE    0x15
0055 #define SUBQ_STATUS_NO_DISC 0x17    /* Illegal, but Toshiba returns it. */
0056 #define SUBQ_ILLEGAL        0xff
0057 
0058 #define PAGE_AUDIO      0x0e
0059 #define LEADOUT         0xaa
0060 
0061 #define WM_MSG_CLASS WM_MSG_CLASS_SCSI
0062 
0063 /* local prototypes */
0064 int wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len );
0065 int wm_scsi2_pause_resume(struct wm_drive *d, int resume);
0066 int wm_scsi2_prevent(struct wm_drive *d, int prevent);
0067 int wm_scsi2_play(struct wm_drive *d, int sframe, int eframe);
0068 int wm_scsi2_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe);
0069 int wm_scsi2_get_cdlen(struct wm_drive *d, int frames);
0070 int wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode,
0071   int *mode, int *pos, int *track, int *ind);
0072 int wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks);
0073 int wm_scsi2_pause(struct wm_drive *d);
0074 int wm_scsi2_resume(struct wm_drive *d);
0075 int wm_scsi2_stop(struct wm_drive *d);
0076 int wm_scsi2_eject(struct wm_drive *d);
0077 int wm_scsi2_closetray(struct wm_drive *d);
0078 int wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right);
0079 int wm_scsi2_set_volume(struct wm_drive *d, int left, int right);
0080 /* local prototypes END */
0081 
0082 /*
0083  * Send a SCSI command over the bus, with all the CDB bytes specified
0084  * as unsigned char parameters.  This doesn't use varargs because some
0085  * systems have stdargs instead and the number of bytes in a CDB is
0086  * limited to 12 anyway.
0087  *
0088  * d    Drive structure
0089  * buf  Buffer for data, both sending and receiving
0090  * len  Size of buffer
0091  * dir  TRUE if the command expects data to be returned in the buffer.
0092  * a0-  CDB bytes.  Either 6, 10, or 12 of them, depending on the command.
0093  */
0094 /*VARARGS4*/
0095 int
0096 sendscsi( struct wm_drive *d, void *buf,
0097     unsigned int len, int dir,
0098     unsigned char a0, unsigned char a1,
0099     unsigned char a2, unsigned char a3,
0100     unsigned char a4, unsigned char a5,
0101     unsigned char a6, unsigned char a7,
0102     unsigned char a8, unsigned char a9,
0103     unsigned char a10, unsigned char a11 )
0104 {
0105     int cdblen = 0;
0106     unsigned char cdb[12];
0107 
0108     cdb[0] = a0;
0109     cdb[1] = a1;
0110     cdb[2] = a2;
0111     cdb[3] = a3;
0112     cdb[4] = a4;
0113     cdb[5] = a5;
0114 
0115     switch ((a0 >> 5) & 7) {
0116     case 0:
0117         cdblen = 6;
0118         break;
0119 
0120     case 5:
0121         cdb[10] = a10;
0122         cdb[11] = a11;
0123         cdblen = 12;
0124 
0125     case 1:
0126     case 2:
0127     case 6: /* assume 10-byte vendor-specific codes for now */
0128         cdb[6] = a6;
0129         cdb[7] = a7;
0130         cdb[8] = a8;
0131         cdb[9] = a9;
0132         if (! cdblen)
0133             cdblen = 10;
0134         break;
0135     }
0136 
0137     if(d->proto.scsi)
0138         return d->proto.scsi(d, cdb, cdblen, buf, len, dir);
0139     return -1;
0140 }
0141 
0142 /*
0143  * Send a MODE SENSE command and return the results (minus header cruft)
0144  * in a user buffer.
0145  *
0146  * d    Drive structure
0147  * page Number of page to query (plus page control bits, if any)
0148  * buf  Result buffer
0149  */
0150 int
0151 wm_scsi_mode_sense( struct wm_drive *d, unsigned char page, unsigned char *buf )
0152 {
0153     unsigned char pagebuf[255];
0154     int status, i, len, offset;
0155 
0156     status = sendscsi(d, pagebuf, sizeof(pagebuf), 1, SCMD_MODE_SENSE, 0,
0157             page, 0, sizeof(pagebuf), 0,0,0,0,0,0,0);
0158     if (status < 0)
0159         return (status);
0160 
0161     /*
0162      * The first byte of the returned data is the transfer length.  Then
0163      * two more bytes and the length of whatever header blocks are in
0164      * front of the page we want.
0165      */
0166     len = pagebuf[0] - pagebuf[3] - 3;
0167     offset = pagebuf[3] + 4;
0168     for (i = 0; i < len; i++)
0169         buf[i] = pagebuf[offset + i];
0170 
0171     return (0);
0172 }
0173 
0174 /*
0175  * Send a MODE SELECT command.
0176  *
0177  * d    Drive structure
0178  * buf  Page buffer (no need to put on block descriptors)
0179  * len  Size of page
0180  */
0181 int
0182 wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len )
0183 {
0184     unsigned char pagebuf[255];
0185     int i;
0186 
0187     pagebuf[0] = pagebuf[1] = pagebuf[2] = pagebuf[3] = 0;
0188     for (i = 0; i < (int) len; i++)
0189         pagebuf[i + 4] = buf[i];
0190 
0191     return sendscsi(d, pagebuf, len + 4, 0, SCMD_MODE_SELECT, 0x10, 0,
0192         0, len + 4, 0,0,0,0,0,0,0);
0193 }
0194 
0195 /*
0196  * Send an INQUIRY command to get the drive type.
0197  *
0198  * d        Drive structure
0199  * vendor   Buffer for vendor name (8 bytes + null)
0200  * model    Buffer for model name (16 bytes + null)
0201  * rev      Buffer for revision level (4 bytes + null)
0202  *
0203  * The above string lengths apply to the SCSI INQUIRY command. The
0204  * actual WorkMan drive structure reserves 31 bytes + NULL per entry.
0205  *
0206  * If the model name begins with "CD-ROM" and zero or more spaces, that will
0207  * all be stripped off since it's just extra junk to WorkMan.
0208  */
0209 int
0210 wm_scsi_get_drive_type(struct wm_drive *d)
0211 {
0212 /* removed   unsigned*/
0213     char *s, *t, buf[36];
0214 
0215     memset(buf, 0, 36);
0216 
0217     wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "Sending SCSI inquiry command...\n");
0218     if(sendscsi(d, buf, 36, 1, SCMD_INQUIRY, 0, 0, 0, 36, 0,0,0,0,0,0,0)) {
0219         sprintf(d->vendor, WM_STR_GENVENDOR);
0220         sprintf(d->model, WM_STR_GENMODEL);
0221         sprintf(d->revision, WM_STR_GENREV);
0222         wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_ERROR,
0223             "SCSI Inquiry command not supported in this context\n");
0224         return -1;
0225     }
0226 
0227     wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_DEBUG, "sent.\n");
0228 
0229     memcpy(d->vendor, buf + 8, 8);
0230     d->vendor[8] = '\0';
0231     memcpy(d->model, buf + 16, 16);
0232     d->model[16] = '\0';
0233     memcpy(d->revision, buf + 32, 4);
0234     d->revision[4] = '\0';
0235     wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "SCSI Inquiry result: [%s|%s|%s]\n",
0236         d->vendor, d->model, d->revision);
0237 
0238     /* Remove "CD-ROM " from the model. */
0239     if (! strncmp(d->model, "CD-ROM", 6)) {
0240         s = d->model + 6;
0241         t = d->model;
0242         while (*s == ' ' || *s == '\t')
0243             s++;
0244         while( (*t++ = *s++) )
0245             ;
0246     }
0247 
0248     wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "scsi: Cooked data: %s %s rev. %s\n",
0249         d->vendor, d->model,d->revision);
0250 
0251     return 0;
0252 } /* wm_scsi_get_drive_type() */
0253 
0254 /*
0255  * Send a SCSI-2 PAUSE/RESUME command.  "resume" is 1 to resume, 0 to pause.
0256  */
0257 int
0258 wm_scsi2_pause_resume(struct wm_drive *d, int resume)
0259 {
0260     return sendscsi(d, NULL, 0, 0, SCMD_PAUSE_RESUME, 0, 0, 0, 0, 0, 0,
0261         0, resume ? 1 : 0, 0,0,0);
0262 }
0263 
0264 /*
0265  * Send a SCSI-2 "prevent media removal" command.  "prevent" is 1 to lock
0266  * caddy in.
0267  */
0268 int
0269 wm_scsi2_prevent(struct wm_drive *d, int prevent)
0270 {
0271     return sendscsi(d, NULL, 0, 0, SCMD_PREVENT, 0, 0, 0, 0, 0, 0,
0272         0, prevent ? 1 : 0, 0,0,0);
0273 }
0274 
0275 /*
0276  * Send a SCSI-2 PLAY AUDIO MSF command.  Pass the starting and ending
0277  * frame numbers.
0278  */
0279 int
0280 wm_scsi2_play(struct wm_drive *d, int sframe, int eframe)
0281 {
0282     return sendscsi(d, NULL, 0, 0, SCMD_PLAY_AUDIO_MSF, 0, 0,
0283         sframe / (60 * 75), (sframe / 75) % 60, sframe % 75,
0284         eframe / (60 * 75), (eframe / 75) % 60, eframe % 75,
0285         0,0,0);
0286 }
0287 
0288 /*
0289  * Send a SCSI-2 READ TOC command to get the data for a particular track.
0290  * Fill in track information from the returned data.
0291  */
0292 int
0293 wm_scsi2_get_trackinfo(struct wm_drive *d, int track,
0294     int *data, int *startframe)
0295 {
0296     unsigned char buf[12]; /* one track's worth of info */
0297 
0298     if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 2,
0299         0, 0, 0, 0, track, sizeof(buf) / 256,
0300         sizeof(buf) % 256, 0,0,0))
0301         return (-1);
0302 
0303     *data = buf[5] & 4 ? 1 : 0;
0304     *startframe = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
0305 
0306     return 0;
0307 }
0308 
0309 /*
0310  * Get the starting frame for the leadout area (which should be the same as
0311  * the length of the disc as far as WorkMan is concerned).
0312  */
0313 int
0314 wm_scsi2_get_cdlen(struct wm_drive *d, int frames)
0315 {
0316     int tmp;
0317     return wm_scsi2_get_trackinfo(d, LEADOUT, &tmp, &frames);
0318 }
0319 
0320 /*
0321  * Get the current status of the drive by sending the appropriate SCSI-2
0322  * READ SUB-CHANNEL command.
0323  */
0324 int
0325 wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode,
0326     int *mode, int *pos, int *track, int *ind)
0327 {
0328     unsigned char buf[48];
0329 
0330     /* If we can't get status, the CD is ejected, so default to that. */
0331     *mode = WM_CDM_EJECTED;
0332 
0333     /* If we can't read status, the CD has been ejected. */
0334     buf[1] = SUBQ_ILLEGAL;
0335     if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_SUBCHANNEL, 2, 64, 1,
0336         0, 0, 0, sizeof(buf) / 256, sizeof(buf) % 256, 0,0,0))
0337         return 0;
0338 
0339         switch (buf[1]) {
0340         case SUBQ_STATUS_PLAY:
0341         *mode = WM_CDM_PLAYING;
0342         *track = buf[6];
0343         *ind = buf[7];
0344         *pos = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
0345         break;
0346 
0347     case SUBQ_STATUS_PAUSE:
0348         if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) {
0349             *mode = WM_CDM_PAUSED;
0350             *track = buf[6];
0351             *ind = buf[7];
0352             *pos = buf[9] * 60 * 75 +
0353                 buf[10] * 75 +
0354                 buf[11];
0355         } else
0356             *mode = WM_CDM_STOPPED;
0357         break;
0358 
0359     /*
0360      * SUBQ_STATUS_DONE is sometimes returned when the CD is idle,
0361      * even though the spec says it should only be returned when an
0362      * audio play operation finishes.
0363      */
0364     case SUBQ_STATUS_DONE:
0365     case SUBQ_STATUS_NONE:
0366     case SUBQ_STATUS_INVALID:
0367         if (oldmode == WM_CDM_PLAYING)
0368             *mode = WM_CDM_TRACK_DONE;
0369         else
0370             *mode = WM_CDM_STOPPED;
0371         break;
0372 
0373     /*
0374      * This usually means there's no disc in the drive.
0375      */
0376     case SUBQ_STATUS_NO_DISC:
0377         break;
0378 
0379     /*
0380      * This usually means the user ejected the CD manually.
0381      */
0382     case SUBQ_STATUS_ERROR:
0383         break;
0384 
0385     case SUBQ_ILLEGAL: /* call didn't really succeed */
0386         break;
0387 
0388         default:
0389         *mode = WM_CDM_UNKNOWN;
0390         break;
0391         }
0392 
0393     return 0;
0394 }
0395 
0396 /*
0397  * Get the number of tracks on the CD using the SCSI-2 READ TOC command.
0398  */
0399 int
0400 wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks)
0401 {
0402     unsigned char buf[4];
0403 
0404     if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 0,
0405         0, 0, 0, 0, 0, sizeof(buf) / 256,
0406         sizeof(buf) % 256, 0,0,0))
0407         return -1;
0408 
0409     *tracks = buf[3] - buf[2] + 1;
0410     return 0;
0411 }
0412 
0413 /*
0414  * Pause the CD.
0415  */
0416 int
0417 wm_scsi2_pause(struct wm_drive *d)
0418 {
0419     return wm_scsi2_pause_resume(d, 0);
0420 }
0421 
0422 /*
0423  * Resume playing after a pause.
0424  */
0425 int
0426 wm_scsi2_resume(struct wm_drive *d)
0427 {
0428     return wm_scsi2_pause_resume(d, 1);
0429 }
0430 
0431 /*
0432  * Stop playing the CD by sending a START STOP UNIT command.
0433  */
0434 int
0435 wm_scsi2_stop(struct wm_drive *d)
0436 {
0437     return sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 0, 0,0,0,0,0,0,0,0,0,0);
0438 }
0439 
0440 /*
0441  * Eject the CD by sending a START STOP UNIT command.
0442  */
0443 int
0444 wm_scsi2_eject(struct wm_drive *d)
0445 {
0446     /* Unlock the disc (possibly unnecessary). */
0447     if (wm_scsi2_prevent(d, 0))
0448         return -1;
0449     wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for ejecting...\n");
0450     return sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0);
0451 }
0452 
0453 /*
0454  * Something like a dummy. The SCSI-2 specs are too hard for me to
0455  * understand here...
0456  *
0457  * If you have the correct command handy, please send the code to
0458  * milliByte@DeathsDoor.com
0459  */
0460 int
0461 wm_scsi2_closetray(struct wm_drive *d)
0462 {
0463     wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for closing...\n");
0464     return sendscsi(d, NULL, 0,0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0);
0465 }
0466 
0467 /*
0468  * Get the volume by doing a MODE SENSE command.
0469  */
0470 int
0471 wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right)
0472 {
0473     unsigned char mode[16];
0474 
0475     *left = *right = -1;
0476 
0477     /* Get the current audio parameters first. */
0478     if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
0479         return (-1);
0480 
0481     *left = ((int) mode[9] * 100) / 255;
0482     *right = ((int) mode[11] * 100) / 255;
0483 
0484     return 0;
0485 }
0486 
0487 /*
0488  * Set the volume by doing a MODE SELECT command.
0489  */
0490 int
0491 wm_scsi2_set_volume(struct wm_drive *d, int left, int right)
0492 {
0493     unsigned char mode[16];
0494 
0495     /* Get the current audio parameters first. */
0496     if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
0497         return -1;
0498 
0499     /* Tweak the volume part of the parameters. */
0500     mode[9] = (left * 255) / 100;
0501     mode[11] = (right * 255) / 100;
0502 
0503     /* And send them back to the drive. */
0504     return wm_scsi_mode_select(d, mode, sizeof(mode));
0505 }
0506 
0507 /*------------------------------------------------------------------------*
0508  * wm_scsi_get_cdtext(drive, buffer, length)
0509  *
0510  * Return a buffer with cdtext-stream. buffer mus be allocated and filled
0511  *
0512  *
0513  *------------------------------------------------------------------------*/
0514 
0515 int
0516 wm_scsi_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_length)
0517 {
0518     int ret;
0519     unsigned char temp[8];
0520     unsigned char *dynamic_temp;
0521     int cdtext_possible;
0522     unsigned short cdtext_data_length;
0523     unsigned long feature_list_length;
0524 #define IGNORE_FEATURE_LIST
0525 #ifndef IGNORE_FEATURE_LIST
0526     struct feature_list_header *pHeader;
0527     struct feature_descriptor_cdread *pDescriptor;
0528 #endif /* IGNORE_FEATURE_LIST */
0529 
0530     dynamic_temp = NULL;
0531     cdtext_possible = 0;
0532     wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi_get_cdtext entered\n");
0533 
0534     wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: use GET_FEATURY_LIST(0x46)...\n");
0535     ret = sendscsi(d, temp, 8, 1, 0x46, 0x02, 0x00, 0x1E, 0, 0, 0, 0, 8, 0, 0, 0);
0536 
0537     if(ret) {
0538         wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
0539             "CDTEXT ERROR: GET_FEATURY_LIST(0x46) not implemented or broken. ret = %i!\n", ret);
0540 #ifndef IGNORE_FEATURE_LIST
0541         wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
0542             "CDTEXT ERROR: Try #define IGNORE_FEATURE_LIST in libwm/scsi.c\n");
0543 #else
0544         cdtext_possible = 1;
0545         wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
0546             "CDTEXT INFO: GET_FEATURY_LIST(0x46) ignored. It is OK, because many CDROMS do not implement this feature\n");
0547 #endif /* IGNORE_FEATURE_LIST */
0548     } else {
0549         feature_list_length = temp[0]*0xFFFFFF + temp[1]*0xFFFF + temp[2]*0xFF + temp[3] + 4;
0550 
0551         dynamic_temp = malloc(feature_list_length);
0552 
0553         if(!dynamic_temp)
0554             return -1;
0555 
0556         memset(dynamic_temp, 0, feature_list_length);
0557         ret = sendscsi(d, dynamic_temp, feature_list_length, 1,
0558             0x46, 0x02, 0x00, 0x1E, 0, 0, 0, (feature_list_length>>8) & 0xFF,
0559             feature_list_length & 0xFF, 0, 0, 0);
0560 
0561 #ifndef IGNORE_FEATURE_LIST
0562         if(!ret) {
0563             pHeader = (struct feature_list_header*)dynamic_temp;
0564 /*     printf("length = %i, profile = 0x%02X%02X\n", pHeader->lenght_lsb, pHeader->profile_msb, pHeader->profile_lsb);*/
0565             pDescriptor = (struct feature_descriptor_cdread*)
0566                 (dynamic_temp + sizeof(struct feature_list_header));
0567 /*     printf("code = 0x%02X%02X, settings = 0x%02X, add_length = %i, add_settings = 0x%02X \n",
0568          pDescriptor->feature_code_msb, pDescriptor->feature_code_lsb, pDescriptor->settings,
0569          pDescriptor->add_lenght, pDescriptor->add_settings);*/
0570 
0571             cdtext_possible = pDescriptor->add_settings & 0x01;
0572         } else {
0573             cdtext_possible = 0;
0574         }
0575 #else
0576         cdtext_possible = 1;
0577 #endif /* IGNORE_FEATURE_LIST */
0578 
0579         free (dynamic_temp);
0580         dynamic_temp = 0;
0581     }
0582 
0583     if(!cdtext_possible) {
0584         wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
0585             "CDTEXT INFO: GET_FEATURY_LIST(0x46) says, CDTEXT is not present!\n");
0586         return EXIT_SUCCESS;
0587     }
0588 
0589     wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read, how long CDTEXT is?\n");
0590     ret = sendscsi(d, temp, 4, 1, SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0, 0, 0, 4, 0, 0, 0);
0591 
0592     if(ret) {
0593         wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
0594             "CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret);
0595     } else {
0596         cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */
0597     /* cdtext_data_length%18 == 0;? */
0598         wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
0599             "CDTEXT INFO: CDTEXT is %i byte(s) long\n", cdtext_data_length);
0600     /* cdc_buffer[2];  cdc_buffer[3]; reserwed */
0601         dynamic_temp = malloc(cdtext_data_length);
0602         if(!dynamic_temp)
0603             return -1;
0604 
0605         memset(dynamic_temp, 0, cdtext_data_length);
0606         wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read CDTEXT\n");
0607         ret = sendscsi(d, dynamic_temp, cdtext_data_length, 1,
0608             SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0, 0, (cdtext_data_length>>8) & 0xFF,
0609             cdtext_data_length & 0xFF, 0, 0, 0);
0610 
0611         if(ret) {
0612             wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
0613                 "CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret);
0614         } else {
0615             cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */
0616             wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
0617                 "CDTEXT INFO: read %i byte(s) of CDTEXT\n", cdtext_data_length);
0618 
0619             /* send cdtext only 18 bytes packs * ? */
0620             *(p_buffer_length) = cdtext_data_length - 4;
0621             *pp_buffer = malloc(*p_buffer_length);
0622             if(!(*pp_buffer)) {
0623                 return -1;
0624             }
0625             memcpy(*pp_buffer, dynamic_temp + 4, *p_buffer_length);
0626         }
0627         free(dynamic_temp);
0628         dynamic_temp = 0;
0629     }
0630 
0631     return ret;
0632 } /* wm_scsi_get_cdtext() */
0633 
0634 int
0635 wm_scsi_set_speed(struct wm_drive *d, int read_speed)
0636 {
0637     int ret;
0638 
0639     ret = sendscsi(d, NULL, 0, 0,
0640         SCMD_SET_CD_SPEED, 0x00, (read_speed>>8) & 0xFF, read_speed & 0xFF, 0xFF, 0xFF,
0641         0, 0, 0, 0, 0, 0);
0642     wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS,
0643         "wm_scsi_set_speed returns %i\n", ret);
0644     return ret;
0645 } /* wm_scsi_set_speed() */