includes: AROS_UFIxx -> AROS_INTxx change
[AROS.git] / rom / filesys / CDVDFS / src / cdrom.c
blob36fdeb442f102ff26f11b587c6b944b44fcc4cc4
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-2011 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 * 18-Dec-11 twilen Added media change interrupt support.
15 * 11-Aug-10 sonic Fixed comparison warning in Has_Audio_Tracks()
16 * 01-Mar-10 neil Do not read past end of disc.
17 * 12-Jun-09 neil If drive returns incorrect TOC length, calculate it
18 * based on the number of tracks.
19 * 09-May-09 weissms - Let Do_SCSI_Command result depend on DoIO result.
20 * - Removed redundant checks of io_Error, some code reuse
21 * (Clear_Sector_Buffers).
22 * 20-Mar-09 sonic - Removed usage of AROS-specific include
23 * 08-Mar-09 error - Corrected Test_Unit_Ready returning only NO_DISC state
24 * 06-Mar-09 error - Removed madness, fixed insanity. Cleanup started
25 * 06-Jun-08 sonic - Fixed to compile with gcc v2
26 * 30-Mar-08 error - Updated 'Find_Last_Session' with a generic command
27 * mandatory for all MMC devices; corrected major flaw
28 * with uninitialized variables
29 * 12-Aug-07 sonic - Added some debug output.
30 * 09-Apr-07 sonic - Disabled DirectSCSI on AROS.
31 * 08-Apr-07 sonic - Removed redundant TRACKDISK option.
32 * - Added trackdisk64 support.
33 * - Removed unneeded dealing with block length.
34 * 07-Jul-02 sheutlin various changes when porting to AROS
35 * - global variables are now in a struct Globals *global
36 * 02-Sep-94 fmu Display READ TOC for Apple CD 150 drives.
37 * 01-Sep-94 fmu Workaround for bad NEC 3X READ TOC command in
38 * Has_Audio_Tracks() and Data_Tracks().
39 * 20-Aug-94 fmu New function Find_Last_Session ().
40 * 23-Jul-94 fmu Last index modified from 99 to 1 in Start_Play_Audio().
41 * 18-May-94 fmu New drive model: MODEL_CDU_8002 (= Apple CD 150).
42 * 17-May-94 fmu Sense length 20 instead of 18 (needed by ALF controller).
43 * 17-Feb-94 fmu Added support for Toshiba 4101.
44 * 06-Feb-94 dmb - Fixed bug in Test_Unit_Ready() trackdisk support.
45 * - Fixed bug in Open_CDROM (size of request)
46 * - Added function Clear_Sector_Buffers().
47 * 01-Jan-94 fmu Added function Data_Tracks() for multisession support.
48 * 11-Dec-93 fmu - Memory type can now be chosen by the user.
49 * - Addional parameter p_direction for Do_SCSI_Command().
50 * - Start_Play_Audio() now plays all tracks.
51 * - Mode_Select() instead of Select_XA_Mode().
52 * - Support for CDROM drives with 512, 1024 or 2048 bytes
53 * per block.
54 * 06-Dec-93 fmu New drive type DRIVE_SCSI_2.
55 * 09-Nov-93 fmu Added Select_XA_Mode.
56 * 23-Oct-93 fmu Open_CDROM now returns an error code that tell what
57 * went wrong.
58 * 09-Oct-93 fmu SAS/C support added.
59 * 03-Oct-93 fmu New buffering algorithm.
60 * 27-Sep-93 fmu Added support for multi-LUN devices.
61 * 24-Sep-93 fmu - SCSI buffers may now reside in fast or chip memory.
62 * - TD_CHANGESTATE instead of CMD_READ in Test_Unit_Ready
65 #include <proto/alib.h>
66 #include <proto/exec.h>
67 #include <devices/trackdisk.h>
69 #ifndef TD_READ64
70 #include <devices/newstyle.h>
71 #define TD_READ64 NSCMD_TD_READ64
72 #endif
74 #include <limits.h>
75 #include <string.h>
76 #include <stdio.h>
78 #include "cdrom.h"
79 #include "debug.h"
80 #include "globals.h"
82 #include "clib_stuff.h"
83 #include <exec/interrupts.h>
85 AROS_INTH1(CDChangeHandler, struct CDVDBase *, global)
87 AROS_INTFUNC_INIT
89 Signal(&global->DosProc->pr_Task, global->g_changeint_sigbit);
90 return 0;
92 AROS_INTFUNC_EXIT
96 * i decided to change few things to make this code less insane.
97 * biggest change is - i don't care any more if anyone reads one sector at a time
98 * reading disc 1 sector at a time is totally insane.
99 * presently our schema will read 16 sectors instead, that is, 32768bytes at a time
100 * that should give us SIGNIFICANT speed improvement. unfortunately has some impact
101 * on a cache, too, but that will change over time. currently, cache will eat
102 * STD_BUFFERS * 16 * 2048 bytes
105 CDROM *Open_CDROM
107 struct CDVDBase *global,
108 char *p_device,
109 int p_scsi_id,
110 uint32_t p_memory_type,
111 int p_std_buffers,
112 int p_file_buffers
115 CDROM *cd;
116 int i;
117 int err = CDROMERR_OK;
120 * fake try-catch
124 err = CDROMERR_NO_MEMORY;
125 cd = AllocVec (sizeof (CDROM), MEMF_PUBLIC | MEMF_CLEAR | p_memory_type);
127 if (NULL == cd)
128 break;
130 cd->global = global;
131 cd->buffers_cnt = p_std_buffers;
134 * change: allocating 16 * SCSI_BUFSIZE * bufs; min access unit 32kB!
136 cd->buffer_data = AllocVec (((SCSI_BUFSIZE * p_std_buffers) << 4) + 15, MEMF_PUBLIC | p_memory_type);
137 if (NULL == cd->buffer_data)
138 break;
140 cd->buffer_io = AllocVec(SCSI_BUFSIZE, p_memory_type);
141 if (NULL == cd->buffer_io)
142 break;
144 cd->buffers = AllocVec (sizeof (unsigned char *) * p_std_buffers, MEMF_PUBLIC);
145 if (NULL == cd->buffers)
146 break;
148 cd->current_sectors = AllocVec (sizeof (long) * p_std_buffers, MEMF_PUBLIC);
149 if (NULL == cd->current_sectors)
150 break;
152 cd->last_used = AllocVec (sizeof (uint32_t) * p_std_buffers, MEMF_PUBLIC | MEMF_CLEAR);
153 if (NULL == cd->last_used)
154 break;
158 * make the buffer quad-word aligned. This greatly helps
159 * performance on '040-powered systems with DMA SCSI
160 * controllers.
162 cd->buffers[0] = (UBYTE *)(((IPTR)cd->buffer_data + 15) & ~15);
163 cd->current_sectors[0] = -1;
165 for (i=1; i<cd->buffers_cnt; i++)
167 cd->current_sectors[i] = -1;
168 cd->buffers[i] = cd->buffers[i-1] + (SCSI_BUFSIZE << 4);
171 err = CDROMERR_MSGPORT;
172 cd->port = CreateMsgPort ();
173 if (NULL == cd->port)
174 break;
176 err = CDROMERR_IOREQ;
177 cd->scsireq = (struct IOStdReq *)CreateIORequest (cd->port, sizeof (struct IOExtTD));
178 if (NULL == cd->scsireq)
179 break;
181 err = CDROMERR_DEVICE;
182 if (OpenDevice ((UBYTE *) p_device, p_scsi_id, (struct IORequest *) cd->scsireq, 0))
183 break;
185 cd->device_open = TRUE;
187 if (global->g_scan_interval < 0) {
188 /* Add media change interrupts */
189 cd->iochangeint = (struct IOStdReq *)AllocVec(sizeof (struct IOExtTD), MEMF_PUBLIC);
190 if (NULL == cd->iochangeint)
191 break;
192 CopyMem(cd->scsireq, cd->iochangeint, sizeof (struct IOExtTD));
193 cd->changeint.is_Node.ln_Type = NT_INTERRUPT;
194 cd->changeint.is_Node.ln_Name = "CDFS ChangeInt";
195 cd->changeint.is_Data = (APTR)global;
196 cd->changeint.is_Code = (VOID_FUNC)CDChangeHandler;
197 cd->iochangeint->io_Length = sizeof(struct Interrupt);
198 cd->iochangeint->io_Data = &cd->changeint;
199 cd->iochangeint->io_Command = TD_ADDCHANGEINT;
200 SendIO((struct IORequest *)cd->iochangeint);
203 cd->scsireq->io_Command = CMD_CLEAR;
204 DoIO ((struct IORequest *) cd->scsireq);
206 cd->t_changeint = -1;
207 cd->t_changeint2 = -2;
209 /* The LUN is the 2nd digit of the SCSI id number: */
210 cd->lun = (p_scsi_id / 10) % 10;
212 /* 'tick' is incremented every time a sector is accessed. */
213 cd->tick = 0;
214 err = CDROMERR_OK;
215 } while (0);
217 if (CDROMERR_OK != err)
219 Cleanup_CDROM(cd);
220 cd = NULL;
223 return cd;
226 int Do_SCSI_Command
228 CDROM *p_cd,
229 unsigned char *p_buf,
230 long p_buf_length,
231 unsigned char *p_command,
232 int p_length,
233 int p_direction
236 p_cd->scsireq->io_Length = sizeof (struct SCSICmd);
237 p_cd->scsireq->io_Data = (APTR) &p_cd->cmd;
238 p_cd->scsireq->io_Command = HD_SCSICMD;
240 p_cd->cmd.scsi_Data = (UWORD *) p_buf;
241 p_cd->cmd.scsi_Length = p_buf_length;
242 p_cd->cmd.scsi_Flags = SCSIF_AUTOSENSE | p_direction;
243 p_cd->cmd.scsi_SenseData = (UBYTE *) p_cd->sense;
244 p_cd->cmd.scsi_SenseLength = 20;
245 p_cd->cmd.scsi_SenseActual = 0;
246 p_cd->cmd.scsi_Command = (UBYTE *) p_command;
247 p_cd->cmd.scsi_CmdLength = p_length;
249 p_command[1] |= p_cd->lun << 5;
251 if (0 != DoIO((struct IORequest *) p_cd->scsireq) ||
252 0 != p_cd->cmd.scsi_Status)
254 Clear_Sector_Buffers(p_cd);
255 return 0;
257 else
258 return 1;
261 int Read_From_Drive
263 CDROM *p_cd,
264 unsigned char *p_buf,
265 long p_buf_length,
267 long p_sector,
268 int p_number_of_sectors
271 p_cd->scsireq->io_Length = 2048 * p_number_of_sectors;
272 p_cd->scsireq->io_Data = (APTR) p_buf;
273 p_cd->scsireq->io_Offset = (ULONG) p_sector << 11;
274 p_cd->scsireq->io_Actual = (ULONG) p_sector >> 21;
275 p_cd->scsireq->io_Command = p_cd->scsireq->io_Actual ? TD_READ64 : CMD_READ;
277 D(bug("[CDVDFS]\tAccessing sectors %ld:%ld\n", (long)p_sector, (long)p_number_of_sectors));
279 if (0 != DoIO((struct IORequest*) p_cd->scsireq))
281 D(bug("[CDVDFS]\tTransfer failed: %ld\n", (long)p_cd->scsireq->io_Error));
282 Clear_Sector_Buffers(p_cd);
283 return 0;
286 D(bug("[CDVDFS]\tTransfer successful.\n"));
287 return 1;
291 * USAGE NOTE >> VERY IMPORTANT <<
292 * this procedure delivers you buffer that is 'valid' until the next 16-sector-boundary.
293 * if you want to read from sec 14 till 34, then you have to do 3 calls (14-16, 16-32, 32-34)
295 int Read_Chunk(CDROM *p_cd, long p_sector)
297 struct CDVDBase *global = p_cd->global;
298 int status;
299 int i;
300 int loc;
301 long vol_size;
302 long start;
303 int count;
305 D(bug("[CDVDFS]\tClient requested sector %ld\n", p_sector));
307 for (i=0; i<p_cd->buffers_cnt; i++)
309 if ((p_sector & ~0xf) != p_cd->current_sectors[i])
310 continue;
313 * get buffer offset
315 D(bug("[CDVDFS]\tSector already cached\n"));
316 p_cd->buffer = p_cd->buffers[i] + ((p_sector & 0xf) << 11);
319 * try most frequently used
321 p_cd->last_used[i] += 2;
322 for (i=0; i<p_cd->buffers_cnt; i++)
324 if (p_cd->last_used[i] > 0)
325 p_cd->last_used[i] -= 1;
328 return 1;
332 * find an empty buffer position:
334 for (loc=0; loc<p_cd->buffers_cnt; loc++)
335 if (p_cd->current_sectors[loc] == -1)
336 break;
339 * no free buffer position; remove the buffer that is unused
340 * for the longest time
342 if (loc==p_cd->buffers_cnt)
344 uint32_t oldest_tick = UINT_MAX;
345 uint32_t tick;
347 for (loc=0, i=0; i<p_cd->buffers_cnt; i++)
349 tick = p_cd->last_used[i];
350 if (tick < oldest_tick)
351 loc = i, oldest_tick = tick;
356 * read **16** sectors
357 * NOTE: all DVD discs require chunk size to be at least n*16 sectors
358 * most of the CDs have enough padding (18 sectors) at the end
359 * (but not all)
361 start = p_sector & ~0xf;
362 count = 16;
363 if (global->g_volume != NULL)
364 vol_size = Volume_Size(global->g_volume);
365 else
366 vol_size = 0;
367 if (vol_size != 0 && vol_size - start < count)
368 count = vol_size - start;
369 status =
370 Read_From_Drive(p_cd, p_cd->buffers[loc], SCSI_BUFSIZE, start, count);
372 if (status)
374 p_cd->current_sectors[loc] = p_sector & ~0xf;
375 p_cd->buffer = p_cd->buffers[loc] + ((p_sector & 0xf) << 11);
376 p_cd->last_used[loc] = 1000;
379 return status;
382 int Test_Unit_Ready(CDROM *p_cd)
384 p_cd->scsireq->io_Command = TD_CHANGENUM;
386 if (0 != DoIO ((struct IORequest *) p_cd->scsireq))
387 return FALSE;
389 p_cd->t_changeint = p_cd->scsireq->io_Actual;
391 p_cd->scsireq->io_Command = TD_CHANGESTATE;
392 if ((0 != DoIO ((struct IORequest *) p_cd->scsireq)) ||
393 (0 != p_cd->scsireq->io_Actual))
394 return FALSE;
396 return TRUE;
399 int Mode_Select
401 CDROM *p_cd,
402 int p_mode,
403 int p_block_length
406 uint8_t cmd[6] = { };
407 unsigned char mode[12] = { };
409 cmd[0] = 0x15;
410 cmd[1] = 0x10;
411 cmd[4] = 0x0a;
413 mode[3] = 8;
414 mode[4] = p_mode;
415 mode[9] = p_block_length >> 16;
416 mode[10] = (p_block_length >> 8) & 0xff;
417 mode[11] = p_block_length & 0xff;
419 CopyMem(mode, p_cd->buffer_io, sizeof (mode));
420 return Do_SCSI_Command(p_cd, p_cd->buffer_io, sizeof(mode), cmd, 6, SCSIF_WRITE);
423 int Inquire (CDROM *p_cd, t_inquiry_data *p_data)
425 uint8_t cmd[6] = { };
426 cmd[0] = 0x12;
427 cmd[4] = 96;
429 if (!Do_SCSI_Command(p_cd,p_cd->buffer_io,96,cmd,6,SCSIF_READ))
430 return FALSE;
432 CopyMem(p_cd->buffer_io, p_data, sizeof (*p_data));
433 return 1;
437 t_toc_data *Read_TOC
439 CDROM *p_cd,
440 t_toc_header *p_toc_header
443 uint8_t cmd[10] = { };
444 uint32_t toc_len = 0;
445 uint8_t *buf = p_cd->buffer_io;
448 * check toc len
450 cmd[0] = 0x43;
451 cmd[7] = 0;
452 cmd[8] = 4;
453 if (0 == Do_SCSI_Command(p_cd, buf, 4, cmd, 10, SCSIF_READ))
454 return NULL;
457 * toc len = len field + field contents
458 * Some drives don't return the full TOC length when the buffer is too
459 * small for the whole TOC: in this case, calculate it based on the
460 * number of tracks
462 toc_len = 2 + ((buf[0] << 8) | (buf[1]));
463 if (toc_len < 12)
464 toc_len = 4 + (buf[3] - buf[2] + 2) * 8;
467 * make sure it never happens (shouldn't)
469 if (toc_len > SCSI_BUFSIZE)
470 return NULL;
473 * read complete TOC
475 cmd[7] = toc_len >> 8;
476 cmd[8] = toc_len & 0xff;
477 if (0 == Do_SCSI_Command(p_cd, buf, toc_len, cmd, 10, SCSIF_READ))
478 return NULL;
481 * that's a bit stupid (4 bytes and CopyMem) but still..
483 CopyMem(buf, p_toc_header, sizeof (*p_toc_header));
484 return (t_toc_data *) (buf + 4);
487 int Has_Audio_Tracks(CDROM *p_cd)
489 t_toc_header hdr;
490 t_toc_data *toc;
491 int i, len;
493 toc = Read_TOC (p_cd, &hdr);
494 if (!toc)
495 return FALSE;
498 * calc num TOC entries
499 * last entry is usually LEADOUT (0xAA)
501 len = hdr.length >> 3;
504 * traverse all tracks, check for audio?
506 for (i=0; i<len; i++)
508 if ((99 >= toc[i].track_number) &&
509 (0 == (toc[i].flags & 4)))
510 return toc[i].track_number;
512 return FALSE;
516 * Create a buffer containing the start addresses of all data tracks
517 * on the disk.
519 * Returns:
520 * number of tracks or -1 on error.
523 int Data_Tracks(CDROM *p_cd, uint32_t** p_buf)
525 int cnt=0;
526 t_toc_header hdr;
527 t_toc_data *toc;
528 int i, j, len;
531 * collect TOC
533 toc = Read_TOC(p_cd, &hdr);
534 if (!toc)
535 return -1;
538 * calc TOC entries count
540 len = hdr.length >> 3;
543 * count number of data tracks:
545 for (i=0; i<len; i++)
547 if ((99 >= toc[i].track_number) &&
548 (0 != (toc[i].flags & 4)))
549 cnt++;
552 if (cnt == 0)
553 return 0;
556 * allocate memory for output buffer:
558 *p_buf = (uint32_t*) AllocVec (cnt * sizeof (uint32_t*), MEMF_PUBLIC);
559 if (!*p_buf)
560 return -1;
563 * fill output buffer:
565 for (i=0, j=0; i<len; i++)
567 if ((99 >= toc[i].track_number) &&
568 (0 != (toc[i].flags & 4)))
569 (*p_buf)[j++] = toc[i].address;
572 return cnt;
575 inline void block2msf (uint32_t blk, unsigned char *msf)
577 blk = (blk+150) & 0xffffff;
578 msf[0] = blk / 4500; /* 4500 = 60 seconds * 75 frames */
579 blk %= 4500;
580 msf[1] = blk / 75;
581 msf[2] = blk % 75;
584 int Start_Play_Audio(CDROM *p_cd)
586 uint8_t cmd[10] = { };
587 uint32_t start = 0xffffffff,end;
588 t_toc_header hdr;
589 t_toc_data *toc;
590 int i, len;
592 cmd[0] = 0x47;
595 * read TOC
597 toc = Read_TOC (p_cd, &hdr);
598 if (!toc)
599 return FALSE;
602 * calc len
604 len = hdr.length >> 3;
607 * find beginning of audio track
609 for (i=0; i<len; i++)
611 if ((99 >= toc[i].track_number) &&
612 (0 == (toc[i].flags & 4)))
614 start=toc[i].address;
615 break;
620 * no audio - leave.
622 if (0xffffffff == start)
623 return FALSE;
626 * find end of audio track
628 for (; i<len; i++)
630 if (((99 < toc[i].track_number) && (toc[i].track_number > 99)) ||
631 (0 != (toc[i].flags & 4)))
632 break;
634 end=toc[i].address;
637 * fill up request and send
639 block2msf(start, &cmd[3]);
640 block2msf(end-1, &cmd[6]);
642 return Do_SCSI_Command(p_cd, 0, 0, cmd, 10, SCSIF_READ);
645 int Stop_Play_Audio(CDROM *p_cd)
647 uint8_t cmd[6] = { };
648 cmd[0] = 0x1b;
649 return Do_SCSI_Command(p_cd, 0, 0, cmd, 6, SCSIF_READ);
652 void Cleanup_CDROM (CDROM *p_cd)
654 if (p_cd->iochangeint) {
655 p_cd->iochangeint->io_Length = sizeof(struct Interrupt);
656 p_cd->iochangeint->io_Data = &p_cd->changeint;
657 p_cd->iochangeint->io_Command = TD_REMCHANGEINT;
658 DoIO((struct IORequest *)p_cd->iochangeint);
659 FreeVec(p_cd->iochangeint);
661 if (p_cd->device_open)
662 CloseDevice ((struct IORequest *) p_cd->scsireq);
663 if (p_cd->scsireq)
664 DeleteIORequest((struct IORequest *)p_cd->scsireq);
665 if (p_cd->port)
666 DeleteMsgPort (p_cd->port);
667 if (p_cd->last_used)
668 FreeVec (p_cd->last_used);
669 if (p_cd->current_sectors)
670 FreeVec (p_cd->current_sectors);
671 if (p_cd->buffers)
672 FreeVec (p_cd->buffers);
673 if (p_cd->buffer_io)
674 FreeVec (p_cd->buffer_io);
675 if (p_cd->buffer_data)
676 FreeVec (p_cd->buffer_data);
677 FreeVec (p_cd);
680 void Clear_Sector_Buffers (CDROM *p_cd)
682 int i;
684 for (i=0; i<p_cd->buffers_cnt; i++)
685 p_cd->current_sectors[i] = -1;
688 /* Finds offset of last session. (Not supported by all CDROM drives)
690 * Returns: - FALSE if there is no special SCSI command to determine the
691 * offset of the last session.
692 * - TRUE if the offset of the last session has been determined.
695 int Find_Last_Session(CDROM *p_cd, uint32_t *p_result)
697 uint8_t cmd[10] = { };
698 uint8_t *data = p_cd->buffer_io;
700 cmd[0] = 0x43;
701 cmd[2] = 0x01;
704 * first: READTOC IS MANDATORY
705 * drives not conforming to MMC2 died in 1995.
707 *p_result = 0;
710 * Ask the scsi device for the length of this TOC
712 cmd[7] = 0;
713 cmd[8] = 12;
714 if (!Do_SCSI_Command(p_cd, data, 12, cmd, sizeof(cmd), SCSIF_READ))
715 return FALSE;
718 * check if we are dealing with a DATA track here.
719 * nobody would like to spend an hour trying to get his mixed mode cd read
721 D(bug("[CDVDFS]\tFirst track in last session has type %lx\n", data[5]));
722 if ((data[5] & 0xfc) != 0x14)
724 D(bug("[CDVDFS]\tThis track is not a DATA track. Will default to track 1.\n"));
727 * this indeed sets the address of first track in first session, but we have no idea if this really is a data track.
728 * we don't care anyways. it will be detected by higher layer
730 return TRUE;
734 * the ReadTOC has MSF field set to 0 so we treat obtained values as logical block address
737 *p_result = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | (data[11]);
738 return TRUE;