2 * $Id: scsi.c,v 1.8 1999/06/17 06:48:03 dirk Exp $
4 * This file is part of WorkMan, the civilized CD player library
5 * (c) 1991-1997 by Steven Grimm (original author)
6 * (c) by Dirk Försterling (current 'author' = maintainer)
7 * The maintainer can be contacted by his e-mail address:
8 * milliByte@DeathsDoor.com
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the Free
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Frontend functions for sending raw SCSI commands to the CD-ROM drive.
26 * These depend on wm_scsi(), which should be defined in each platform
30 static char scsi_id
[] = "$Id: scsi.c,v 1.8 1999/06/17 06:48:03 dirk Exp $";
35 #include "include/wm_config.h"
36 #include "include/wm_struct.h"
37 #include "include/wm_scsi.h"
38 #include "include/wm_platform.h"
39 #include "include/wm_helpers.h"
40 #include "include/wm_cdrom.h"
42 #define SCMD_INQUIRY 0x12
43 #define SCMD_MODE_SELECT 0x15
44 #define SCMD_MODE_SENSE 0x1a
45 #define SCMD_START_STOP 0x1b
46 #define SCMD_PREVENT 0x1e
47 #define SCMD_READ_SUBCHANNEL 0x42
48 #define SCMD_READ_TOC 0x43
49 #define SCMD_PLAY_AUDIO_MSF 0x47
50 #define SCMD_PAUSE_RESUME 0x4b
52 #define SUBQ_STATUS_INVALID 0x00
53 #define SUBQ_STATUS_PLAY 0x11
54 #define SUBQ_STATUS_PAUSE 0x12
55 #define SUBQ_STATUS_DONE 0x13
56 #define SUBQ_STATUS_ERROR 0x14
57 #define SUBQ_STATUS_NONE 0x15
58 #define SUBQ_STATUS_NO_DISC 0x17 /* Illegal, but Toshiba returns it. */
59 #define SUBQ_ILLEGAL 0xff
61 #define PAGE_AUDIO 0x0e
64 #define WM_MSG_CLASS WM_MSG_CLASS_SCSI
67 * Send a SCSI command over the bus, with all the CDB bytes specified
68 * as unsigned char parameters. This doesn't use varargs because some
69 * systems have stdargs instead and the number of bytes in a CDB is
70 * limited to 12 anyway.
73 * buf Buffer for data, both sending and receiving
75 * dir TRUE if the command expects data to be returned in the buffer.
76 * a0- CDB bytes. Either 6, 10, or 12 of them, depending on the command.
80 sendscsi( struct wm_drive
*d
, void *buf
,
81 unsigned int len
, int dir
,
82 unsigned char a0
, unsigned char a1
,
83 unsigned char a2
, unsigned char a3
,
84 unsigned char a4
, unsigned char a5
,
85 unsigned char a6
, unsigned char a7
,
86 unsigned char a8
, unsigned char a9
,
87 unsigned char a10
, unsigned char a11
)
91 unsigned char cdb
[12];
100 switch ((a0
>> 5) & 7) {
112 case 6: /* assume 10-byte vendor-specific codes for now */
122 return (wm_scsi(d
, cdb
, cdblen
, buf
, len
, dir
));
126 * Send a MODE SENSE command and return the results (minus header cruft)
130 * page Number of page to query (plus page control bits, if any)
134 wm_scsi_mode_sense( struct wm_drive
*d
, unsigned char page
, unsigned char *buf
)
136 unsigned char pagebuf
[255];
137 int status
, i
, len
, offset
;
139 status
= sendscsi(d
, pagebuf
, sizeof(pagebuf
), 1, SCMD_MODE_SENSE
, 0,
140 page
, 0, sizeof(pagebuf
), 0,0,0,0,0,0,0);
145 * The first byte of the returned data is the transfer length. Then
146 * two more bytes and the length of whatever header blocks are in
147 * front of the page we want.
149 len
= pagebuf
[0] - pagebuf
[3] - 3;
150 offset
= pagebuf
[3] + 4;
151 for (i
= 0; i
< len
; i
++)
152 buf
[i
] = pagebuf
[offset
+ i
];
158 * Send a MODE SELECT command.
161 * buf Page buffer (no need to put on block descriptors)
165 wm_scsi_mode_select(d
, buf
, len
)
170 unsigned char pagebuf
[255];
173 pagebuf
[0] = pagebuf
[1] = pagebuf
[2] = pagebuf
[3] = 0;
174 for (i
= 0; i
< (int) len
; i
++)
175 pagebuf
[i
+ 4] = buf
[i
];
177 return (sendscsi(d
, pagebuf
, len
+ 4, 0, SCMD_MODE_SELECT
, 0x10, 0,
178 0, len
+ 4, 0,0,0,0,0,0,0));
182 * Send an INQUIRY command to get the drive type.
185 * vendor Buffer for vendor name (8 bytes + null)
186 * model Buffer for model name (16 bytes + null)
187 * rev Buffer for revision level (4 bytes + null)
189 * The above string lengths apply to the SCSI INQUIRY command. The
190 * actual WorkMan drive structure reserves 31 bytes + NULL per entry.
192 * If the model name begins with "CD-ROM" and zero or more spaces, that will
193 * all be stripped off since it's just extra junk to WorkMan.
196 wm_scsi_get_drive_type( struct wm_drive
*d
, char *vendor
,
197 char *model
, char *rev
)
199 /* removed unsigned*/
200 char *s
, *t
, buf
[36];
202 wm_lib_message(WM_MSG_LEVEL_INFO
|WM_MSG_CLASS
, "Sending SCSI inquiry command...");
203 if (sendscsi(d
, buf
, sizeof(buf
), 1, SCMD_INQUIRY
, 0, 0, 0, sizeof(buf
), 0,0,0,0,0,0,0))
205 sprintf( vendor
, WM_STR_GENVENDOR
);
206 sprintf( model
, WM_STR_GENMODEL
);
207 sprintf( rev
, WM_STR_GENREV
);
208 wm_lib_message(WM_MSG_LEVEL_ERROR
|WM_MSG_CLASS
, "\nSCSI inquiry command not supported by the hardware\n");
209 return (WM_ERR_SCSI_INQUIRY_FAILED
);
211 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "sent.\n");
213 memcpy(vendor
, buf
+ 8, 8);
215 memcpy(model
, buf
+ 16, 16);
217 memcpy(rev
, buf
+ 32, 4);
219 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "SCSI Inquiry result: [%s|%s|%s]\n", vendor
, model
, rev
);
222 /* Remove "CD-ROM " from the model. */
223 if (! strncmp(model
, "CD-ROM", 6))
227 while (*s
== ' ' || *s
== '\t')
229 while( (*t
++ = *s
++) )
232 wm_lib_message(WM_MSG_LEVEL_INFO
|WM_MSG_CLASS
, "Cooked data: %s %s rev. %s\n", vendor
, model
,rev
);
234 } /* wm_scsi_get_drive_type() */
237 * Send a SCSI-2 PAUSE/RESUME command. "resume" is 1 to resume, 0 to pause.
240 wm_scsi2_pause_resume(d
, resume
)
244 return (sendscsi(d
, NULL
, 0, 0, SCMD_PAUSE_RESUME
, 0, 0, 0, 0, 0, 0,
245 0, resume
? 1 : 0, 0,0,0));
249 * Send a SCSI-2 "prevent media removal" command. "prevent" is 1 to lock
253 wm_scsi2_prevent(d
, prevent
)
257 return (sendscsi(d
, NULL
, 0, 0, SCMD_PREVENT
, 0, 0, 0, 0, 0, 0,
258 0, prevent
? 1 : 0, 0,0,0));
262 * Send a SCSI-2 PLAY AUDIO MSF command. Pass the starting and ending
266 wm_scsi2_play(d
, sframe
, eframe
)
270 return (sendscsi(d
, NULL
, 0, 0, SCMD_PLAY_AUDIO_MSF
, 0, 0,
271 sframe
/ (60 * 75), (sframe
/ 75) % 60, sframe
% 75,
272 eframe
/ (60 * 75), (eframe
/ 75) % 60, eframe
% 75,
277 * Send a SCSI-2 READ TOC command to get the data for a particular track.
278 * Fill in track information from the returned data.
281 wm_scsi2_get_trackinfo(d
, track
, data
, startframe
)
283 int track
, *data
, *startframe
;
285 unsigned char buf
[12]; /* one track's worth of info */
287 if (sendscsi(d
, buf
, sizeof(buf
), 1, SCMD_READ_TOC
, 2,
288 0, 0, 0, 0, track
, sizeof(buf
) / 256,
289 sizeof(buf
) % 256, 0,0,0))
292 *data
= buf
[5] & 4 ? 1 : 0;
293 *startframe
= buf
[9] * 60 * 75 + buf
[10] * 75 + buf
[11];
299 * Get the starting frame for the leadout area (which should be the same as
300 * the length of the disc as far as WorkMan is concerned).
303 wm_scsi2_get_cdlen(d
, frames
)
309 return (wm_scsi2_get_trackinfo(d
, LEADOUT
, &tmp
, frames
));
313 * Get the current status of the drive by sending the appropriate SCSI-2
314 * READ SUB-CHANNEL command.
317 wm_scsi2_get_drive_status(d
, oldmode
, mode
, pos
, track
, index
)
319 enum wm_cd_modes oldmode
, *mode
;
320 int *pos
, *track
, *index
;
322 unsigned char buf
[48];
324 /* If we can't get status, the CD is ejected, so default to that. */
325 *mode
= WM_CDM_EJECTED
;
327 /* Is the device open? */
331 * stupid somehow, but necessary this time
333 switch( wmcd_open( d
) ) {
343 /* If we can't read status, the CD has been ejected. */
344 buf
[1] = SUBQ_ILLEGAL
;
345 if (sendscsi(d
, buf
, sizeof(buf
), 1, SCMD_READ_SUBCHANNEL
, 2, 64, 1,
346 0, 0, 0, sizeof(buf
) / 256, sizeof(buf
) % 256, 0,0,0))
350 case SUBQ_STATUS_PLAY
:
351 *mode
= WM_CDM_PLAYING
;
354 *pos
= buf
[9] * 60 * 75 + buf
[10] * 75 + buf
[11];
357 case SUBQ_STATUS_PAUSE
:
358 if (oldmode
== WM_CDM_PLAYING
|| oldmode
== WM_CDM_PAUSED
)
360 *mode
= WM_CDM_PAUSED
;
363 *pos
= buf
[9] * 60 * 75 +
368 *mode
= WM_CDM_STOPPED
;
372 * SUBQ_STATUS_DONE is sometimes returned when the CD is idle,
373 * even though the spec says it should only be returned when an
374 * audio play operation finishes.
376 case SUBQ_STATUS_DONE
:
377 case SUBQ_STATUS_NONE
:
378 case SUBQ_STATUS_INVALID
:
379 if (oldmode
== WM_CDM_PLAYING
)
380 *mode
= WM_CDM_TRACK_DONE
;
382 *mode
= WM_CDM_STOPPED
;
386 * This usually means there's no disc in the drive.
388 case SUBQ_STATUS_NO_DISC
:
392 * This usually means the user ejected the CD manually.
394 case SUBQ_STATUS_ERROR
:
397 case SUBQ_ILLEGAL
: /* call didn't really succeed */
401 *mode
= WM_CDM_UNKNOWN
;
403 if( getenv( "WORKMAN_DEBUG" ) != NULL
)
404 printf("wm_scsi2_get_drive_status: status is 0x%x\n",
414 * Get the number of tracks on the CD using the SCSI-2 READ TOC command.
417 wm_scsi2_get_trackcount(d
, tracks
)
421 unsigned char buf
[4];
423 if (sendscsi(d
, buf
, sizeof(buf
), 1, SCMD_READ_TOC
, 0,
424 0, 0, 0, 0, 0, sizeof(buf
) / 256,
425 sizeof(buf
) % 256, 0,0,0))
428 *tracks
= buf
[3] - buf
[2] + 1;
439 return (wm_scsi2_pause_resume(d
, 0));
443 * Resume playing after a pause.
449 return (wm_scsi2_pause_resume(d
, 1));
453 * Stop playing the CD by sending a START STOP UNIT command.
459 return (sendscsi(d
, NULL
, 0, 0, SCMD_START_STOP
, 0, 0,0,0,0,0,0,0,0,0,0));
463 * Eject the CD by sending a START STOP UNIT command.
466 wm_scsi2_eject(struct wm_drive
*d
)
468 /* Unlock the disc (possibly unnecessary). */
469 if (wm_scsi2_prevent(d
, 0))
471 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "Issuing START_STOP for ejecting...\n");
472 return (sendscsi(d
, NULL
, 0, 0, SCMD_START_STOP
, 2, 0,0,0,0,0,0,0,0,0,0));
476 * Something like a dummy. The SCSI-2 specs are too hard for me to
479 * If you have the correct command handy, please send the code to
480 * milliByte@DeathsDoor.com
483 wm_scsi2_closetray(struct wm_drive
*d
)
485 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "Issuing START_STOP for closing...\n");
486 return (sendscsi(d
, NULL
, 0,0, SCMD_START_STOP
, 2, 0,0,0,0,0,0,0,0,0,0));
490 * Get the volume by doing a MODE SENSE command.
493 wm_scsi2_get_volume(d
, left
, right
)
497 unsigned char mode
[16];
501 /* Get the current audio parameters first. */
502 if (wm_scsi_mode_sense(d
, PAGE_AUDIO
, mode
))
505 *left
= ((int) mode
[9] * 100) / 255;
506 *right
= ((int) mode
[11] * 100) / 255;
512 * Set the volume by doing a MODE SELECT command.
515 wm_scsi2_set_volume(d
, left
, right
)
519 unsigned char mode
[16];
521 /* Get the current audio parameters first. */
522 if (wm_scsi_mode_sense(d
, PAGE_AUDIO
, mode
))
525 /* Tweak the volume part of the parameters. */
526 mode
[9] = (left
* 255) / 100;
527 mode
[11] = (right
* 255) / 100;
529 /* And send them back to the drive. */
530 return (wm_scsi_mode_select(d
, mode
, sizeof(mode
)));