revert between 56095 -> 55830 in arch
[AROS.git] / rom / filesys / CDVDFS / src / cdrom.c
blob7e4d8cdfab0a43fcbb1086aae9a8ede4b3bcb6bc
1 /* cdrom.c:
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
8 * All rights reserved.
9 * This software may be freely distributed and redistributed for
10 * non-commercial purposes, provided this notice is included.
11 * ----------------------------------------------------------------------
12 * History:
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
56 * per block.
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
60 * went wrong.
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>
72 #ifndef TD_READ64
73 #include <devices/newstyle.h>
74 #define TD_READ64 NSCMD_TD_READ64
75 #endif
77 #include <limits.h>
78 #include <string.h>
79 #include <stdio.h>
81 #include "cdrom.h"
82 #include "debug.h"
83 #include "globals.h"
85 #include "clib_stuff.h"
86 #include <exec/interrupts.h>
88 AROS_INTH1(CDChangeHandler, struct CDVDBase *, global)
90 AROS_INTFUNC_INIT
92 Signal(&global->DosProc->pr_Task, global->g_changeint_sigbit);
93 return 0;
95 AROS_INTFUNC_EXIT
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
108 CDROM *Open_CDROM
110 struct CDVDBase *global,
111 char *p_device,
112 int p_scsi_id,
113 uint32_t p_memory_type,
114 int p_std_buffers,
115 int p_file_buffers
118 CDROM *cd;
119 int i;
120 int err = CDROMERR_OK;
123 * fake try-catch
127 err = CDROMERR_NO_MEMORY;
128 cd = AllocVec (sizeof (CDROM), MEMF_PUBLIC | MEMF_CLEAR | p_memory_type);
130 if (NULL == cd)
131 break;
133 cd->global = global;
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)
141 break;
143 cd->buffer_io = AllocVec(SCSI_BUFSIZE, p_memory_type);
144 if (NULL == cd->buffer_io)
145 break;
147 cd->buffers = AllocVec (sizeof (unsigned char *) * p_std_buffers, MEMF_PUBLIC);
148 if (NULL == cd->buffers)
149 break;
151 cd->current_sectors = AllocVec (sizeof (long) * p_std_buffers, MEMF_PUBLIC);
152 if (NULL == cd->current_sectors)
153 break;
155 cd->last_used = AllocVec (sizeof (uint32_t) * p_std_buffers, MEMF_PUBLIC | MEMF_CLEAR);
156 if (NULL == cd->last_used)
157 break;
161 * make the buffer quad-word aligned. This greatly helps
162 * performance on '040-powered systems with DMA SCSI
163 * controllers.
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)
177 break;
179 err = CDROMERR_IOREQ;
180 cd->scsireq = (struct IOStdReq *)CreateIORequest (cd->port, sizeof (struct IOExtTD));
181 if (NULL == cd->scsireq)
182 break;
184 err = CDROMERR_DEVICE;
185 if (OpenDevice ((UBYTE *) p_device, p_scsi_id, (struct IORequest *) cd->scsireq, 0))
186 break;
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)
194 break;
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. */
216 cd->tick = 0;
217 err = CDROMERR_OK;
218 } while (0);
220 if (CDROMERR_OK != err)
222 Cleanup_CDROM(cd);
223 cd = NULL;
226 return cd;
229 int Do_SCSI_Command
231 CDROM *p_cd,
232 unsigned char *p_buf,
233 long p_buf_length,
234 unsigned char *p_command,
235 int p_length,
236 int p_direction
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);
258 return 0;
260 else
261 return 1;
264 int Read_From_Drive
266 CDROM *p_cd,
267 unsigned char *p_buf,
268 long p_buf_length,
270 long p_sector,
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);
286 return 0;
289 D(bug("[CDVDFS]\tTransfer successful.\n"));
290 return 1;
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;
301 int status;
302 int i;
303 int loc;
304 long vol_size;
305 long start;
306 int count;
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])
313 continue;
316 * get buffer offset
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;
331 return 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)
339 break;
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;
348 uint32_t tick;
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
362 * (but not all)
364 start = p_sector & ~0xf;
365 count = 16;
366 if (global->g_volume != NULL)
367 vol_size = Volume_Size(global->g_volume);
368 else
369 vol_size = 0;
370 if (vol_size != 0 && vol_size - start < count)
371 count = vol_size - start;
372 status =
373 Read_From_Drive(p_cd, p_cd->buffers[loc], SCSI_BUFSIZE, start, count);
375 if (status)
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;
382 return status;
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))
390 return FALSE;
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))
397 return FALSE;
399 return TRUE;
402 int Mode_Select
404 CDROM *p_cd,
405 int p_mode,
406 int p_block_length
409 uint8_t cmd[6] = { };
410 unsigned char mode[12] = { };
412 cmd[0] = 0x15;
413 cmd[1] = 0x10;
414 cmd[4] = 0x0a;
416 mode[3] = 8;
417 mode[4] = p_mode;
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] = { };
429 cmd[0] = 0x12;
430 cmd[4] = 96;
432 if (!Do_SCSI_Command(p_cd,p_cd->buffer_io,96,cmd,6,SCSIF_READ))
433 return FALSE;
435 CopyMem(p_cd->buffer_io, p_data, sizeof (*p_data));
436 return 1;
440 t_toc_data *Read_TOC
442 CDROM *p_cd,
443 uint32_t *p_toc_len
446 uint8_t cmd[10] = { };
447 uint32_t toc_len = 0;
448 uint8_t *buf = p_cd->buffer_io;
451 * check toc len
453 cmd[0] = 0x43;
454 cmd[7] = 0;
455 cmd[8] = 4;
456 if (0 == Do_SCSI_Command(p_cd, buf, 4, cmd, 10, SCSIF_READ))
457 return NULL;
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
463 * number of tracks
465 toc_len = 2 + ((buf[0] << 8) | (buf[1]));
466 if (toc_len < 12)
467 toc_len = 4 + (buf[3] - buf[2] + 2) * 8;
470 * make sure it never happens (shouldn't)
472 if (toc_len > SCSI_BUFSIZE)
473 return NULL;
476 * read complete TOC
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))
481 return NULL;
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)
494 uint32_t toc_len;
495 t_toc_data *toc;
496 int i, len;
498 toc = Read_TOC (p_cd, &toc_len);
499 if (!toc)
500 return FALSE;
503 * calc num TOC entries
504 * last entry is usually LEADOUT (0xAA)
506 len = toc_len >> 3;
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;
517 return FALSE;
521 * Create a buffer containing the start addresses of all data tracks
522 * on the disk.
524 * Returns:
525 * number of tracks or -1 on error.
528 int Data_Tracks(CDROM *p_cd, uint32_t** p_buf)
530 int cnt=0;
531 uint32_t toc_len;
532 t_toc_data *toc;
533 int i, j, len;
536 * collect TOC
538 toc = Read_TOC(p_cd, &toc_len);
539 if (!toc)
540 return -1;
543 * calc TOC entries count
545 len = toc_len >> 3;
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)))
554 cnt++;
557 if (cnt == 0)
558 return 0;
561 * allocate memory for output buffer:
563 *p_buf = (uint32_t*) AllocVec (cnt * sizeof (uint32_t*), MEMF_PUBLIC);
564 if (!*p_buf)
565 return -1;
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;
577 return cnt;
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 */
584 blk %= 4500;
585 msf[1] = blk / 75;
586 msf[2] = blk % 75;
589 int Start_Play_Audio(CDROM *p_cd)
591 uint8_t cmd[10] = { };
592 uint32_t start = 0xffffffff,end;
593 uint32_t toc_len;
594 t_toc_data *toc;
595 int i, len;
597 cmd[0] = 0x47;
600 * read TOC
602 toc = Read_TOC (p_cd, &toc_len);
603 if (!toc)
604 return FALSE;
607 * calc len
609 len = toc_len >> 3;
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;
620 break;
625 * no audio - leave.
627 if (0xffffffff == start)
628 return FALSE;
631 * find end of audio track
633 for (; i<len; i++)
635 if (((99 < toc[i].track_number) && (toc[i].track_number > 99)) ||
636 (0 != (toc[i].flags & 4)))
637 break;
639 end=toc[i].address;
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] = { };
653 cmd[0] = 0x1b;
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);
668 if (p_cd->scsireq)
669 DeleteIORequest((struct IORequest *)p_cd->scsireq);
670 if (p_cd->port)
671 DeleteMsgPort (p_cd->port);
672 if (p_cd->last_used)
673 FreeVec (p_cd->last_used);
674 if (p_cd->current_sectors)
675 FreeVec (p_cd->current_sectors);
676 if (p_cd->buffers)
677 FreeVec (p_cd->buffers);
678 if (p_cd->buffer_io)
679 FreeVec (p_cd->buffer_io);
680 if (p_cd->buffer_data)
681 FreeVec (p_cd->buffer_data);
682 FreeVec (p_cd);
685 void Clear_Sector_Buffers (CDROM *p_cd)
687 int i;
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;
705 cmd[0] = 0x43;
706 cmd[2] = 0x01;
709 * first: READTOC IS MANDATORY
710 * drives not conforming to MMC2 died in 1995.
712 *p_result = 0;
715 * Ask the scsi device for the length of this TOC
717 cmd[7] = 0;
718 cmd[8] = 12;
719 if (!Do_SCSI_Command(p_cd, data, 12, cmd, sizeof(cmd), SCSIF_READ))
720 return FALSE;
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
735 return TRUE;
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]);
743 return TRUE;