3 * Basic interaction with the CDROM drive.
5 * ----------------------------------------------------------------------
6 * This code is (C) Copyright 1993,1994 by Frank Munkert.
7 * (C) Copyright 2002-2012 The AROS Development Team
9 * This software may be freely distributed and redistributed for
10 * non-commercial purposes, provided this notice is included.
11 * ----------------------------------------------------------------------
14 * 28-Dec-12 neil Pass corrected TOC length back from Read_TOC instead of
15 * the raw TOC header. Errors in the length field of this
16 * header were causing buffer overruns with many drives.
17 * 18-Dec-11 twilen Added media change interrupt support.
18 * 11-Aug-10 sonic Fixed comparison warning in Has_Audio_Tracks()
19 * 01-Mar-10 neil Do not read past end of disc.
20 * 12-Jun-09 neil If drive returns incorrect TOC length, calculate it
21 * based on the number of tracks.
22 * 09-May-09 weissms - Let Do_SCSI_Command result depend on DoIO result.
23 * - Removed redundant checks of io_Error, some code reuse
24 * (Clear_Sector_Buffers).
25 * 20-Mar-09 sonic - Removed usage of AROS-specific include
26 * 08-Mar-09 error - Corrected Test_Unit_Ready returning only NO_DISC state
27 * 06-Mar-09 error - Removed madness, fixed insanity. Cleanup started
28 * 06-Jun-08 sonic - Fixed to compile with gcc v2
29 * 30-Mar-08 error - Updated 'Find_Last_Session' with a generic command
30 * mandatory for all MMC devices; corrected major flaw
31 * with uninitialized variables
32 * 12-Aug-07 sonic - Added some debug output.
33 * 09-Apr-07 sonic - Disabled DirectSCSI on AROS.
34 * 08-Apr-07 sonic - Removed redundant TRACKDISK option.
35 * - Added trackdisk64 support.
36 * - Removed unneeded dealing with block length.
37 * 07-Jul-02 sheutlin various changes when porting to AROS
38 * - global variables are now in a struct Globals *global
39 * 02-Sep-94 fmu Display READ TOC for Apple CD 150 drives.
40 * 01-Sep-94 fmu Workaround for bad NEC 3X READ TOC command in
41 * Has_Audio_Tracks() and Data_Tracks().
42 * 20-Aug-94 fmu New function Find_Last_Session ().
43 * 23-Jul-94 fmu Last index modified from 99 to 1 in Start_Play_Audio().
44 * 18-May-94 fmu New drive model: MODEL_CDU_8002 (= Apple CD 150).
45 * 17-May-94 fmu Sense length 20 instead of 18 (needed by ALF controller).
46 * 17-Feb-94 fmu Added support for Toshiba 4101.
47 * 06-Feb-94 dmb - Fixed bug in Test_Unit_Ready() trackdisk support.
48 * - Fixed bug in Open_CDROM (size of request)
49 * - Added function Clear_Sector_Buffers().
50 * 01-Jan-94 fmu Added function Data_Tracks() for multisession support.
51 * 11-Dec-93 fmu - Memory type can now be chosen by the user.
52 * - Addional parameter p_direction for Do_SCSI_Command().
53 * - Start_Play_Audio() now plays all tracks.
54 * - Mode_Select() instead of Select_XA_Mode().
55 * - Support for CDROM drives with 512, 1024 or 2048 bytes
57 * 06-Dec-93 fmu New drive type DRIVE_SCSI_2.
58 * 09-Nov-93 fmu Added Select_XA_Mode.
59 * 23-Oct-93 fmu Open_CDROM now returns an error code that tell what
61 * 09-Oct-93 fmu SAS/C support added.
62 * 03-Oct-93 fmu New buffering algorithm.
63 * 27-Sep-93 fmu Added support for multi-LUN devices.
64 * 24-Sep-93 fmu - SCSI buffers may now reside in fast or chip memory.
65 * - TD_CHANGESTATE instead of CMD_READ in Test_Unit_Ready
68 #include <proto/alib.h>
69 #include <proto/exec.h>
70 #include <devices/trackdisk.h>
73 #include <devices/newstyle.h>
74 #define TD_READ64 NSCMD_TD_READ64
85 #include "clib_stuff.h"
86 #include <exec/interrupts.h>
88 AROS_INTH1(CDChangeHandler
, struct CDVDBase
*, global
)
92 Signal(&global
->DosProc
->pr_Task
, global
->g_changeint_sigbit
);
99 * i decided to change few things to make this code less insane.
100 * biggest change is - i don't care any more if anyone reads one sector at a time
101 * reading disc 1 sector at a time is totally insane.
102 * presently our schema will read 16 sectors instead, that is, 32768bytes at a time
103 * that should give us SIGNIFICANT speed improvement. unfortunately has some impact
104 * on a cache, too, but that will change over time. currently, cache will eat
105 * STD_BUFFERS * 16 * 2048 bytes
110 struct CDVDBase
*global
,
113 uint32_t p_memory_type
,
120 int err
= CDROMERR_OK
;
127 err
= CDROMERR_NO_MEMORY
;
128 cd
= AllocVec (sizeof (CDROM
), MEMF_PUBLIC
| MEMF_CLEAR
| p_memory_type
);
134 cd
->buffers_cnt
= p_std_buffers
;
137 * change: allocating 16 * SCSI_BUFSIZE * bufs; min access unit 32kB!
139 cd
->buffer_data
= AllocVec (((SCSI_BUFSIZE
* p_std_buffers
) << 4) + 15, MEMF_PUBLIC
| p_memory_type
);
140 if (NULL
== cd
->buffer_data
)
143 cd
->buffer_io
= AllocVec(SCSI_BUFSIZE
, p_memory_type
);
144 if (NULL
== cd
->buffer_io
)
147 cd
->buffers
= AllocVec (sizeof (unsigned char *) * p_std_buffers
, MEMF_PUBLIC
);
148 if (NULL
== cd
->buffers
)
151 cd
->current_sectors
= AllocVec (sizeof (long) * p_std_buffers
, MEMF_PUBLIC
);
152 if (NULL
== cd
->current_sectors
)
155 cd
->last_used
= AllocVec (sizeof (uint32_t) * p_std_buffers
, MEMF_PUBLIC
| MEMF_CLEAR
);
156 if (NULL
== cd
->last_used
)
161 * make the buffer quad-word aligned. This greatly helps
162 * performance on '040-powered systems with DMA SCSI
165 cd
->buffers
[0] = (UBYTE
*)(((IPTR
)cd
->buffer_data
+ 15) & ~15);
166 cd
->current_sectors
[0] = -1;
168 for (i
=1; i
<cd
->buffers_cnt
; i
++)
170 cd
->current_sectors
[i
] = -1;
171 cd
->buffers
[i
] = cd
->buffers
[i
-1] + (SCSI_BUFSIZE
<< 4);
174 err
= CDROMERR_MSGPORT
;
175 cd
->port
= CreateMsgPort ();
176 if (NULL
== cd
->port
)
179 err
= CDROMERR_IOREQ
;
180 cd
->scsireq
= (struct IOStdReq
*)CreateIORequest (cd
->port
, sizeof (struct IOExtTD
));
181 if (NULL
== cd
->scsireq
)
184 err
= CDROMERR_DEVICE
;
185 if (OpenDevice ((UBYTE
*) p_device
, p_scsi_id
, (struct IORequest
*) cd
->scsireq
, 0))
188 cd
->device_open
= TRUE
;
190 if (global
->g_scan_interval
< 0) {
191 /* Add media change interrupts */
192 cd
->iochangeint
= (struct IOStdReq
*)AllocVec(sizeof (struct IOExtTD
), MEMF_PUBLIC
);
193 if (NULL
== cd
->iochangeint
)
195 CopyMem(cd
->scsireq
, cd
->iochangeint
, sizeof (struct IOExtTD
));
196 cd
->changeint
.is_Node
.ln_Type
= NT_INTERRUPT
;
197 cd
->changeint
.is_Node
.ln_Name
= "CDFS ChangeInt";
198 cd
->changeint
.is_Data
= (APTR
)global
;
199 cd
->changeint
.is_Code
= (VOID_FUNC
)CDChangeHandler
;
200 cd
->iochangeint
->io_Length
= sizeof(struct Interrupt
);
201 cd
->iochangeint
->io_Data
= &cd
->changeint
;
202 cd
->iochangeint
->io_Command
= TD_ADDCHANGEINT
;
203 SendIO((struct IORequest
*)cd
->iochangeint
);
206 cd
->scsireq
->io_Command
= CMD_CLEAR
;
207 DoIO ((struct IORequest
*) cd
->scsireq
);
209 cd
->t_changeint
= -1;
210 cd
->t_changeint2
= -2;
212 /* The LUN is the 2nd digit of the SCSI id number: */
213 cd
->lun
= (p_scsi_id
/ 10) % 10;
215 /* 'tick' is incremented every time a sector is accessed. */
220 if (CDROMERR_OK
!= err
)
232 unsigned char *p_buf
,
234 unsigned char *p_command
,
239 p_cd
->scsireq
->io_Length
= sizeof (struct SCSICmd
);
240 p_cd
->scsireq
->io_Data
= (APTR
) &p_cd
->cmd
;
241 p_cd
->scsireq
->io_Command
= HD_SCSICMD
;
243 p_cd
->cmd
.scsi_Data
= (UWORD
*) p_buf
;
244 p_cd
->cmd
.scsi_Length
= p_buf_length
;
245 p_cd
->cmd
.scsi_Flags
= SCSIF_AUTOSENSE
| p_direction
;
246 p_cd
->cmd
.scsi_SenseData
= (UBYTE
*) p_cd
->sense
;
247 p_cd
->cmd
.scsi_SenseLength
= 20;
248 p_cd
->cmd
.scsi_SenseActual
= 0;
249 p_cd
->cmd
.scsi_Command
= (UBYTE
*) p_command
;
250 p_cd
->cmd
.scsi_CmdLength
= p_length
;
252 p_command
[1] |= p_cd
->lun
<< 5;
254 if (0 != DoIO((struct IORequest
*) p_cd
->scsireq
) ||
255 0 != p_cd
->cmd
.scsi_Status
)
257 Clear_Sector_Buffers(p_cd
);
267 unsigned char *p_buf
,
271 int p_number_of_sectors
274 p_cd
->scsireq
->io_Length
= 2048 * p_number_of_sectors
;
275 p_cd
->scsireq
->io_Data
= (APTR
) p_buf
;
276 p_cd
->scsireq
->io_Offset
= (ULONG
) p_sector
<< 11;
277 p_cd
->scsireq
->io_Actual
= (ULONG
) p_sector
>> 21;
278 p_cd
->scsireq
->io_Command
= p_cd
->scsireq
->io_Actual
? TD_READ64
: CMD_READ
;
280 D(bug("[CDVDFS]\tAccessing sectors %ld:%ld\n", (long)p_sector
, (long)p_number_of_sectors
));
282 if (0 != DoIO((struct IORequest
*) p_cd
->scsireq
))
284 D(bug("[CDVDFS]\tTransfer failed: %ld\n", (long)p_cd
->scsireq
->io_Error
));
285 Clear_Sector_Buffers(p_cd
);
289 D(bug("[CDVDFS]\tTransfer successful.\n"));
294 * USAGE NOTE >> VERY IMPORTANT <<
295 * this procedure delivers you buffer that is 'valid' until the next 16-sector-boundary.
296 * if you want to read from sec 14 till 34, then you have to do 3 calls (14-16, 16-32, 32-34)
298 int Read_Chunk(CDROM
*p_cd
, long p_sector
)
300 struct CDVDBase
*global
= p_cd
->global
;
308 D(bug("[CDVDFS]\tClient requested sector %ld\n", p_sector
));
310 for (i
=0; i
<p_cd
->buffers_cnt
; i
++)
312 if ((p_sector
& ~0xf) != p_cd
->current_sectors
[i
])
318 D(bug("[CDVDFS]\tSector already cached\n"));
319 p_cd
->buffer
= p_cd
->buffers
[i
] + ((p_sector
& 0xf) << 11);
322 * try most frequently used
324 p_cd
->last_used
[i
] += 2;
325 for (i
=0; i
<p_cd
->buffers_cnt
; i
++)
327 if (p_cd
->last_used
[i
] > 0)
328 p_cd
->last_used
[i
] -= 1;
335 * find an empty buffer position:
337 for (loc
=0; loc
<p_cd
->buffers_cnt
; loc
++)
338 if (p_cd
->current_sectors
[loc
] == -1)
342 * no free buffer position; remove the buffer that is unused
343 * for the longest time
345 if (loc
==p_cd
->buffers_cnt
)
347 uint32_t oldest_tick
= UINT_MAX
;
350 for (loc
=0, i
=0; i
<p_cd
->buffers_cnt
; i
++)
352 tick
= p_cd
->last_used
[i
];
353 if (tick
< oldest_tick
)
354 loc
= i
, oldest_tick
= tick
;
359 * read **16** sectors
360 * NOTE: all DVD discs require chunk size to be at least n*16 sectors
361 * most of the CDs have enough padding (18 sectors) at the end
364 start
= p_sector
& ~0xf;
366 if (global
->g_volume
!= NULL
)
367 vol_size
= Volume_Size(global
->g_volume
);
370 if (vol_size
!= 0 && vol_size
- start
< count
)
371 count
= vol_size
- start
;
373 Read_From_Drive(p_cd
, p_cd
->buffers
[loc
], SCSI_BUFSIZE
, start
, count
);
377 p_cd
->current_sectors
[loc
] = p_sector
& ~0xf;
378 p_cd
->buffer
= p_cd
->buffers
[loc
] + ((p_sector
& 0xf) << 11);
379 p_cd
->last_used
[loc
] = 1000;
385 int Test_Unit_Ready(CDROM
*p_cd
)
387 p_cd
->scsireq
->io_Command
= TD_CHANGENUM
;
389 if (0 != DoIO ((struct IORequest
*) p_cd
->scsireq
))
392 p_cd
->t_changeint
= p_cd
->scsireq
->io_Actual
;
394 p_cd
->scsireq
->io_Command
= TD_CHANGESTATE
;
395 if ((0 != DoIO ((struct IORequest
*) p_cd
->scsireq
)) ||
396 (0 != p_cd
->scsireq
->io_Actual
))
409 uint8_t cmd
[6] = { };
410 unsigned char mode
[12] = { };
418 mode
[9] = p_block_length
>> 16;
419 mode
[10] = (p_block_length
>> 8) & 0xff;
420 mode
[11] = p_block_length
& 0xff;
422 CopyMem(mode
, p_cd
->buffer_io
, sizeof (mode
));
423 return Do_SCSI_Command(p_cd
, p_cd
->buffer_io
, sizeof(mode
), cmd
, 6, SCSIF_WRITE
);
426 int Inquire (CDROM
*p_cd
, t_inquiry_data
*p_data
)
428 uint8_t cmd
[6] = { };
432 if (!Do_SCSI_Command(p_cd
,p_cd
->buffer_io
,96,cmd
,6,SCSIF_READ
))
435 CopyMem(p_cd
->buffer_io
, p_data
, sizeof (*p_data
));
446 uint8_t cmd
[10] = { };
447 uint32_t toc_len
= 0;
448 uint8_t *buf
= p_cd
->buffer_io
;
456 if (0 == Do_SCSI_Command(p_cd
, buf
, 4, cmd
, 10, SCSIF_READ
))
460 * toc len = len field + field contents
461 * Some drives don't return the full TOC length when the buffer is too
462 * small for the whole TOC: in this case, calculate it based on the
465 toc_len
= 2 + ((buf
[0] << 8) | (buf
[1]));
467 toc_len
= 4 + (buf
[3] - buf
[2] + 2) * 8;
470 * make sure it never happens (shouldn't)
472 if (toc_len
> SCSI_BUFSIZE
)
478 cmd
[7] = toc_len
>> 8;
479 cmd
[8] = toc_len
& 0xff;
480 if (0 == Do_SCSI_Command(p_cd
, buf
, toc_len
, cmd
, 10, SCSIF_READ
))
484 * We pass back the TOC length that the caller would expect to find in
485 * the header, if it could be trusted!
487 *p_toc_len
= toc_len
- 2;
489 return (t_toc_data
*) (buf
+ 4);
492 int Has_Audio_Tracks(CDROM
*p_cd
)
498 toc
= Read_TOC (p_cd
, &toc_len
);
503 * calc num TOC entries
504 * last entry is usually LEADOUT (0xAA)
509 * traverse all tracks, check for audio?
511 for (i
=0; i
<len
; i
++)
513 if ((99 >= toc
[i
].track_number
) &&
514 (0 == (toc
[i
].flags
& 4)))
515 return toc
[i
].track_number
;
521 * Create a buffer containing the start addresses of all data tracks
525 * number of tracks or -1 on error.
528 int Data_Tracks(CDROM
*p_cd
, uint32_t** p_buf
)
538 toc
= Read_TOC(p_cd
, &toc_len
);
543 * calc TOC entries count
548 * count number of data tracks:
550 for (i
=0; i
<len
; i
++)
552 if ((99 >= toc
[i
].track_number
) &&
553 (0 != (toc
[i
].flags
& 4)))
561 * allocate memory for output buffer:
563 *p_buf
= (uint32_t*) AllocVec (cnt
* sizeof (uint32_t*), MEMF_PUBLIC
);
568 * fill output buffer:
570 for (i
=0, j
=0; i
<len
; i
++)
572 if ((99 >= toc
[i
].track_number
) &&
573 (0 != (toc
[i
].flags
& 4)))
574 (*p_buf
)[j
++] = toc
[i
].address
;
580 inline void block2msf (uint32_t blk
, unsigned char *msf
)
582 blk
= (blk
+150) & 0xffffff;
583 msf
[0] = blk
/ 4500; /* 4500 = 60 seconds * 75 frames */
589 int Start_Play_Audio(CDROM
*p_cd
)
591 uint8_t cmd
[10] = { };
592 uint32_t start
= 0xffffffff,end
;
602 toc
= Read_TOC (p_cd
, &toc_len
);
612 * find beginning of audio track
614 for (i
=0; i
<len
; i
++)
616 if ((99 >= toc
[i
].track_number
) &&
617 (0 == (toc
[i
].flags
& 4)))
619 start
=toc
[i
].address
;
627 if (0xffffffff == start
)
631 * find end of audio track
635 if (((99 < toc
[i
].track_number
) && (toc
[i
].track_number
> 99)) ||
636 (0 != (toc
[i
].flags
& 4)))
642 * fill up request and send
644 block2msf(start
, &cmd
[3]);
645 block2msf(end
-1, &cmd
[6]);
647 return Do_SCSI_Command(p_cd
, 0, 0, cmd
, 10, SCSIF_READ
);
650 int Stop_Play_Audio(CDROM
*p_cd
)
652 uint8_t cmd
[6] = { };
654 return Do_SCSI_Command(p_cd
, 0, 0, cmd
, 6, SCSIF_READ
);
657 void Cleanup_CDROM (CDROM
*p_cd
)
659 if (p_cd
->iochangeint
) {
660 p_cd
->iochangeint
->io_Length
= sizeof(struct Interrupt
);
661 p_cd
->iochangeint
->io_Data
= &p_cd
->changeint
;
662 p_cd
->iochangeint
->io_Command
= TD_REMCHANGEINT
;
663 DoIO((struct IORequest
*)p_cd
->iochangeint
);
664 FreeVec(p_cd
->iochangeint
);
666 if (p_cd
->device_open
)
667 CloseDevice ((struct IORequest
*) p_cd
->scsireq
);
669 DeleteIORequest((struct IORequest
*)p_cd
->scsireq
);
671 DeleteMsgPort (p_cd
->port
);
673 FreeVec (p_cd
->last_used
);
674 if (p_cd
->current_sectors
)
675 FreeVec (p_cd
->current_sectors
);
677 FreeVec (p_cd
->buffers
);
679 FreeVec (p_cd
->buffer_io
);
680 if (p_cd
->buffer_data
)
681 FreeVec (p_cd
->buffer_data
);
685 void Clear_Sector_Buffers (CDROM
*p_cd
)
689 for (i
=0; i
<p_cd
->buffers_cnt
; i
++)
690 p_cd
->current_sectors
[i
] = -1;
693 /* Finds offset of last session. (Not supported by all CDROM drives)
695 * Returns: - FALSE if there is no special SCSI command to determine the
696 * offset of the last session.
697 * - TRUE if the offset of the last session has been determined.
700 int Find_Last_Session(CDROM
*p_cd
, uint32_t *p_result
)
702 uint8_t cmd
[10] = { };
703 uint8_t *data
= p_cd
->buffer_io
;
709 * first: READTOC IS MANDATORY
710 * drives not conforming to MMC2 died in 1995.
715 * Ask the scsi device for the length of this TOC
719 if (!Do_SCSI_Command(p_cd
, data
, 12, cmd
, sizeof(cmd
), SCSIF_READ
))
723 * check if we are dealing with a DATA track here.
724 * nobody would like to spend an hour trying to get his mixed mode cd read
726 D(bug("[CDVDFS]\tFirst track in last session has type %lx\n", data
[5]));
727 if ((data
[5] & 0xfc) != 0x14)
729 D(bug("[CDVDFS]\tThis track is not a DATA track. Will default to track 1.\n"));
732 * this indeed sets the address of first track in first session, but we have no idea if this really is a data track.
733 * we don't care anyways. it will be detected by higher layer
739 * the ReadTOC has MSF field set to 0 so we treat obtained values as logical block address
742 *p_result
= (data
[8] << 24) | (data
[9] << 16) | (data
[10] << 8) | (data
[11]);