/****************************************************************** * CopyPolicy: GNU Lesser General Public License 2.1 applies * Original interface.c Copyright (C) 1994-1997 * Eissfeldt heiko@colossus.escape.de * Current incarnation Copyright (C) 1998-2008 Monty xiphmont@mit.edu * * Generic SCSI interface specific code. * ******************************************************************/ #include "low_interface.h" #include "common_interface.h" #include "utils.h" #include static int timed_ioctl(cdrom_drive *d, int fd, int command, void *arg){ struct timespec tv1; struct timespec tv2; int ret1=clock_gettime(d->private_data->clock,&tv1); int ret2=ioctl(fd, command,arg); int ret3=clock_gettime(d->private_data->clock,&tv2); if(ret1<0 || ret3<0){ d->private_data->last_milliseconds=-1; }else{ d->private_data->last_milliseconds = (tv2.tv_sec-tv1.tv_sec)*1000. + (tv2.tv_nsec-tv1.tv_nsec)/1000000.; } return ret2; } /* hook */ static int Dummy (cdrom_drive *d,int s){ return(0); } #include "drive_exceptions.h" static void tweak_SG_buffer(cdrom_drive *d) { int table, reserved, cur, err; char buffer[256]; /* SG_SET_RESERVED_SIZE doesn't actually allocate or reserve anything. * what it _does_ do is give you an error if you ask for a value * larger than q->max_sectors (the length of the device's bio request * queue). So we walk it up from 1 sector until it fails, then get * the value we set it to last. */ /* start with 2 frames, round down to our queue's sector size */ cur = 1; do { cur <<= 1; reserved = cur * (1<<9); err = ioctl(d->cdda_fd, SG_SET_RESERVED_SIZE, &reserved); } while(err >= 0 && (cur*(1<<9) < 0x40000000)); ioctl(d->cdda_fd, SG_GET_RESERVED_SIZE, &reserved); /* this doesn't currently ever work, but someday somebody might implement working sg lists with SG_IO devices, so who knows... */ if (ioctl(d->cdda_fd, SG_GET_SG_TABLESIZE, &table) < 0) table=1; sprintf(buffer,"\tDMA scatter/gather table entries: %d\n\t" "table entry size: %d bytes\n\t" "maximum theoretical transfer: %d sectors\n", table, reserved, table*(reserved/CD_FRAMESIZE_RAW)); cdmessage(d,buffer); cur=reserved; /* only use one entry for now */ /* so since we never go above q->max_sectors, we should never get -EIO. * we might still get -ENOMEM, but we back off for that later. Monty * had an old comment: "not too much; new kernels have trouble with DMA * allocation, so be more conservative: 32kB max until I test more * thoroughly". We're not currently honoring that, because we should * always get -ENOMEM. * * Updated: but we don't always get -ENOMEM. Sometimes USB drives * still fail the wrong way. This needs some kernel-land investigation. */ /* Bumping to 64kB transfer max --Monty */ if (!getenv("CDDA_IGNORE_BUFSIZE_LIMIT")) { cur=(cur>1024*64?1024*64:cur); }else{ cdmessage(d,"\tEnvironment variable CDDA_IGNORE_BUFSIZE_LIMIT set,\n" "\t\tforcing maximum possible sector size. This can break\n" "\t\tspectacularly; use with caution!\n"); } d->nsectors=cur/CD_FRAMESIZE_RAW; d->bigbuff=cur; sprintf(buffer,"\tSetting default read size to %d sectors (%d bytes).\n\n", d->nsectors,d->nsectors*CD_FRAMESIZE_RAW); if(cur==0) exit(1); cdmessage(d,buffer); } static void clear_garbage(cdrom_drive *d){ fd_set fdset; struct timeval tv; struct sg_header *sg_hd=d->private_data->sg_hd; int flag=0; /* clear out any possibly preexisting garbage */ FD_ZERO(&fdset); FD_SET(d->cdda_fd,&fdset); tv.tv_sec=0; tv.tv_usec=0; /* I like select */ while(select(d->cdda_fd+1,&fdset,NULL,NULL,&tv)==1){ sg_hd->twelve_byte = 0; sg_hd->result = 0; sg_hd->reply_len = SG_OFF; read(d->cdda_fd, sg_hd, 1); /* reset for select */ FD_ZERO(&fdset); FD_SET(d->cdda_fd,&fdset); tv.tv_sec=0; tv.tv_usec=0; if(!flag && d->report_all) cdmessage(d,"Clearing previously returned data from SCSI buffer\n"); flag=1; } } static int check_sbp_error(const unsigned char status, const unsigned char *sbp) { char key = sbp[2] & 0xf; char ASC = sbp[12]; char ASCQ = sbp[13]; if(status==0)return 0; if(status==8)return TR_BUSY; if (sbp[0]) { switch (key){ case 0: if (errno==0) errno = EIO; return(TR_UNKNOWN); case 1: break; case 2: errno = ENOMEDIUM; return(TR_NOTREADY); case 3: if ((ASC==0x0C) & (ASCQ==0x09)) { /* loss of streaming */ if (errno==0) errno = EIO; return(TR_STREAMING); } else { if (errno==0) errno = EIO; return(TR_MEDIUM); } case 4: if (errno==0) errno = EIO; return(TR_FAULT); case 5: if (errno==0) errno = EINVAL; return(TR_ILLEGAL); default: if (errno==0) errno = EIO; return(TR_UNKNOWN); } } return 0; } /* process a complete scsi command. */ static int sg2_handle_scsi_cmd(cdrom_drive *d, unsigned char *cmd, unsigned int cmd_len, unsigned int in_size, unsigned int out_size, unsigned char bytefill, int bytecheck, unsigned char *sense_buffer){ struct timespec tv1; struct timespec tv2; int tret1,tret2; int status = 0; struct sg_header *sg_hd=d->private_data->sg_hd; long writebytes=SG_OFF+cmd_len+in_size; /* generic scsi device services */ /* clear out any possibly preexisting garbage */ clear_garbage(d); memset(sg_hd,0,sizeof(sg_hd)); memset(sense_buffer,0,SG_MAX_SENSE); memcpy(d->private_data->sg_buffer,cmd,cmd_len+in_size); sg_hd->twelve_byte = cmd_len == 12; sg_hd->result = 0; sg_hd->reply_len = SG_OFF + out_size; /* The following is one of the scariest hacks I've ever had to use. The idea is this: We want to know if a command fails. The generic scsi driver (as of now) won't tell us; it hands back the uninitialized contents of the preallocated kernel buffer. We force this buffer to a known value via another bug (nonzero data length for a command that doesn't take data) such that we can tell if the command failed. Scared yet? */ if(bytecheck && out_size>in_size){ memset(d->private_data->sg_buffer+cmd_len+in_size,bytefill,out_size-in_size); /* the size does not remove cmd_len due to the way the kernel driver copies buffers */ writebytes+=(out_size-in_size); } { /* Select on write with a 5 second timeout. This is a hack until a better error reporting layer is in place; right now, always print a message. */ fd_set fdset; struct timeval tv; FD_ZERO(&fdset); FD_SET(d->cdda_fd,&fdset); tv.tv_sec=60; /* Increased to 1m for plextor, as the drive will try to get through rough spots on its own and this can take time 19991129 */ tv.tv_usec=0; while(1){ int ret=select(d->cdda_fd+1,NULL,&fdset,NULL,&tv); if(ret>0)break; if(ret<0 && errno!=EINTR)break; if(ret==0){ fprintf(stderr,"\nSCSI transport error: timeout waiting to write" " packet\n\n"); return(TR_EWRITE); } } } sigprocmask (SIG_BLOCK, &(d->sigset), NULL ); tret1=clock_gettime(d->private_data->clock,&tv1); errno=0; status = write(d->cdda_fd, sg_hd, writebytes ); if (status<0 || status != writebytes ) { sigprocmask ( SIG_UNBLOCK, &(d->sigset), NULL ); if(errno==0)errno=EIO; return(TR_EWRITE); } { /* Select on read (and write; this signals an error) with a 5 second timeout. This is a hack until a better error reporting layer is in place; right now, always print a message. */ fd_set rset; struct timeval tv; FD_ZERO(&rset); FD_SET(d->cdda_fd,&rset); tv.tv_sec=60; /* Increased to 1m for plextor, as the drive will try to get through rough spots on its own and this can take time 19991129 */ tv.tv_usec=0; while(1){ int ret=select(d->cdda_fd+1,&rset,NULL,NULL,&tv); if(ret<0 && errno!=EINTR)break; if(ret==0){ sigprocmask ( SIG_UNBLOCK, &(d->sigset), NULL ); fprintf(stderr,"\nSCSI transport error: timeout waiting to read" " packet\n\n"); return(TR_EREAD); } if(ret>0){ /* is it readable or something else? */ if(FD_ISSET(d->cdda_fd,&rset))break; sigprocmask ( SIG_UNBLOCK, &(d->sigset), NULL ); fprintf(stderr,"\nSCSI transport: error reading packet\n\n"); return(TR_EREAD); } } } tret2=clock_gettime(d->private_data->clock,&tv2); errno=0; status = read(d->cdda_fd, sg_hd, SG_OFF + out_size); sigprocmask ( SIG_UNBLOCK, &(d->sigset), NULL ); memcpy(sense_buffer,sg_hd->sense_buffer,SG_MAX_SENSE); if (status<0)return status; if(status != SG_OFF + out_size || sg_hd->result){ if(errno==0)errno=EIO; return(TR_EREAD); } status = check_sbp_error(sg_hd->target_status, sense_buffer); if(status)return status; /* Failed/Partial DMA transfers occasionally get through. Why? No clue, but it's been demonstrated in practice that we occasionally get fewer bytes than we request with no indication anything went wrong. */ if(bytecheck && in_size+cmd_lenprivate_data->sg_buffer[i]!=bytefill){ flag=1; break; } if(!flag){ errno=EINVAL; return(TR_ILLEGAL); } } errno=0; if(tret1<0 || tret2<0){ d->private_data->last_milliseconds=-1; }else{ d->private_data->last_milliseconds = (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_nsec-tv1.tv_nsec)/1000000; } return(0); } static int sgio_handle_scsi_cmd(cdrom_drive *d, unsigned char *cmd, unsigned int cmd_len, unsigned int in_size, unsigned int out_size, unsigned char bytefill, int bytecheck, unsigned char *sense){ int status = 0; struct sg_io_hdr hdr; memset(&hdr,0,sizeof(hdr)); memset(sense,0,sizeof(sense)); memcpy(d->private_data->sg_buffer,cmd+cmd_len,in_size); hdr.cmdp = cmd; hdr.cmd_len = cmd_len; hdr.sbp = sense; hdr.mx_sb_len = SG_MAX_SENSE; hdr.timeout = 50000; hdr.interface_id = 'S'; hdr.dxferp = d->private_data->sg_buffer; hdr.flags = SG_FLAG_DIRECT_IO; /* direct IO if we can get it */ /* scary buffer fill hack */ if(bytecheck && out_size>in_size) memset(hdr.dxferp+in_size,bytefill,out_size-in_size); if (in_size) { hdr.dxfer_len = in_size; hdr.dxfer_direction = SG_DXFER_TO_DEV; errno = 0; status = ioctl(d->ioctl_fd, SG_IO, &hdr); if (status >= 0 && hdr.status){ status = check_sbp_error(hdr.status,hdr.sbp); if(status) return status; } if (status < 0) return TR_EWRITE; } if (!in_size | out_size) { hdr.dxfer_len = out_size; if(bytecheck && d->interface != SGIO_SCSI_BUGGY1) hdr.dxfer_direction = out_size ? SG_DXFER_TO_FROM_DEV : SG_DXFER_NONE; else hdr.dxfer_direction = out_size ? SG_DXFER_FROM_DEV : SG_DXFER_NONE; errno = 0; status = timed_ioctl(d,d->ioctl_fd, SG_IO, &hdr); if (status >= 0 && hdr.status){ status = check_sbp_error(hdr.status,hdr.sbp); if(status) return status; } if (status < 0) return status; } /* Failed/Partial DMA transfers occasionally get through. Why? No clue, but it's been demonstrated in practice that we occasionally get fewer bytes than we request with no indication anything went wrong. */ if(bytecheck && in_sizeprivate_data->sg_buffer[i]!=bytefill){ flag=1; break; } if(!flag){ errno=EINVAL; return(TR_ILLEGAL); } } /* Can't rely on .duration because we can't be certain kernel has HZ set to something useful */ /* d->private_data->last_milliseconds = hdr.duration; */ errno = 0; return 0; } static int handle_scsi_cmd(cdrom_drive *d, unsigned char *cmd, unsigned int cmd_len, unsigned int in_size, unsigned int out_size, unsigned char bytefill, int bytecheck, unsigned char *sense){ if(d->interface == SGIO_SCSI || d->interface == SGIO_SCSI_BUGGY1) return sgio_handle_scsi_cmd(d,cmd,cmd_len,in_size,out_size,bytefill,bytecheck,sense); return sg2_handle_scsi_cmd(d,cmd,cmd_len,in_size,out_size,bytefill,bytecheck,sense); } static int test_unit_ready(cdrom_drive *d){ unsigned char sense[SG_MAX_SENSE]; unsigned char key, ASC, ASCQ; unsigned char cmd[6] = { 0x00, /* TEST_UNIT_READY */ 0, /* reserved */ 0, /* reserved */ 0, /* reserved */ 0, /* reserved */ 0};/* control */ handle_scsi_cmd(d, cmd, 6, 0, 56, 0,0, sense); key = d->private_data->sg_buffer[2] & 0xf; ASC = d->private_data->sg_buffer[12]; ASCQ = d->private_data->sg_buffer[13]; if(key == 2 && ASC == 4 && ASCQ == 1) return 0; return 1; } static void reset_scsi(cdrom_drive *d){ int arg,tries=0; d->enable_cdda(d,0); cdmessage(d,"sending SG SCSI reset... "); if(ioctl(d->cdda_fd,SG_SCSI_RESET,&arg)) cdmessage(d,"FAILED: EBUSY\n"); else cdmessage(d,"OK\n"); while(1) { if(test_unit_ready(d))break; tries++; usleep(10); } d->enable_cdda(d,1); } static int mode_sense_atapi(cdrom_drive *d,int size,int page){ unsigned char sense[SG_MAX_SENSE]; unsigned char cmd[10]= {0x5A, /* MODE_SENSE */ 0x00, /* reserved */ 0x00, /* page */ 0, /* reserved */ 0, /* reserved */ 0, /* reserved */ 0, /* reserved */ 0, /* MSB (0) */ 0, /* sizeof(modesense - SG_OFF) */ 0}; /* reserved */ cmd[1]=d->lun<<5; cmd[2]=0x3F&page; cmd[8]=size+4; if (handle_scsi_cmd (d, cmd, 10, 0, size+4,'\377',1,sense)) return(1); { unsigned char *b=d->private_data->sg_buffer; if(b[0])return(1); /* Handles only up to 256 bytes */ if(b[6])return(1); /* Handles only up to 256 bytes */ b[0]=b[1]-3; b[1]=b[2]; b[2]=b[3]; b[3]=b[7]; memmove(b+4,b+8,size-4); } return(0); } /* group 0 (6b) command */ static int mode_sense_scsi(cdrom_drive *d,int size,int page){ unsigned char sense[SG_MAX_SENSE]; unsigned char cmd[6]={0x1A, /* MODE_SENSE */ 0x00, /* return block descriptor/lun */ 0x00, /* page */ 0, /* reserved */ 0, /* sizeof(modesense - SG_OFF) */ 0}; /* control */ cmd[1]=d->lun<<5; cmd[2]=(0x3F&page); cmd[4]=size; if (handle_scsi_cmd (d, cmd, 6, 0, size, '\377',1, sense)) return(1); /* dump it all... */ return(0); } static int mode_sense(cdrom_drive *d,int size,int page){ if(d->is_atapi) return(mode_sense_atapi(d,size,page)); return(mode_sense_scsi(d,size,page)); } /* Current SG/SGIO impleenmtations specifically disallow mode set unless running as root (or setuid). One can see why (could be disastrous on, eg, a SCSI disk), but it curtails what we can do with older SCSI cdroms. */ static int mode_select(cdrom_drive *d,int density,int secsize){ /* short circut the way Heiko does it; less flexible, but shorter */ unsigned char sense[SG_MAX_SENSE]; if(d->is_atapi){ unsigned char cmd[26] = { 0x55, /* MODE_SELECT */ 0x10, /* no save page */ 0, /* reserved */ 0, /* reserved */ 0, /* reserved */ 0, /* reserved */ 0, /* reserved */ 0, /* reserved */ 16, /* sizeof(mode) */ 0, /* reserved */ /* mode parameter header */ 0, 0, 0, 0, 0, 0, 0, 8, /* Block Descriptor Length */ /* descriptor block */ 0, /* Density Code */ 0, 0, 0, /* # of Blocks */ 0, /* reserved */ 0, 0, 0};/* Blocklen */ unsigned char *mode = cmd + 18; /* prepare to read cds in the previous mode */ mode [0] = density; mode [6] = secsize >> 8; /* block length "msb" */ mode [7] = secsize & 0xFF; /* block length lsb */ /* do the scsi cmd */ return(handle_scsi_cmd (d,cmd,10, 16, 0,0,0,sense)); }else{ unsigned char cmd[18] = { 0x15, /* MODE_SELECT */ 0x10, /* no save page */ 0, /* reserved */ 0, /* reserved */ 12, /* sizeof(mode) */ 0, /* reserved */ /* mode section */ 0, 0, 0, 8, /* Block Descriptor Length */ 0, /* Density Code */ 0, 0, 0, /* # of Blocks */ 0, /* reserved */ 0, 0, 0};/* Blocklen */ unsigned char *mode = cmd + 10; /* prepare to read cds in the previous mode */ mode [0] = density; mode [6] = secsize >> 8; /* block length "msb" */ mode [7] = secsize & 0xFF; /* block length lsb */ /* do the scsi cmd */ return(handle_scsi_cmd (d,cmd,6, 12, 0,0,0,sense)); } } /* get current sector size from SCSI cdrom drive */ static unsigned int get_orig_sectorsize(cdrom_drive *d){ if(mode_sense(d,12,0x01))return(-1); d->orgdens = d->private_data->sg_buffer[4]; return(d->orgsize = ((int)(d->private_data->sg_buffer[10])<<8)+d->private_data->sg_buffer[11]); } /* switch CDROM scsi drives to given sector size */ static int set_sectorsize (cdrom_drive *d,unsigned int secsize){ return(mode_select(d,d->orgdens,secsize)); } /* switch old Toshiba/DEC and HP drives from/to cdda density */ int scsi_enable_cdda (cdrom_drive *d, int fAudioMode){ if (fAudioMode) { if(mode_select(d,d->density,CD_FRAMESIZE_RAW)){ if(d->error_retry) cderror(d,"001: Unable to set CDROM to read audio mode\n"); return(-1); } } else { if(mode_select(d,d->orgdens,d->orgsize)){ if(d->error_retry) cderror(d,"001: Unable to set CDROM to read audio mode\n"); return(-1); } } return(0); } typedef struct scsi_TOC { /* structure of scsi table of contents (cdrom) */ unsigned char reserved1; unsigned char bFlags; unsigned char bTrack; unsigned char reserved2; signed char start_MSB; unsigned char start_1; unsigned char start_2; unsigned char start_LSB; } scsi_TOC; /* read the table of contents from the cd and fill the TOC array */ /* Do it like the kernel ioctl driver; the 'all at once' approach fails on at least one Kodak drive. */ static int scsi_read_toc (cdrom_drive *d){ int i,first,last; unsigned tracks; /* READTOC, MSF format flag, res, res, res, res, Start track, len msb, len lsb, flags */ /* read the header first */ unsigned char sense[SG_MAX_SENSE]; unsigned char cmd[10] = { 0x43, 0, 0, 0, 0, 0, 1, 0, 12, 0}; cmd[1]=d->lun<<5; if (handle_scsi_cmd (d,cmd,10, 0, 12,'\377',1,sense)){ cderror(d,"004: Unable to read table of contents header\n"); return(-4); } first=d->private_data->sg_buffer[2]; last=d->private_data->sg_buffer[3]; tracks=last-first+1; if (last > MAXTRK || first > MAXTRK || last<0 || first<0) { cderror(d,"003: CDROM reporting illegal number of tracks\n"); return(-3); } for (i = first; i <= last; i++){ memcpy(cmd, (char []){ 0x43, 0, 0, 0, 0, 0, 0, 0, 12, 0}, 10); cmd[1]=d->lun<<5; cmd[6]=i; if (handle_scsi_cmd (d,cmd, 10, 0, 12,'\377',1,sense)){ cderror(d,"005: Unable to read table of contents entry\n"); return(-5); } { scsi_TOC *toc=(scsi_TOC *)(d->private_data->sg_buffer+4); d->disc_toc[i-first].bFlags=toc->bFlags; d->disc_toc[i-first].bTrack=i; d->disc_toc[i-first].dwStartSector= d->adjust_ssize * (((int)(toc->start_MSB)<<24) | (toc->start_1<<16)| (toc->start_2<<8)| (toc->start_LSB)); } } memcpy(cmd, (char []){ 0x43, 0, 0, 0, 0, 0, 0, 0, 12, 0}, 10); cmd[1]=d->lun<<5; cmd[6]=0xAA; if (handle_scsi_cmd (d,cmd,10, 0, 12,'\377',1,sense)){ cderror(d,"002: Unable to read table of contents lead-out\n"); return(-2); } { scsi_TOC *toc=(scsi_TOC *)(d->private_data->sg_buffer+4); d->disc_toc[i-first].bFlags=toc->bFlags; d->disc_toc[i-first].bTrack=0xAA; d->disc_toc[i-first].dwStartSector= d->adjust_ssize * (((int)(toc->start_MSB)<<24) | (toc->start_1<<16)| (toc->start_2<<8)| (toc->start_LSB)); } d->cd_extra = FixupTOC(d,tracks+1); /* include lead-out */ return(tracks); } /* a contribution from Boris for IMS cdd 522 */ /* check this for ACER/Creative/Foo 525,620E,622E, etc? */ static int scsi_read_toc2 (cdrom_drive *d){ u_int32_t foo,bar; int i; unsigned tracks; unsigned char cmd[10] = { 0xe5, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense[SG_MAX_SENSE]; cmd[5]=1; cmd[8]=255; if (handle_scsi_cmd (d,cmd,10, 0, 256,'\377',1,sense)){ cderror(d,"004: Unable to read table of contents header\n"); return(-4); } /* copy to our structure and convert start sector */ tracks = d->private_data->sg_buffer[1]; if (tracks > MAXTRK) { cderror(d,"003: CDROM reporting illegal number of tracks\n"); return(-3); } for (i = 0; i < tracks; i++){ memcpy(cmd, (char[]){ 0xe5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10); cmd[5]=i+1; cmd[8]=255; if (handle_scsi_cmd (d,cmd,10, 0, 256,'\377',1,sense)){ cderror(d,"005: Unable to read table of contents entry\n"); return(-5); } d->disc_toc[i].bFlags = d->private_data->sg_buffer[10]; d->disc_toc[i].bTrack = i + 1; d->disc_toc[i].dwStartSector= d->adjust_ssize * (((signed char)(d->private_data->sg_buffer[2])<<24) | (d->private_data->sg_buffer[3]<<16)| (d->private_data->sg_buffer[4]<<8)| (d->private_data->sg_buffer[5])); } d->disc_toc[i].bFlags = 0; d->disc_toc[i].bTrack = i + 1; memcpy (&foo, d->private_data->sg_buffer+2, 4); memcpy (&bar, d->private_data->sg_buffer+6, 4); d->disc_toc[i].dwStartSector = d->adjust_ssize * (be32_to_cpu(foo) + be32_to_cpu(bar)); d->disc_toc[i].dwStartSector= d->adjust_ssize * ((((signed char)(d->private_data->sg_buffer[2])<<24) | (d->private_data->sg_buffer[3]<<16)| (d->private_data->sg_buffer[4]<<8)| (d->private_data->sg_buffer[5]))+ ((((signed char)(d->private_data->sg_buffer[6])<<24) | (d->private_data->sg_buffer[7]<<16)| (d->private_data->sg_buffer[8]<<8)| (d->private_data->sg_buffer[9])))); d->cd_extra = FixupTOC(d,tracks+1); return(tracks); } static int scsi_set_speed (cdrom_drive *d, int speed){ unsigned char cmd[12]={0xBB, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0, 0, 0}; unsigned char sense[SG_MAX_SENSE]; if(speed>=0) speed=speed*44100*4/1024; else speed=-1; cmd[2] = (speed >> 8) & 0xFF; cmd[3] = (speed) & 0xFF; return handle_scsi_cmd(d,cmd,12,0,0,0,0,sense); } /* These do one 'extra' copy in the name of clean code */ static int i_read_28 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[10]={0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if(d->fua) cmd[1]=0x08; cmd[1]|=d->lun<<5; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[8] = sectors; if((ret=handle_scsi_cmd(d,cmd,10,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_A8 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if(d->fua) cmd[1]=0x08; cmd[1]|=d->lun<<5; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[9] = sectors; if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_D4_10 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[10]={0xd4, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if(d->fua) cmd[1]=0x08; cmd[1]|=d->lun<<5; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[8] = sectors; if((ret=handle_scsi_cmd(d,cmd,10,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_D4_12 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xd4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if(d->fua) cmd[1]=0x08; cmd[1]|=d->lun<<5; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[9] = sectors; if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_D5 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[10]={0xd5, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if(d->fua) cmd[1]=0x08; cmd[1]|=d->lun<<5; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[8] = sectors; if((ret=handle_scsi_cmd(d,cmd,10,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_D8 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xd8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if(d->fua) cmd[1]=0x08; cmd[1]|=d->lun<<5; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[9] = sectors; if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_mmc (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xbe, 0x2, 0, 0, 0, 0, 0, 0, 0, 0x10, 0, 0}; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[8] = sectors; if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_mmcB (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xbe, 0x0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0, 0}; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[8] = sectors; if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_mmc2 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xbe, 0x2, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0}; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[8] = sectors; if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_mmc2B (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xbe, 0x0, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0}; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[8] = sectors; if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_mmc3 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xbe, 0x6, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0}; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[8] = sectors; if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_mmc3B (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xbe, 0x4, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0}; cmd[3] = (begin >> 16) & 0xFF; cmd[4] = (begin >> 8) & 0xFF; cmd[5] = begin & 0xFF; cmd[8] = sectors; if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } /* straight from the MMC3 spec */ static inline void LBA_to_MSF(long lba, unsigned char *M, unsigned char *S, unsigned char *F){ if(lba>=-150){ *M=(lba+150)/(60*75); lba-=(*M)*60*75; *S=(lba+150)/75; lba-=(*S)*75; *F=(lba+150); }else{ *M=(lba+450150)/(60*75); lba-=(*M)*60*75; *S=(lba+450150)/75; lba-=(*S)*75; *F=(lba+450150); } } static int i_read_msf (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xb9, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0, 0}; LBA_to_MSF(begin,cmd+3,cmd+4,cmd+5); LBA_to_MSF(begin+sectors,cmd+6,cmd+7,cmd+8); if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_msf2 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xb9, 0, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0}; LBA_to_MSF(begin,cmd+3,cmd+4,cmd+5); LBA_to_MSF(begin+sectors,cmd+6,cmd+7,cmd+8); if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static int i_read_msf3 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){ int ret; unsigned char cmd[12]={0xb9, 4, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0}; LBA_to_MSF(begin,cmd+3,cmd+4,cmd+5); LBA_to_MSF(begin+sectors,cmd+6,cmd+7,cmd+8); if((ret=handle_scsi_cmd(d,cmd,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1,sense))) return(ret); if(p)memcpy(p,d->private_data->sg_buffer,sectors*CD_FRAMESIZE_RAW); return(0); } static long scsi_read_map (cdrom_drive *d, void *p, long begin, long sectors, int (*map)(cdrom_drive *, void *, long, long, unsigned char *)){ unsigned char sense[SG_MAX_SENSE]; int retry_count,err; char *buffer=(char *)p; /* read d->nsectors at a time, max. */ sectors=(sectors>d->nsectors?d->nsectors:sectors); sectors=(sectors<1?1:sectors); retry_count=0; while(1) { if((err=map(d,(p?buffer:NULL),begin,sectors,sense))){ if(d->report_all){ char b[256]; sprintf(b,"scsi_read error: sector=%ld length=%ld retry=%d\n", begin,sectors,retry_count); cdmessage(d,b); sprintf(b," Sense key: %x ASC: %x ASCQ: %x\n", (int)(sense[2]&0xf), (int)(sense[12]), (int)(sense[13])); cdmessage(d,b); sprintf(b," Transport error: %s\n",strerror_tr[err]); cdmessage(d,b); sprintf(b," System error: %s\n",strerror(errno)); cdmessage(d,b); fprintf(stderr,"scsi_read error: sector=%ld length=%ld retry=%d\n", begin,sectors,retry_count); fprintf(stderr," Sense key: %x ASC: %x ASCQ: %x\n", (int)(sense[2]&0xf), (int)(sense[12]), (int)(sense[13])); fprintf(stderr," Transport error: %s\n",strerror_tr[err]); fprintf(stderr," System error: %s\n",strerror(errno)); } switch(errno){ case EINTR: usleep(100); continue; case ENOMEM: /* D'oh. Possible kernel error. Keep limping */ usleep(100); if(sectors==1){ /* Nope, can't continue */ cderror(d,"300: Kernel memory error\n"); return(-300); } if(d->report_all){ char b[256]; sprintf(b,"scsi_read: kernel couldn't alloc %ld bytes. " "backing off...\n",sectors*CD_FRAMESIZE_RAW); cdmessage(d,b); } sectors--; continue; case ENOMEDIUM: cderror(d,"404: No medium present\n"); return(-404); default: if(sectors==1){ if(errno==EIO) if(d->fua==-1) /* testing for FUA support */ return(-7); /* *Could* be I/O or media error. I think. If we're at 30 retries, we better skip this unhappy little sector. */ if(retry_count>MAX_RETRIES-1){ char b[256]; sprintf(b,"010: Unable to access sector %ld\n", begin); cderror(d,b); return(-10); } break; } /* Hmm. OK, this is just a tad silly. just in case this was a timeout and a reset happened, we need to set the drive back to cdda */ reset_scsi(d); } if(!d->error_retry)return(-7); }else{ /* Did we get all the bytes we think we did, or did the kernel suck? */ if(buffer){ long i; for(i=sectors*CD_FRAMESIZE_RAW;i>1;i-=2) if(buffer[i-1]!='\177' || buffer[i-2]!='\177') break; i/=CD_FRAMESIZE_RAW; if(i!=sectors){ if(d->report_all){ char b[256]; sprintf(b,"scsi_read underrun: pos=%ld len=%ld read=%ld retry=%d\n", begin,sectors,i,retry_count); cdmessage(d,b); } reset_scsi(d); } if(i>0)return(i); }else break; } retry_count++; if(sectors==1 && retry_count>MAX_RETRIES){ cderror(d,"007: Unknown, unrecoverable error reading data\n"); return(-7); } if(sectors>1)sectors=sectors/2; d->enable_cdda(d,0); d->enable_cdda(d,1); } return(sectors); } long scsi_read_28 (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_28)); } long scsi_read_A8 (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_A8)); } long scsi_read_D4_10 (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_D4_10)); } long scsi_read_D4_12 (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_D4_12)); } long scsi_read_D5 (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_D5)); } long scsi_read_D8 (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_D8)); } long scsi_read_mmc (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_mmc)); } long scsi_read_mmc2 (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_mmc2)); } long scsi_read_mmc3 (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_mmc3)); } long scsi_read_mmcB (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_mmcB)); } long scsi_read_mmc2B (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_mmc2B)); } long scsi_read_mmc3B (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_mmc3B)); } long scsi_read_msf (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_msf)); } long scsi_read_msf2 (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_msf2)); } long scsi_read_msf3 (cdrom_drive *d, void *p, long begin, long sectors){ return(scsi_read_map(d,p,begin,sectors,i_read_msf3)); } /* Some drives, given an audio read command, return only 2048 bytes of data as opposed to 2352 bytes. Look for bytess at the end of the single sector verification read */ static int count_2352_bytes(cdrom_drive *d){ long i; for(i=2351;i>=0;i--) if(d->private_data->sg_buffer[i]!=(unsigned char)'\177') return(((i+3)>>2)<<2); return(0); } static int verify_nonzero(cdrom_drive *d){ long i,flag=0; for(i=0;i<2352;i++) if(d->private_data->sg_buffer[i]!=0){ flag=1; break; } return(flag); } /* So many different read commands, densities, features... Verify that our selected 'read' command actually reads nonzero data, else search through other possibilities */ static int verify_read_command(cdrom_drive *d){ int i,j,k; int audioflag=1; int (*enablecommand) (struct cdrom_drive *d, int speed); long (*readcommand) (struct cdrom_drive *d, void *p, long begin, long sectors); unsigned char density; int16_t *buff=malloc(CD_FRAMESIZE_RAW); cdmessage(d,"Verifying CDDA command set...\n"); /* try the expected command set; grab the center of each track, look for data */ if(d->enable_cdda(d,1)==0){ audioflag=0; for(i=1;i<=d->tracks;i++){ if(cdda_track_audiop(d,i)==1){ long firstsector=cdda_track_firstsector(d,i); long lastsector=cdda_track_lastsector(d,i); long sector=(firstsector+lastsector)>>1; audioflag=1; if(d->read_audio(d,buff,sector,1)>0){ if(count_2352_bytes(d)==2352){ cdmessage(d,"\tExpected command set reads OK.\n"); d->enable_cdda(d,0); free(buff); return(0); } } } } d->enable_cdda(d,0); } if(!audioflag){ cdmessage(d,"\tCould not find any audio tracks on this disk.\n"); return(-403); } { char *es="",*rs=""; d->bigendianp=-1; density=d->density; readcommand=d->read_audio; enablecommand=d->enable_cdda; /* No nonzeroes? D'oh. Exhaustive search */ cdmessage(d,"\tExpected command set FAILED!\n" "\tPerforming full probe for CDDA command set...\n"); /* loops: density/enable no, 0x0/org, 0x04/org, 0x82/org read command read_10 read_12 read_nec read_sony read_mmc read_mmc2 */ /* NEC test must come before sony; the nec drive expects d8 to be 10 bytes, and a 12 byte verson (Sony) crashes the drive */ for(i=0;i<5;i++){ switch(i){ case 0: d->density=0; d->enable_cdda=Dummy; es="none "; break; case 1: d->density=0; d->enable_cdda=scsi_enable_cdda; es="yes/0x00"; break; case 2: d->density=0x04; d->enable_cdda=scsi_enable_cdda; es="yes/0x04"; break; case 3: d->density=0x82; d->enable_cdda=scsi_enable_cdda; es="yes/0x82"; break; case 4: d->density=0x81; d->enable_cdda=scsi_enable_cdda; es="yes/0x81"; break; } for(j=0;j<15;j++){ switch(j){ case 0: d->read_audio=scsi_read_28; rs="28 0x,00"; break; case 1: d->read_audio=scsi_read_A8; rs="a8 0x,00"; break; /* 2 through 10 do not allow/require density */ case 2: d->read_audio=scsi_read_mmcB; rs="be 02,10"; if(i==0)break; case 3: j=3; d->read_audio=scsi_read_mmc2B; rs="be 02,f8"; if(i==0)break; case 4: j=4; d->read_audio=scsi_read_mmc3B; rs="be 06,f8"; if(i==0)break; case 5: j=5; d->read_audio=scsi_read_mmc; rs="be 00,10"; if(i==0)break; case 6: j=6; d->read_audio=scsi_read_mmc2; rs="be 00,f8"; if(i==0)break; case 7: j=7; d->read_audio=scsi_read_mmc3; rs="be 04,f8"; if(i==0)break; case 8: j=8; d->read_audio=scsi_read_msf; rs="b9 00,10"; if(i==0)break; case 9: j=9; d->read_audio=scsi_read_msf2; rs="b9 00,f8"; if(i==0)break; case 10: j=10; d->read_audio=scsi_read_msf3; rs="b9 04,f8"; if(i==0)break; case 11: d->read_audio=scsi_read_D4_10; rs="d4(10)0x"; break; case 12: d->read_audio=scsi_read_D4_12; rs="d4(12)0x"; break; case 13: d->read_audio=scsi_read_D5; rs="d5 0x,00"; break; case 14: d->read_audio=scsi_read_D8; rs="d8 0x,00"; break; } cdmessage(d,"\ttest -> density: ["); cdmessage(d,es); cdmessage(d,"] command: ["); cdmessage(d,rs); cdmessage(d,"]\n"); { int densityflag=0; int rejectflag=0; int zeroflag=0; int lengthflag=0; if(d->enable_cdda(d,1)==0){ for(k=1;k<=d->tracks;k++){ if(cdda_track_audiop(d,k)==1){ long firstsector=cdda_track_firstsector(d,k); long lastsector=cdda_track_lastsector(d,k); long sector=(firstsector+lastsector)>>1; if(d->read_audio(d,buff,sector,1)>0){ if((lengthflag=count_2352_bytes(d))==2352){ if(verify_nonzero(d)){ cdmessage(d,"\t\tCommand set FOUND!\n"); free(buff); d->enable_cdda(d,0); return(0); }else{ zeroflag++; } } }else{ rejectflag++; break; } } } d->enable_cdda(d,0); }else{ densityflag++; } if(densityflag) cdmessage(d,"\t\tDrive rejected density set\n"); if(rejectflag){ char buffer[256]; sprintf(buffer,"\t\tDrive rejected read command packet(s)\n"); cdmessage(d,buffer); } if(lengthflag>0 && lengthflag<2352){ char buffer[256]; sprintf(buffer,"\t\tDrive returned at least one packet, but with\n" "\t\tincorrect size (%d)\n",lengthflag); cdmessage(d,buffer); } if(zeroflag){ char buffer[256]; sprintf(buffer,"\t\tDrive returned %d packet(s), but contents\n" "\t\twere entirely zero\n",zeroflag); cdmessage(d,buffer); } } } } /* D'oh. */ d->density=density; d->read_audio=readcommand; d->enable_cdda=enablecommand; cdmessage(d,"\tUnable to find any suitable command set from probe;\n" "\tdrive probably not CDDA capable.\n"); cderror(d,"006: Could not read any data from drive\n"); } free(buff); return(-6); } static void check_cache(cdrom_drive *d){ long i; if(!(d->read_audio==scsi_read_mmc || d->read_audio==scsi_read_mmc2 || d->read_audio==scsi_read_mmc3 || d->read_audio==scsi_read_mmcB || d->read_audio==scsi_read_mmc2B || d->read_audio==scsi_read_mmc3B)){ cdmessage(d,"This command set may use a Force Unit Access bit."); cdmessage(d,"\nChecking drive for FUA bit support...\n"); d->enable_cdda(d,1); d->fua=1; for(i=1;i<=d->tracks;i++){ if(cdda_track_audiop(d,i)==1){ long firstsector=cdda_track_firstsector(d,i); long lastsector=cdda_track_lastsector(d,i); long sector=(firstsector+lastsector)>>1; if(d->read_audio(d,NULL,sector,1)>0){ cdmessage(d,"\tDrive accepted FUA bit.\n"); d->enable_cdda(d,0); return; } } } d->fua=0; cdmessage(d,"\tDrive rejected FUA bit.\n"); /* we only use the FUA bit as a possible extra layer of redundancy; too many drives accept it, but still don't force unit access. Still use the old cachebusting algo. */ return; } } static int check_atapi(cdrom_drive *d){ int atapiret=-1; int fd = d->cdda_fd; /* check the device we'll actually be using to read */ cdmessage(d,"\nChecking for SCSI emulation...\n"); if (ioctl(fd,SG_EMULATED_HOST,&atapiret)){ cderror(d,"\tSG_EMULATED_HOST ioctl() failed!\n"); return(-1); } else { if(atapiret==1){ if(d->interface == SGIO_SCSI){ cdmessage(d,"\tDrive is ATAPI (using SG_IO host adaptor emulation)\n"); }else if(d->interface == SGIO_SCSI_BUGGY1){ cdmessage(d,"\tDrive is ATAPI (using SG_IO host adaptor emulation with workarounds)\n"); }else{ cdmessage(d,"\tDrive is ATAPI (using SCSI host adaptor emulation)\n"); /* Disable kernel SCSI command translation layer for access through sg */ if (ioctl(fd,SG_SET_TRANSFORM,0)) cderror(d,"\tCouldn't disable kernel command translation layer\n"); } d->is_atapi=1; }else{ cdmessage(d,"\tDrive is SCSI\n"); d->is_atapi=0; } return(d->is_atapi); } } static int check_mmc(cdrom_drive *d){ unsigned char *b; cdmessage(d,"\nChecking for MMC style command set...\n"); d->is_mmc=0; if(mode_sense(d,22,0x2A)==0){ b=d->private_data->sg_buffer; b+=b[3]+4; if((b[0]&0x3F)==0x2A){ /* MMC style drive! */ d->is_mmc=1; if(b[1]>=4){ if(b[5]&0x1){ cdmessage(d,"\tDrive is MMC style\n"); return(1); }else{ cdmessage(d,"\tDrive is MMC, but reports CDDA incapable.\n"); cdmessage(d,"\tIt will likely not be able to read audio data.\n"); return(1); } } } } cdmessage(d,"\tDrive does not have MMC CDDA support\n"); return(0); } static void check_exceptions(cdrom_drive *d,exception *list){ int i=0; while(list[i].model){ if(!strncmp(list[i].model,d->drive_model,strlen(list[i].model))){ if(list[i].density)d->density=list[i].density; if(list[i].enable)d->enable_cdda=list[i].enable; if(list[i].read)d->read_audio=list[i].read; if(list[i].bigendianp!=-1)d->bigendianp=list[i].bigendianp; return; } i++; } } /* request vendor brand and model */ unsigned char *scsi_inquiry(cdrom_drive *d){ unsigned char sense[SG_MAX_SENSE]; unsigned char cmd[6]={ 0x12,0,0,0,56,0 }; if(handle_scsi_cmd(d,cmd,6, 0, 56,'\377',1,sense)) { cderror(d,"008: Unable to identify CDROM model\n"); return(NULL); } return (d->private_data->sg_buffer); } int scsi_init_drive(cdrom_drive *d){ int ret; check_atapi(d); check_mmc(d); /* generic Sony type defaults; specialize from here */ d->density = 0x0; d->enable_cdda = Dummy; d->read_audio = scsi_read_D8; d->fua=0x0; if(d->is_atapi)d->lun=0; /* it should already be; just to make sure */ if(d->is_mmc){ d->read_audio = scsi_read_mmc2B; d->bigendianp=0; check_exceptions(d,mmc_list); }else{ if(d->is_atapi){ /* Not MMC maybe still uses 0xbe */ d->read_audio = scsi_read_mmc2B; d->bigendianp=0; check_exceptions(d,atapi_list); }else{ check_exceptions(d,scsi_list); } } d->read_toc = (!memcmp(d->drive_model, "IMS", 3) && !d->is_atapi) ? scsi_read_toc2 : scsi_read_toc; d->set_speed = scsi_set_speed; if(!d->is_atapi){ unsigned sector_size= get_orig_sectorsize(d); if(sector_size<2048 && set_sectorsize(d,2048)) d->adjust_ssize = 2048 / sector_size; else d->adjust_ssize = 1; }else d->adjust_ssize = 1; d->tracks=d->read_toc(d); if(d->tracks<1) return(d->tracks); tweak_SG_buffer(d); d->opened=1; if((ret=verify_read_command(d)))return(ret); check_cache(d); d->error_retry=1; d->private_data->sg_hd=realloc(d->private_data->sg_hd,d->nsectors*CD_FRAMESIZE_RAW + SG_OFF + 128); d->private_data->sg_buffer=((unsigned char *)d->private_data->sg_hd)+SG_OFF; d->report_all=1; return(0); }