1 /******************************************************************
2 * CopyPolicy: GNU Lesser General Public License 2.1 applies
3 * Copyright (C) 1998-2008 Monty xiphmont@mit.edu
5 * Autoscan for or verify presence of a cdrom device
7 ******************************************************************/
9 #define _GNU_SOURCE /* get cuserid */
10 #define _USE_XOPEN /* get cuserid */
19 #include <sys/types.h>
20 #include "cdda_interface.h"
21 #include "low_interface.h"
22 #include "common_interface.h"
25 #define MAX_DEV_LEN 20 /* Safe because strings only come from below */
26 /* must be absolute paths! */
27 static char *scsi_cdrom_prefixes
[]={
31 static char *scsi_generic_prefixes
[]={
35 static char *devfs_scsi_test
="/dev/scsi/";
36 static char *devfs_scsi_cd
="cd";
37 static char *devfs_scsi_generic
="generic";
39 static char *cdrom_devices
[]={
51 /* "/dev/aztcd", timeout is too long */
56 /* Functions here look for a cdrom drive; full init of a drive type
57 happens in interface.c */
59 cdrom_drive
*cdda_find_a_cdrom(int messagedest
,char **messages
){
65 while(cdrom_devices
[i
]!=NULL
){
67 /* is it a name or a pattern? */
69 if((pos
=strchr(cdrom_devices
[i
],'?'))){
71 /* try first eight of each device */
73 char *buffer
=copystring(cdrom_devices
[i
]);
75 /* number, then letter */
77 buffer
[pos
-(cdrom_devices
[i
])]=j
+48;
78 if((d
=cdda_identify(buffer
,messagedest
,messages
)))
80 idmessage(messagedest
,messages
,"",NULL
);
81 buffer
[pos
-(cdrom_devices
[i
])]=j
+97;
82 if((d
=cdda_identify(buffer
,messagedest
,messages
)))
84 idmessage(messagedest
,messages
,"",NULL
);
87 /* Name. Go for it. */
88 if((d
=cdda_identify(cdrom_devices
[i
],messagedest
,messages
)))
91 idmessage(messagedest
,messages
,"",NULL
);
95 idmessage(messagedest
,messages
,
96 "\n\nNo cdrom drives accessible to %s found.\n",
101 cdrom_drive
*cdda_identify(const char *device
, int messagedest
,char **messages
){
105 idmessage(messagedest
,messages
,"Checking %s for cdrom...",device
);
107 if(stat(device
,&st
)){
108 idperror(messagedest
,messages
,"\tCould not stat %s",device
);
113 if (!S_ISCHR(st
.st_mode
) &&
114 !S_ISBLK(st
.st_mode
)){
115 idmessage(messagedest
,messages
,"\t%s is not a block or character device",device
);
120 /* an IDE device may have scsi-ide support, SG_IO support and cooked
121 support. Prefer the SCSI variants, they give the most control */
122 d
=cdda_identify_scsi(NULL
,device
,messagedest
,messages
);
123 if(!d
)d
=cdda_identify_cooked(device
,messagedest
,messages
);
126 if(!d
)d
=cdda_identify_test(device
,messagedest
,messages
);
132 char *test_resolve_symlink(const char *file
,int messagedest
,char **messages
){
133 char resolved
[PATH_MAX
];
136 idperror(messagedest
,messages
,"\t\tCould not stat %s",file
);
140 if(realpath(file
,resolved
))
141 return(strdup(resolved
));
143 idperror(messagedest
,messages
,"\t\tCould not resolve symlink %s",file
);
148 cdrom_drive
*cdda_identify_cooked(const char *dev
, int messagedest
,
155 char *description
=NULL
;
158 idmessage(messagedest
,messages
,"\tTesting %s for cooked ioctl() interface",dev
);
160 device
=test_resolve_symlink(dev
,messagedest
,messages
);
161 if(device
==NULL
)return(NULL
);
163 if(stat(device
,&st
)){
164 idperror(messagedest
,messages
,"\t\tCould not stat %s",device
);
169 if (!S_ISCHR(st
.st_mode
) &&
170 !S_ISBLK(st
.st_mode
)){
171 idmessage(messagedest
,messages
,"\t\t%s is not a block or character device",device
);
176 type
=(int)(st
.st_rdev
>>8);
183 /* Ping for CDROM-ness */
185 fd
=open(device
,O_RDONLY
|O_NONBLOCK
);
187 idperror(messagedest
,messages
,"\t\tUnable to open %s",device
);
192 if(ioctl_ping_cdrom(fd
)){
193 idmessage(messagedest
,messages
,"\t\tDevice %s is not a CDROM",device
);
199 char *temp
=atapi_drive_info(fd
);
200 description
=catstring(NULL
,"ATAPI compatible ");
201 description
=catstring(description
,temp
);
206 case CDU31A_CDROM_MAJOR
:
207 /* major indicates this is a cdrom; no ping necessary. */
208 description
=copystring("Sony CDU31A or compatible");
210 case CDU535_CDROM_MAJOR
:
211 /* major indicates this is a cdrom; no ping necessary. */
212 description
=copystring("Sony CDU535 or compatible");
215 case MATSUSHITA_CDROM_MAJOR
:
216 case MATSUSHITA_CDROM2_MAJOR
:
217 case MATSUSHITA_CDROM3_MAJOR
:
218 case MATSUSHITA_CDROM4_MAJOR
:
219 /* major indicates this is a cdrom; no ping necessary. */
220 description
=copystring("non-ATAPI IDE-style Matsushita/Panasonic CR-5xx or compatible");
222 case SANYO_CDROM_MAJOR
:
223 description
=copystring("Sanyo proprietary or compatible: NOT CDDA CAPABLE");
225 case MITSUMI_CDROM_MAJOR
:
226 case MITSUMI_X_CDROM_MAJOR
:
227 description
=copystring("Mitsumi proprietary or compatible: NOT CDDA CAPABLE");
229 case OPTICS_CDROM_MAJOR
:
230 description
=copystring("Optics Dolphin or compatible: NOT CDDA CAPABLE");
232 case AZTECH_CDROM_MAJOR
:
233 description
=copystring("Aztech proprietary or compatible: NOT CDDA CAPABLE");
235 case GOLDSTAR_CDROM_MAJOR
:
236 description
=copystring("Goldstar proprietary: NOT CDDA CAPABLE");
238 case CM206_CDROM_MAJOR
:
239 description
=copystring("Philips/LMS CM206 proprietary: NOT CDDA CAPABLE");
242 case SCSI_CDROM_MAJOR
:
243 case SCSI_GENERIC_MAJOR
:
245 idmessage(messagedest
,messages
,"\t\t%s is not a cooked ioctl CDROM.",device
);
249 /* What the hell is this? */
250 idmessage(messagedest
,messages
,"\t\t%s is not a cooked ioctl CDROM.",device
);
257 d
=calloc(1,sizeof(cdrom_drive
));
258 d
->cdda_device_name
=device
;
259 d
->ioctl_device_name
=copystring(device
);
260 d
->drive_model
=description
;
264 d
->interface
=COOKED_IOCTL
;
265 d
->bigendianp
=-1; /* We don't know yet... */
267 d
->private=calloc(1,sizeof(*d
->private));
271 d
->private->clock
=(clock_gettime(CLOCK_MONOTONIC
,&tv
)<0?CLOCK_REALTIME
:CLOCK_MONOTONIC
);
273 idmessage(messagedest
,messages
,"\t\tCDROM sensed: %s\n",description
);
278 long l1
; /* target | lun << 8 | channel << 16 | low_ino << 24 */
279 long l2
; /* Unique id */
282 typedef struct scsiid
{
288 /* Even *this* isn't as simple as it bloody well should be :-P */
289 /* SG has an easy interface, but SCSI overall does not */
290 static int get_scsi_id(int fd
, scsiid
*id
){
294 /* get the host/id/lun */
296 if(fd
==-1)return(-1);
297 if(ioctl(fd
,SCSI_IOCTL_GET_IDLUN
,&argid
))return(-1);
298 id
->bus
=argid
.l2
; /* for now */
299 id
->id
=argid
.l1
&0xff;
300 id
->lun
=(argid
.l1
>>8)&0xff;
302 if(ioctl(fd
,SCSI_IOCTL_GET_BUS_NUMBER
,&busarg
)==0)
308 /* slightly wasteful, but a clean abstraction */
309 static char *scsi_match(const char *device
,char **prefixes
,
312 char *prompt
,int messagedest
,char **messages
){
313 int dev
=open(device
,O_RDONLY
|O_NONBLOCK
);
319 /* if we're running under /devfs, build the device name from the
320 device we already have */
321 if(!strncmp(device
,devfs_test
,strlen(devfs_test
))){
323 strcpy(buffer
,device
);
324 pos
=strrchr(buffer
,'/');
327 sprintf(pos
,"/%s",devfs_other
);
328 matchf
=open(buffer
,O_RDONLY
|O_NONBLOCK
);
329 for (i
= 0; (i
<10) && (matchf
==-1); i
++) {
330 fprintf(stderr
, "Error trying to open %s exclusively (%s). retrying in 1 seconds.\n", buffer
, strerror(errno
));
331 usleep(1000000 + 100000.0 * rand()/(RAND_MAX
+1.0));
332 matchf
= open(buffer
,O_RDONLY
|O_NONBLOCK
);
337 return(strdup(buffer
));
342 /* get the host/id/lun */
344 idperror(messagedest
,messages
,"\t\tCould not access device %s",
349 if(get_scsi_id(dev
,&a
)){
350 idperror(messagedest
,messages
,"\t\tDevice %s could not perform ioctl()",
356 /* go through most likely /dev nodes for a match */
362 while(prefixes
[pattern
]!=NULL
){
366 sprintf(buffer
,"%s%d",prefixes
[pattern
],i
);
370 sprintf(buffer
,"%s%c",prefixes
[pattern
],i
+'a');
374 matchf
=open(buffer
,O_RDONLY
|O_NONBLOCK
);
375 for (k
= 0; (k
<10) && (matchf
==-1); k
++) {
376 fprintf(stderr
, "Error trying to open %s exclusively (%s). retrying in 1 second.\n", buffer
, strerror(errno
));
377 usleep(1000000 + 100000.0 * rand()/(RAND_MAX
+1.0));
378 matchf
=open(buffer
,O_RDONLY
|O_NONBLOCK
);
382 if(get_scsi_id(matchf
,&b
)==0){
383 if(a
.bus
==b
.bus
&& a
.id
==b
.id
&& a
.lun
==b
.lun
){
386 return(strdup(buffer
));
396 idmessage(messagedest
,messages
,prompt
,device
);
400 if(dev
!=-1)close(dev
);
404 void strscat(char *a
,char *b
,int n
){
414 /* At this point, we're going to punt compatability before SG2, and
415 allow only SG2 and SG3 */
416 static int verify_SG_version(cdrom_drive
*d
,int messagedest
,
418 /* are we using the new SG driver by Doug Gilbert? If not, punt */
419 int version
,major
,minor
;
421 idmessage(messagedest
,messages
,
422 "\nFound an accessible SCSI CDROM drive."
423 "\nLooking at revision of the SG interface in use...","");
425 if(ioctl(d
->cdda_fd
,SG_GET_VERSION_NUM
,&version
)){
427 idmessage(messagedest
,messages
,
428 "\tOOPS! Old 2.0/early 2.1/early 2.2.x (non-ac patch) style "
429 "SG.\n\tCdparanoia no longer supports the old interface.\n","");
433 version
-=major
*10000;
437 sprintf(buffer
,"\tSG interface version %d.%d.%d; OK.",
438 major
,minor
,version
);
440 idmessage(messagedest
,messages
,buffer
,"");
444 int check_sgio(const char *device
, int messagedest
, char **messages
){
446 struct sg_io_hdr hdr
;
448 if (!device
) return 0;
450 /* we don't really care what type of device it is -- if it can do
451 * SG_IO, then we'll put it through the normal mmc/atapi/etc tests
452 * later, but it's good enough for now. */
453 fd
= open(device
, O_RDWR
|O_NONBLOCK
);
455 idperror(messagedest
,messages
,
456 "\t\tCould not access device %s to test for SG_IO support",device
);
460 memset(&hdr
, 0, sizeof (struct sg_io_hdr
));
461 /* First try with interface_id = 'A'; for all but the sg case,
462 * that'll get us a -EINVAL if it supports SG_IO, and some other
463 * error for all other cases. */
464 hdr
.interface_id
= 'A';
465 if (ioctl(fd
, SG_IO
, &hdr
)) {
467 case EINVAL
: /* sr and ata give us EINVAL when SG_IO is
468 * supported but interface_id is bad. */
470 case ENOSYS
: /* sg gives us ENOSYS when SG_IO is supported but
471 * interface_id is bad. IMHO, this is wrong and
472 * needs fixing in the kernel. */
477 default: /* everything else gives ENOTTY, I think. I'm just
478 * going to be paranoid and reject everything else. */
485 /* if we get here, something is dreadfuly wrong. ioctl(fd,SG_IO,&hdr)
486 * handled SG_IO, but took hdr.interface_id = 'A' as valid, and an empty
487 * command as good. Don't trust it. */
492 /* scanning is always done by specifying a device name in
493 specialized_device; generic_device is only filled when the generic device
494 force option is used, and in that case, use of SG (not SGIO) should indeed be
496 cdrom_drive
*cdda_identify_scsi(const char *generic_device
,
497 const char *specialized_device
, int messagedest
,
511 idmessage(messagedest
,messages
,"\tTesting %s for SCSI/MMC interface",
514 if(specialized_device
)
515 idmessage(messagedest
,messages
,"\tTesting %s for SCSI/MMC interface",
521 idmessage(messagedest
,messages
,"\t\tgeneric device forced; not testing for SG_IO interface",
524 if(stat(generic_device
,&g_st
)){
525 idperror(messagedest
,messages
,"\t\tCould not access device %s",
530 if((int)(g_st
.st_rdev
>>8)!=SCSI_GENERIC_MAJOR
){
531 idmessage(messagedest
,messages
,"\t\t%s is not a generic SCSI device",
537 if(specialized_device
){
538 if(stat(specialized_device
,&i_st
)){
539 idperror(messagedest
,messages
,"\t\tCould not access device %s",
545 /* we need to resolve any symlinks for the lookup code to work */
548 generic_device
=test_resolve_symlink(generic_device
,messagedest
,messages
);
549 if(generic_device
==NULL
)goto cdda_identify_scsi_fail
;
551 if(specialized_device
){
552 specialized_device
=test_resolve_symlink(specialized_device
,messagedest
,messages
);
553 if(specialized_device
==NULL
)goto cdda_identify_scsi_fail
;
556 /* sgio is always preferred if it's there, unless user has forced the generic scsi device name */
558 if(check_sgio(specialized_device
,messagedest
,messages
)){
559 idmessage(messagedest
,messages
,"\t\tSG_IO device: %s",specialized_device
);
561 idmessage(messagedest
,messages
,"\t\tno SG_IO support for device: %s",specialized_device
);
568 /* was a generic device passed in as the specialized device name? */
569 if(specialized_device
){
570 if((int)(i_st
.st_rdev
>>8)==SCSI_GENERIC_MAJOR
){
571 char *temp
=(char *)generic_device
;
572 generic_device
=specialized_device
;
573 specialized_device
=temp
;
576 if(!generic_device
|| !specialized_device
){
579 scsi_match(generic_device
,scsi_cdrom_prefixes
,
580 devfs_scsi_test
,devfs_scsi_cd
,
581 "\t\tNo cdrom device found to match generic device %s",
582 messagedest
,messages
);
585 scsi_match(specialized_device
,scsi_generic_prefixes
,
586 devfs_scsi_test
,devfs_scsi_generic
,
587 "\t\tNo generic SCSI device found to match CDROM device %s",
588 messagedest
,messages
);
590 goto cdda_identify_scsi_fail
;
595 idmessage(messagedest
,messages
,"\t\tgeneric device: %s",generic_device
);
596 idmessage(messagedest
,messages
,"\t\tioctl device: %s",(specialized_device
?
599 if(specialized_device
){
600 if(stat(specialized_device
,&i_st
)){
601 idperror(messagedest
,messages
,"\t\tCould not access cdrom device "
602 "%s",specialized_device
);
603 goto cdda_identify_scsi_fail
;
607 if(stat(generic_device
,&g_st
)){
608 idperror(messagedest
,messages
,"\t\tCould not access generic SCSI device "
609 "%s",generic_device
);
611 goto cdda_identify_scsi_fail
;
615 if(specialized_device
) {
617 i_fd
=open(specialized_device
,O_RDWR
|O_NONBLOCK
);
619 i_fd
=open(specialized_device
,O_RDONLY
|O_NONBLOCK
);
623 g_fd
=open(generic_device
,O_RDWR
);
626 if(specialized_device
&& i_fd
==-1){
627 idperror(messagedest
,messages
,"\t\tCould not open cdrom device "
628 "%s (continuing)",specialized_device
);
629 goto cdda_identify_scsi_fail
;
632 if(generic_device
&& g_fd
==-1){
633 idperror(messagedest
,messages
,"\t\tCould not open generic SCSI device "
634 "%s",generic_device
);
635 goto cdda_identify_scsi_fail
;
639 type
=(int)(i_st
.st_rdev
>>8);
642 if(type
==SCSI_CDROM_MAJOR
){
643 if (!S_ISBLK(i_st
.st_mode
)) {
644 idmessage(messagedest
,messages
,"\t\tSCSI CDROM device %s not a "
645 "block device",specialized_device
);
646 goto cdda_identify_scsi_fail
;
649 idmessage(messagedest
,messages
,"\t\tSCSI CDROM device %s has wrong "
650 "major number",specialized_device
);
651 goto cdda_identify_scsi_fail
;
657 if((int)(g_st
.st_rdev
>>8)==SCSI_GENERIC_MAJOR
){
658 if (!S_ISCHR(g_st
.st_mode
)) {
659 idmessage(messagedest
,messages
,"\t\tGeneric SCSI device %s not a "
660 "char device",generic_device
);
661 goto cdda_identify_scsi_fail
;
664 idmessage(messagedest
,messages
,"\t\tGeneric SCSI device %s has wrong "
665 "major number",generic_device
);
666 goto cdda_identify_scsi_fail
;
670 d
=calloc(1,sizeof(cdrom_drive
));
674 d
->bigendianp
=-1; /* We don't know yet... */
676 d
->messagedest
= messagedest
;
677 d
->private=calloc(1,sizeof(*d
->private));
681 d
->private->clock
=(clock_gettime(CLOCK_MONOTONIC
,&tv
)<0?CLOCK_REALTIME
:CLOCK_MONOTONIC
);
684 d
->interface
=SGIO_SCSI
;
685 d
->private->sg_buffer
=(unsigned char *)(d
->private->sg_hd
=malloc(MAX_BIG_BUFF_SIZE
));
686 g_fd
=d
->cdda_fd
=dup(d
->ioctl_fd
);
688 version
=verify_SG_version(d
,messagedest
,messages
);
690 case -1:case 0:case 1:
691 d
->interface
=GENERIC_SCSI
;
692 goto cdda_identify_scsi_fail
;
694 d
->interface
=GENERIC_SCSI
;
698 /* malloc our big buffer for scsi commands */
699 d
->private->sg_hd
=malloc(MAX_BIG_BUFF_SIZE
);
700 d
->private->sg_buffer
=((unsigned char *)d
->private->sg_hd
)+SG_OFF
;
706 if(get_scsi_id(i_fd
,&lun
))
707 d
->lun
=0; /* a reasonable guess on a failed ioctl */
712 p
= (char *)scsi_inquiry(d
);
716 /* 2.6 kernels have a bug where the block layer implements
717 SG_DXFER_TO_FROM_DEVICE as a write operation instead of read.
718 I've submitted a kernel patch, but it will take a while to
719 propogate. Work around it for now. --Monty */
721 if(d
->interface
== SGIO_SCSI
){
722 /* test the inquiry with the workaround */
723 d
->interface
= SGIO_SCSI_BUGGY1
;
724 p
= (char *)scsi_inquiry(d
);
727 idmessage(messagedest
,messages
,
728 "\t\tThis kernel's block layer has a buggy SG_DXFER_TO_FROM_DEVICE;\n\t\t activating workaround.\n",NULL
);
730 /* Failed for some reason other than the buggy ioctl(); set the interface type back */
731 d
->interface
= SGIO_SCSI
;
737 idmessage(messagedest
,messages
,
738 "\t\tInquiry command failed; unable to probe drive\n",NULL
);
739 goto cdda_identify_scsi_fail
;
742 /* It would seem some TOSHIBA CDROMs gets things wrong */
744 !strncmp (p
+ 8, "TOSHIBA", 7) &&
745 !strncmp (p
+ 16, "CD-ROM", 6) &&
748 p
[1] |= 0x80; /* removable */
751 if (*p
!= TYPE_ROM
&& *p
!= TYPE_WORM
) {
752 idmessage(messagedest
,messages
,
753 "\t\tDrive is neither a CDROM nor a WORM device\n",NULL
);
754 goto cdda_identify_scsi_fail
;
757 d
->drive_model
=calloc(36,1);
758 memcpy(d
->inqbytes
,p
,4);
759 d
->cdda_device_name
=copystring(generic_device
);
760 d
->ioctl_device_name
=copystring(specialized_device
);
761 d
->drive_model
=calloc(36,1);
762 strscat(d
->drive_model
,p
+8,8);
763 strscat(d
->drive_model
,p
+16,16);
764 strscat(d
->drive_model
,p
+32,4);
766 idmessage(messagedest
,messages
,"\nCDROM model sensed sensed: %s",d
->drive_model
);
769 cdda_identify_scsi_fail
:
770 if(generic_device
)free((char *)generic_device
);
771 if(specialized_device
)free((char *)specialized_device
);
772 if(i_fd
!=-1)close(i_fd
);
773 if(g_fd
!=-1)close(g_fd
);
776 if(d
->private->sg_hd
)free(d
->private->sg_hd
);
786 cdrom_drive
*cdda_identify_test(const char *filename
, int messagedest
,
793 idmessage(messagedest
,messages
,"\tTesting %s for file/test interface",
796 if(stat(filename
,&st
)){
797 idperror(messagedest
,messages
,"\t\tCould not access file %s",
802 if(!S_ISREG(st
.st_mode
)){
803 idmessage(messagedest
,messages
,"\t\t%s is not a regular file",
808 fd
=open(filename
,O_RDONLY
);
810 idperror(messagedest
,messages
,"\t\tCould not open file %s",filename
);
814 d
=calloc(1,sizeof(cdrom_drive
));
816 d
->cdda_device_name
=copystring(filename
);
817 d
->ioctl_device_name
=copystring(filename
);
821 d
->interface
=TEST_INTERFACE
;
822 d
->bigendianp
=-1; /* We don't know yet... */
824 d
->private=calloc(1,sizeof(*d
->private));
825 d
->drive_model
=copystring("File based test interface");
826 idmessage(messagedest
,messages
,"\t\tCDROM sensed: %s\n",d
->drive_model
);