2 * CD Info 1.1 - prints various information about a CD,
3 * detects the type of the CD.
5 * (c) 1996,1997,1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
6 * and Heiko Eissfeldt <heiko@colossus.escape.de>
16 #include <sys/ioctl.h>
18 # include <linux/version.h>
19 # include <linux/cdrom.h>
20 # if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,50)
21 # include <linux/ucdrom.h>
26 #define CDROM_LEADOUT (0xAA)
31 Subject: -65- How can I read an IRIX (EFS) CD-ROM on a machine which
33 Date: 18 Jun 1995 00:00:01 EST
35 You want 'efslook', at
36 ftp://viz.tamu.edu/pub/sgi/software/efslook.tar.gz.
39 ! Robert E. Seastrom <rs@access4.digex.net>'s software (with source
40 ! code) for using an SGI CD-ROM on a Macintosh is at
41 ! ftp://bifrost.seastrom.com/pub/mac/CDROM-Jumpstart.sit151.hqx.
45 #define FS_NO_DATA 0 /* audio only */
46 #define FS_HIGH_SIERRA 1
48 #define FS_INTERACTIVE 3
52 #define FS_ISO_HFS 7 /* both hfs & isofs filesystem */
53 #define FS_ISO_9660_INTERACTIVE 8 /* both CD-RTOS and isofs filesystem */
59 #define MULTISESSION 32
61 #define HIDDEN_TRACK 128
65 #define ROCKRIDGE 2048
69 #define STRONG "\033[1m"
70 #define NORMAL "\033[0m"
72 #define STRONG "__________________________________\n"
76 int filehandle; /* Handle of /dev/>cdrom< */
77 int rc; /* return code */
79 int isofs_size = 0; /* size of session */
80 int start_track; /* first sector of track */
81 int ms_offset; /* multisession offset found by track-walking */
82 int data_start; /* start of data area */
85 char buffer[2048]; /* for CD-Data */
86 char buffer2[2048]; /* for CD-Data */
87 char buffer3[2048]; /* for CD-Data */
88 char buffer4[2048]; /* for CD-Data */
89 char buffer5[2048]; /* for CD-Data */
91 char toc_header[2]; /* first/last Track */
92 struct cdrom_tocentry *toc[CDROM_LEADOUT+1]; /* TOC-entries */
94 struct cdrom_multisession ms;
95 struct cdrom_subchnl sub;
96 int first_data = -1; /* # of first data track */
97 int num_data = 0; /* # of data tracks */
98 int first_audio = -1; /* # of first audio track */
99 int num_audio = 0; /* # of audio tracks */
101 /* ------------------------------------------------------------------------ */
102 /* some iso9660 fiddling */
105 int read_super(int offset)
107 /* sector 16, super block */
108 memset(buffer,0,2048);
109 if (0 > lseek(filehandle,2048*(offset+16),SEEK_SET))
112 printf("about to read sector %u\n", offset + 16);
114 if (0 > read(filehandle,buffer,2048))
119 int read_super2(int offset)
121 /* sector 0, for photocd check */
122 memset(buffer2,0,2048);
123 if (0 > lseek(filehandle,2048*(offset+0),SEEK_SET))
126 printf("about to read sector %u\n", offset + 0);
128 if (0 > read(filehandle,buffer2,2048))
133 int read_super3(int offset)
135 /* sector 4, for ufs check */
136 memset(buffer3,0,2048);
137 if (0 > lseek(filehandle,2048*(offset+4),SEEK_SET))
140 printf("about to read sector %u\n", offset + 4);
142 if (0 > read(filehandle,buffer3,2048))
147 int read_super4(int offset)
149 /* sector 17, for bootable CD check */
150 memset(buffer4,0,2048);
151 if (0 > lseek(filehandle,2048*(offset+17),SEEK_SET))
154 printf("about to read sector %u\n", offset + 17);
156 if (0 > read(filehandle,buffer4,2048))
161 int read_super5(int offset)
163 /* sector 150, for Video CD check */
164 memset(buffer5,0,2048);
165 if (0 > lseek(filehandle,2048*(offset+150),SEEK_SET))
168 printf("about to read sector %u\n", offset + 150);
170 if (0 > read(filehandle,buffer5,2048))
177 return 0 == memcmp(&buffer[1],"CD001",5);
182 return 0 == memcmp(&buffer[9],"CDROM",5);
187 return (0 == memcmp(&buffer[1],"CD-I",4));
192 return (0 == memcmp(&buffer[8],"CD-RTOS",7));
197 return (0 == memcmp(&buffer[16],"CD-BRIDGE",9));
202 return 0 == memcmp(&buffer[1024],"CD-XA001",8);
207 return (0 == memcmp(&buffer[8],"CDTV",4));
212 return 0 == memcmp(&buffer2[64], "PPPPHHHHOOOOTTTTOOOO____CCCCDDDD", 24);
217 return (0 == memcmp(&buffer2[512],"PM",2)) ||
218 (0 == memcmp(&buffer2[512],"TS",2)) ||
219 (0 == memcmp(&buffer2[1024], "BD",2));
224 return 0 == memcmp(&buffer2[0x438],"\x53\xef",2);
229 return (0 == memcmp(&buffer2[0],"\x01\x5a\x5a\x5a\x5a\x5a\x01", 7)) &&
230 (0 == memcmp(&buffer2[40], "CD-ROM", 6));
235 return 0 == memcmp(&buffer3[1372],"\x54\x19\x01\x0" ,4);
238 int is_bootable(void)
240 return 0 == memcmp(&buffer4[7],"EL TORITO",9);
245 return 2 == buffer4[0] && buffer4[88] == 0x25 && buffer4[89] == 0x2f;
248 int is_video_cdi(void)
250 return 0 == memcmp(&buffer5[0],"VIDEO_CD",8);
253 int get_size(void) /* iso9660 volume space in 2048 byte units */
255 return ((buffer[80] & 0xff) |
256 ((buffer[81] & 0xff) << 8) |
257 ((buffer[82] & 0xff) << 16) |
258 ((buffer[83] & 0xff) << 24));
261 int get_joliet_level( void )
263 switch (buffer4[90]) {
271 int guess_filesystem(int start_session)
275 if (read_super(start_session) < 0)
280 /* buffer is defined */
281 if (is_cdi()) printf("CD-I, ");
282 if (is_cd_rtos()) printf("CD-RTOS, ");
283 if (is_isofs()) printf("ISOFS, ");
284 if (is_hs()) printf("HS, ");
285 if (is_bridge()) printf("BRIDGE, ");
286 if (is_xa()) printf("XA, ");
287 if (is_cdtv()) printf("CDTV, ");
292 if (is_cdi() && is_cd_rtos() && !is_bridge() && !is_xa()) {
293 return FS_INTERACTIVE;
294 } else { /* read sector 0 ONLY, when NO greenbook CD-I !!!! */
296 if (read_super2(start_session) < 0)
300 /* buffer2 is defined */
301 if (is_photocd()) printf("PHOTO CD, ");
302 if (is_hfs()) printf("HFS, ");
303 if (is_ext2()) printf("EXT2 FS, ");
304 if (is_3do()) printf("3DO, ");
308 ret |= FS_HIGH_SIERRA;
309 else if (is_isofs()) {
310 if (is_cd_rtos() && is_bridge())
311 ret = FS_ISO_9660_INTERACTIVE;
316 isofs_size = get_size();
321 if (read_super4(start_session) < 0)
325 /* buffer4 is defined */
326 if (is_joliet()) printf("JOLIET, ");
328 if (is_bootable()) printf("BOOTABLE, ");
332 joliet_level = get_joliet_level();
338 if (is_bridge() && is_xa() && is_isofs() && is_cd_rtos() &&
340 if (read_super5(start_session) < 0)
344 /* buffer5 is defined */
345 if (is_video_cdi()) printf("VIDEO-CDI, ");
359 if (read_super3(start_session) < 0)
363 /* buffer3 is defined */
364 if (is_ufs()) printf("UFS, ");
383 /* ------------------------------------------------------------------------ */
404 for (i = 1; i <= toc_header[1]; i++) {
405 n += cddb_sum(toc[i]->cdte_addr.msf.minute * 60 +
406 toc[i]->cdte_addr.msf.second);
409 t = + toc[CDROM_LEADOUT]->cdte_addr.msf.minute * 60
410 + toc[CDROM_LEADOUT]->cdte_addr.msf.second
411 - toc[1]->cdte_addr.msf.minute * 60
412 - toc[1]->cdte_addr.msf.second;
414 return ((n % 0xff) << 24 | t << 8 | toc_header[1]);
417 /* ------------------------------------------------------------------------ */
419 char *devname = "/dev/cdrom";
423 main(int argc, char *argv[])
427 progname = strrchr(argv[0],'/');
428 progname = progname ? progname+1 : argv[0];
431 if (0 == strncmp(argv[1],"/dev/",5))
434 devname=malloc(6+strlen(argv[1]));
435 sprintf(devname,"/dev/%s",argv[1]);
439 printf("CD Info 1.1 | (c) 1996-98 Gerd Knorr & Heiko Eißfeldt\n");
442 filehandle = open(devname,O_RDONLY);
443 if (filehandle == -1) {
444 fprintf(stderr,"%s: %s: %s\n",progname, devname, strerror(errno));
448 #ifdef CDROMREADTOCHDR
449 /* read the number of tracks from CD*/
450 if (ioctl(filehandle,CDROMREADTOCHDR,&toc_header)) {
451 fprintf(stderr,"%s: read TOC ioctl failed, give up\n",progname);
455 printf(STRONG "track list (%i - %i)\n" NORMAL,
456 toc_header[0],toc_header[1]);
459 printf(" nr: msf lba ctrl adr type\n");
460 for (i = toc_header[0]; i <= CDROM_LEADOUT; i++) {
461 toc[i] = malloc(sizeof(struct cdrom_tocentry));
462 if (toc[i] == NULL) {
463 fprintf(stderr, "out of memory\n");
466 memset(toc[i],0,sizeof(struct cdrom_tocentry));
467 toc[i]->cdte_track = i;
468 toc[i]->cdte_format = CDROM_MSF;
469 if (ioctl(filehandle,CDROMREADTOCENTRY,toc[i])) {
471 "%s: read TOC entry ioctl failed for track %i, give up\n",
472 progname,toc[i]->cdte_track);
475 printf("%3d: %02d:%02d:%02d (%06d) 0x%x 0x%x %s%s\n",
476 (int)toc[i]->cdte_track,
477 (int)toc[i]->cdte_addr.msf.minute,
478 (int)toc[i]->cdte_addr.msf.second,
479 (int)toc[i]->cdte_addr.msf.frame,
480 (int)toc[i]->cdte_addr.msf.frame +
481 (int)toc[i]->cdte_addr.msf.second*75 +
482 (int)toc[i]->cdte_addr.msf.minute*75*60 - 150,
483 (int)toc[i]->cdte_ctrl,
484 (int)toc[i]->cdte_adr,
485 (toc[i]->cdte_ctrl & CDROM_DATA_TRACK) ? "data ":"audio",
486 CDROM_LEADOUT == i ? " (leadout)" : "");
487 if (i == CDROM_LEADOUT)
489 if (toc[i]->cdte_ctrl & CDROM_DATA_TRACK) {
491 if (-1 == first_data)
492 first_data = toc[i]->cdte_track;
495 if (-1 == first_audio)
496 first_audio = toc[i]->cdte_track;
498 /* skip to leadout */
499 if (i == (int)(toc_header[1]))
503 printf(STRONG "what ioctl's report\n" NORMAL);
507 printf("get mcn : "); fflush(stdout);
508 if (ioctl(filehandle,CDROM_GET_MCN,&mcn))
511 printf("%s\n",mcn.medium_catalog_number);
514 #ifdef CDROM_DISC_STATUS
515 /* get disk status */
516 printf("disc status : "); fflush(stdout);
517 switch (ioctl(filehandle,CDROM_DISC_STATUS,0)) {
518 case CDS_NO_INFO: printf("no info\n"); break;
519 case CDS_NO_DISC: printf("no disc\n"); break;
520 case CDS_AUDIO: printf("audio\n"); break;
521 case CDS_DATA_1: printf("data mode 1\n"); break;
522 case CDS_DATA_2: printf("data mode 2\n"); break;
523 case CDS_XA_2_1: printf("XA mode 1\n"); break;
524 case CDS_XA_2_2: printf("XA mode 2\n"); break;
525 default: printf("unknown (failed?)\n");
529 #ifdef CDROMMULTISESSION
530 /* get multisession */
531 printf("multisession: "); fflush(stdout);
532 ms.addr_format = CDROM_LBA;
533 if (ioctl(filehandle,CDROMMULTISESSION,&ms))
536 printf("%d%s\n",ms.addr.lba,ms.xa_flag?" XA":"");
540 /* get audio status from subchnl */
541 printf("audio status: "); fflush(stdout);
542 sub.cdsc_format = CDROM_MSF;
543 if (ioctl(filehandle,CDROMSUBCHNL,&sub))
546 switch (sub.cdsc_audiostatus) {
547 case CDROM_AUDIO_INVALID: printf("invalid\n"); break;
548 case CDROM_AUDIO_PLAY: printf("playing"); break;
549 case CDROM_AUDIO_PAUSED: printf("paused"); break;
550 case CDROM_AUDIO_COMPLETED: printf("completed\n"); break;
551 case CDROM_AUDIO_ERROR: printf("error\n"); break;
552 case CDROM_AUDIO_NO_STATUS: printf("no status\n"); break;
553 default: printf("Oops: unknown\n");
555 if (sub.cdsc_audiostatus == CDROM_AUDIO_PLAY ||
556 sub.cdsc_audiostatus == CDROM_AUDIO_PAUSED) {
557 printf(" at: %02d:%02d abs / %02d:%02d track %d\n",
558 sub.cdsc_absaddr.msf.minute,
559 sub.cdsc_absaddr.msf.second,
560 sub.cdsc_reladdr.msf.minute,
561 sub.cdsc_reladdr.msf.second,
566 printf(STRONG "try to find out what sort of CD this is\n" NORMAL);
568 /* try to find out what sort of CD we have */
570 /* no data track, may be a "real" audio CD or hidden track CD */
571 start_track = (int)toc[1]->cdte_addr.msf.frame +
572 (int)toc[1]->cdte_addr.msf.second*75 +
573 (int)toc[1]->cdte_addr.msf.minute*75*60 - 150;
574 /* CD-I/Ready says start_track <= 30*75 then CDDA */
575 if (start_track > 100 /* 100 is just a guess */) {
576 fs = guess_filesystem(0);
577 if ((fs & FS_MASK) != FS_UNKNOWN)
580 fs &= ~FS_MASK; /* del filesystem info */
581 printf("Oops: %i unused sectors at start, but hidden track check failed.\n",start_track);
585 /* we have data track(s) */
586 for (j = 2, i = first_data; i <= toc_header[1]; i++) {
587 if (!(toc[i]->cdte_ctrl & CDROM_DATA_TRACK))
589 start_track = (i == 1) ? 0 :
590 (int)toc[i]->cdte_addr.msf.frame +
591 (int)toc[i]->cdte_addr.msf.second*75 +
592 (int)toc[i]->cdte_addr.msf.minute*75*60
594 /* save the start of the data area */
596 data_start = start_track;
598 /* skip tracks which belong to the current walked session */
599 if (start_track < data_start + isofs_size)
602 fs = guess_filesystem(start_track);
603 if (!(((fs & FS_MASK) == FS_ISO_9660 ||
604 (fs & FS_MASK) == FS_ISO_HFS ||
605 /* (fs & FS_MASK) == FS_ISO_9660_INTERACTIVE) && (fs & XA))) */
606 (fs & FS_MASK) == FS_ISO_9660_INTERACTIVE)))
607 break; /* no method for non-iso9660 multisessions */
610 /* track is beyond last session -> new session found */
611 ms_offset = start_track;
612 printf("session #%d starts at track %2i, offset %6i, isofs size %6i\n",
613 j++,toc[i]->cdte_track,start_track,isofs_size);
614 printf("iso9660: %i MB size, label `%.32s'\n",
615 isofs_size/512,buffer+40);
621 switch(fs & FS_MASK) {
624 printf("Audio CD, cddb disc ID is %08lx\n",cddb_discid());
627 printf("CD-ROM with iso9660 fs");
629 printf(" and joliet extension level %d", joliet_level);
631 printf(" and rockridge extensions");
634 case FS_ISO_9660_INTERACTIVE:
635 printf("CD-ROM with CD-RTOS and iso9660 fs\n");
638 printf("CD-ROM with high sierra fs\n");
641 printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : "");
644 printf("CD-ROM with Macintosh HFS\n");
647 printf("CD-ROM with both Macintosh HFS and iso9660 fs\n");
650 printf("CD-ROM with Unix UFS\n");
653 printf("CD-ROM with Linux second extended filesystem\n");
656 printf("CD-ROM with Panasonic 3DO filesystem\n");
659 printf("CD-ROM with unknown filesystem\n");
662 switch(fs & FS_MASK) {
664 case FS_ISO_9660_INTERACTIVE:
666 printf("iso9660: %i MB size, label `%.32s'\n",
667 isofs_size/512,buffer+40);
671 if (first_data == 1 && num_audio > 0)
672 need_lf += printf("mixed mode CD ");
674 need_lf += printf("XA sectors ");
675 if (fs & MULTISESSION)
676 need_lf += printf("Multisession, offset = %i ",ms_offset);
677 if (fs & HIDDEN_TRACK)
678 need_lf += printf("Hidden Track ");
680 need_lf += printf("%sPhoto CD ", num_audio > 0 ? " Portfolio " : "");
682 need_lf += printf("Commodore CDTV ");
684 need_lf += printf("CD-Plus/Extra ");
686 need_lf += printf("bootable CD ");
687 if (fs & VIDEOCDI && num_audio == 0)
688 need_lf += printf("Video CD ");
689 if (need_lf) puts("");