Blame lib/cdda_interface/cddap_interface.c

Packit cb6d3d
/*
Packit cb6d3d
  Copyright (C) 2004, 2005, 2007, 2008 Rocky Bernstein <rocky@gnu.org>
Packit cb6d3d
  Copyright (C) 2014 Robert Kausch <robert.kausch@freac.org>
Packit cb6d3d
  Original interface.c Copyright (C) 1994-1997
Packit cb6d3d
             Eissfeldt heiko@colossus.escape.de
Packit cb6d3d
  Current blenderization Copyright (C) 1998-1999 Monty xiphmont@mit.edu
Packit cb6d3d
  Copyright (C) 1998 Monty xiphmont@mit.edu
Packit cb6d3d
*/
Packit cb6d3d
/**
Packit cb6d3d
Packit cb6d3d
  CD-ROM code which interfaces between user-level visible CD paranoia
Packit cb6d3d
  routines and libddio routines. (There is some GNU/Linux-specific
Packit cb6d3d
  code here too that should probably be removed.
Packit cb6d3d
Packit cb6d3d
**/
Packit cb6d3d
Packit cb6d3d
#include "config.h"
Packit cb6d3d
#include "common_interface.h"
Packit cb6d3d
#include "low_interface.h"
Packit cb6d3d
#include "utils.h"
Packit cb6d3d
Packit cb6d3d
/** The below variables are trickery to force the above enum symbol
Packit cb6d3d
    values to be recorded in debug symbol tables. They are used to
Packit cb6d3d
    allow one to refer to the enumeration value names in the typedefs
Packit cb6d3d
    above in a debugger and debugger expressions
Packit cb6d3d
*/
Packit cb6d3d
Packit cb6d3d
paranoia_jitter_t     debug_paranoia_jitter;
Packit cb6d3d
paranoia_cdda_enums_t debug_paranoia_cdda_enums;
Packit cb6d3d
Packit cb6d3d
/*! reads TOC via libcdio and returns the number of tracks in the disc.
Packit cb6d3d
    0 is returned if there was an error.
Packit cb6d3d
*/
Packit cb6d3d
static int
Packit cb6d3d
cddap_readtoc (cdrom_drive_t *d)
Packit cb6d3d
{
Packit cb6d3d
  int i;
Packit cb6d3d
  track_t i_track;
Packit cb6d3d
Packit cb6d3d
  /* Save TOC Entries */
Packit cb6d3d
  d->tracks = cdio_get_num_tracks(d->p_cdio) ;
Packit cb6d3d
Packit cb6d3d
  if (CDIO_INVALID_TRACK == d->tracks) return 0;
Packit cb6d3d
Packit cb6d3d
  i_track   = cdio_get_first_track_num(d->p_cdio);
Packit cb6d3d
Packit cb6d3d
  for ( i=0; i < d->tracks; i++) {
Packit cb6d3d
    d->disc_toc[i].bTrack = i_track;
Packit cb6d3d
    d->disc_toc[i].dwStartSector = cdio_get_track_lsn(d->p_cdio, i_track);
Packit cb6d3d
    i_track++;
Packit cb6d3d
  }
Packit cb6d3d
Packit cb6d3d
  d->disc_toc[i].bTrack = i_track;
Packit cb6d3d
  d->disc_toc[i].dwStartSector = cdio_get_track_lsn(d->p_cdio,
Packit cb6d3d
						    CDIO_CDROM_LEADOUT_TRACK);
Packit cb6d3d
Packit cb6d3d
  d->cd_extra=FixupTOC(d, d->tracks+1); /* fixup includes lead-out */
Packit cb6d3d
  return --i_track;  /* number of tracks returned does not include lead-out */
Packit cb6d3d
}
Packit cb6d3d
Packit cb6d3d
Packit cb6d3d
/* Set operating speed */
Packit cb6d3d
static int
Packit cb6d3d
cddap_setspeed(cdrom_drive_t *d, int i_speed)
Packit cb6d3d
{
Packit cb6d3d
  return cdio_set_speed(d->p_cdio, i_speed);
Packit cb6d3d
}
Packit cb6d3d
Packit cb6d3d
/* read 'i_sector' adjacent audio sectors
Packit cb6d3d
 * into buffer '*p' beginning at sector 'begin'
Packit cb6d3d
 */
Packit cb6d3d
Packit cb6d3d
static long int
Packit cb6d3d
read_blocks (cdrom_drive_t *d, void *p, lsn_t begin, long i_sectors)
Packit cb6d3d
{
Packit cb6d3d
  int retry_count = 0;
Packit cb6d3d
  int err;
Packit cb6d3d
  int ret = 0;
Packit cb6d3d
  char *buffer=(char *)p;
Packit cb6d3d
Packit cb6d3d
  if(p==NULL)buffer = malloc(i_sectors*CD_FRAMESIZE_RAW);
Packit cb6d3d
Packit cb6d3d
  do {
Packit cb6d3d
    struct timespec tv1;
Packit cb6d3d
    struct timespec tv2;
Packit cb6d3d
    int ret1,ret2;
Packit cb6d3d
Packit cb6d3d
    ret1 = gettime(&tv1);
Packit cb6d3d
    err  = cdio_read_audio_sectors( d->p_cdio, buffer, begin, i_sectors);
Packit cb6d3d
    ret2 = gettime(&tv2);
Packit cb6d3d
Packit cb6d3d
    if(ret1<0 || ret2<0) {
Packit cb6d3d
      d->last_milliseconds=-1;
Packit cb6d3d
    } else {
Packit cb6d3d
      d->last_milliseconds = (tv2.tv_sec-tv1.tv_sec)*1000. + (tv2.tv_nsec-tv1.tv_nsec)/1000000.;
Packit cb6d3d
    }
Packit cb6d3d
Packit cb6d3d
    if ( DRIVER_OP_SUCCESS != err ) {
Packit cb6d3d
      if (!d->error_retry) {
Packit cb6d3d
	ret=-7;
Packit cb6d3d
	goto done;
Packit cb6d3d
      }
Packit cb6d3d
Packit cb6d3d
      if (i_sectors==1) {
Packit cb6d3d
	/* *Could* be I/O or media error.  I think.  If we're at
Packit cb6d3d
	   30 retries, we better skip this unhappy little
Packit cb6d3d
	   sector. */
Packit cb6d3d
	if (retry_count>MAX_RETRIES-1) {
Packit cb6d3d
	  char b[256];
Packit cb6d3d
	  snprintf(b, sizeof(b),
Packit cb6d3d
		   "010: Unable to access sector %ld: skipping...\n",
Packit cb6d3d
		   (long int) begin);
Packit cb6d3d
	  cderror(d, b);
Packit cb6d3d
	  ret=-10;
Packit cb6d3d
	  goto done;
Packit cb6d3d
	}
Packit cb6d3d
      }
Packit cb6d3d
Packit cb6d3d
      if(retry_count>4)
Packit cb6d3d
	if(i_sectors>1)
Packit cb6d3d
	  i_sectors=i_sectors*3/4;
Packit cb6d3d
      retry_count++;
Packit cb6d3d
      if (retry_count>MAX_RETRIES) {
Packit cb6d3d
	cderror(d,"007: Unknown, unrecoverable error reading data\n");
Packit cb6d3d
	ret=-7;
Packit cb6d3d
	goto done;
Packit cb6d3d
      }
Packit cb6d3d
    } else
Packit cb6d3d
      break;
Packit cb6d3d
  } while (err);
Packit cb6d3d
Packit cb6d3d
  ret=i_sectors;
Packit cb6d3d
Packit cb6d3d
 done:
Packit cb6d3d
  if(p==NULL && buffer)free(buffer);
Packit cb6d3d
  return ret;
Packit cb6d3d
}
Packit cb6d3d
Packit cb6d3d
typedef enum  {
Packit cb6d3d
  JITTER_NONE = 0,
Packit cb6d3d
  JITTER_SMALL= 1,
Packit cb6d3d
  JITTER_LARGE= 2,
Packit cb6d3d
  JITTER_MASSIVE=3
Packit cb6d3d
} jitter_baddness_t;
Packit cb6d3d
Packit cb6d3d
/* read 'i_sector' adjacent audio sectors
Packit cb6d3d
 * into buffer '*p' beginning at sector 'begin'
Packit cb6d3d
 */
Packit cb6d3d
Packit cb6d3d
static long int
Packit cb6d3d
jitter_read (cdrom_drive_t *d, void *p, lsn_t begin, long i_sectors,
Packit cb6d3d
	     jitter_baddness_t jitter_badness)
Packit cb6d3d
{
Packit cb6d3d
  static int i_jitter=0;
Packit cb6d3d
  int jitter_flag;
Packit cb6d3d
  long i_sectors_orig = i_sectors;
Packit cb6d3d
  long i_jitter_offset = 0;
Packit cb6d3d
Packit cb6d3d
  char *p_buf=malloc(CDIO_CD_FRAMESIZE_RAW*(i_sectors+1));
Packit cb6d3d
Packit cb6d3d
  if (d->i_test_flags & CDDA_TEST_ALWAYS_JITTER)
Packit cb6d3d
    jitter_flag = 1;
Packit cb6d3d
  else
Packit cb6d3d
#ifdef HAVE_DRAND48
Packit cb6d3d
    jitter_flag = (drand48() > .9) ? 1 : 0;
Packit cb6d3d
#else
Packit cb6d3d
    jitter_flag = (((float)rand()/RAND_MAX) > .9) ? 1 : 0;
Packit cb6d3d
#endif
Packit cb6d3d
Packit cb6d3d
  if (jitter_flag) {
Packit cb6d3d
    int i_coeff = 0;
Packit cb6d3d
    int i_jitter_sectors = 0;
Packit cb6d3d
    switch(jitter_badness) {
Packit cb6d3d
    case JITTER_SMALL  : i_coeff =   4; break;
Packit cb6d3d
    case JITTER_LARGE  : i_coeff =  32; break;
Packit cb6d3d
    case JITTER_MASSIVE: i_coeff = 128; break;
Packit cb6d3d
    case JITTER_NONE   :
Packit cb6d3d
    default            : ;
Packit cb6d3d
    }
Packit cb6d3d
#ifdef HAVE_DRAND48
Packit cb6d3d
    i_jitter = i_coeff * (int)((drand48()-.5)*CDIO_CD_FRAMESIZE_RAW/8);
Packit cb6d3d
#else
Packit cb6d3d
    i_jitter = i_coeff * (int)((((float)rand()/RAND_MAX)-.5)*CDIO_CD_FRAMESIZE_RAW/8);
Packit cb6d3d
#endif
Packit cb6d3d
Packit cb6d3d
    /* We may need to add another sector to compensate for the bytes that
Packit cb6d3d
       will be dropped off when jittering, and the begin location may
Packit cb6d3d
       be a little different.
Packit cb6d3d
    */
Packit cb6d3d
    i_jitter_sectors = i_jitter / CDIO_CD_FRAMESIZE_RAW;
Packit cb6d3d
Packit cb6d3d
    if (i_jitter >= 0)
Packit cb6d3d
      i_jitter_offset  = i_jitter % CDIO_CD_FRAMESIZE_RAW;
Packit cb6d3d
    else {
Packit cb6d3d
      i_jitter_offset  = CDIO_CD_FRAMESIZE_RAW -
Packit cb6d3d
	(-i_jitter % CDIO_CD_FRAMESIZE_RAW);
Packit cb6d3d
      i_jitter_sectors--;
Packit cb6d3d
    }
Packit cb6d3d
Packit cb6d3d
Packit cb6d3d
    if (begin + i_jitter_sectors > 0) {
Packit cb6d3d
#if TRACE_PARANOIA
Packit cb6d3d
      char buffer[256];
Packit cb6d3d
      sprintf(buffer, "jittering by %d, offset %ld\n", i_jitter,
Packit cb6d3d
	      i_jitter_offset);
Packit cb6d3d
      cdmessage(d,buffer);
Packit cb6d3d
#endif
Packit cb6d3d
Packit cb6d3d
      begin += i_jitter_sectors;
Packit cb6d3d
      i_sectors ++;
Packit cb6d3d
    } else
Packit cb6d3d
      i_jitter_offset = 0;
Packit cb6d3d
Packit cb6d3d
  }
Packit cb6d3d
Packit cb6d3d
  i_sectors = read_blocks(d, p_buf, begin, i_sectors);
Packit cb6d3d
Packit cb6d3d
  if (i_sectors < 0) return i_sectors;
Packit cb6d3d
Packit cb6d3d
  if (i_sectors < i_sectors_orig) {
Packit cb6d3d
    /* Had to reduce # of sectors due to read errors. So give full amount,
Packit cb6d3d
       with no jittering. */
Packit cb6d3d
    if (p) memcpy(p, p_buf, i_sectors*CDIO_CD_FRAMESIZE_RAW);
Packit cb6d3d
  } else {
Packit cb6d3d
    /* Got full amount, but now adjust size for jittering. */
Packit cb6d3d
    if (p) memcpy(p, p_buf+i_jitter_offset, i_sectors_orig*CDIO_CD_FRAMESIZE_RAW);
Packit cb6d3d
    i_sectors = i_sectors_orig;
Packit cb6d3d
  }
Packit cb6d3d
Packit cb6d3d
  free(p_buf);
Packit cb6d3d
  return(i_sectors);
Packit cb6d3d
}
Packit cb6d3d
Packit cb6d3d
/* read 'i_sector' adjacent audio sectors
Packit cb6d3d
 * into buffer '*p' beginning at sector 'begin'
Packit cb6d3d
 */
Packit cb6d3d
Packit cb6d3d
static long int
Packit cb6d3d
cddap_read (cdrom_drive_t *d, void *p, lsn_t begin, long i_sectors)
Packit cb6d3d
{
Packit cb6d3d
  jitter_baddness_t jitter_badness = d->i_test_flags & 0x3;
Packit cb6d3d
Packit cb6d3d
  /* read d->nsectors at a time, max. */
Packit cb6d3d
  i_sectors = ( i_sectors > d->nsectors && d->nsectors > 0 )
Packit cb6d3d
    ? d->nsectors : i_sectors;
Packit cb6d3d
Packit cb6d3d
  /* If we are testing under-run correction, we will deliberately set
Packit cb6d3d
     what we read a frame short.  */
Packit cb6d3d
  if (d->i_test_flags & CDDA_TEST_UNDERRUN )
Packit cb6d3d
    i_sectors--;
Packit cb6d3d
Packit cb6d3d
  if (jitter_badness) {
Packit cb6d3d
    return jitter_read(d, p, begin, i_sectors, jitter_badness);
Packit cb6d3d
  } else
Packit cb6d3d
    return read_blocks(d, p, begin, i_sectors);
Packit cb6d3d
Packit cb6d3d
}
Packit cb6d3d
Packit cb6d3d
static int
Packit cb6d3d
verify_read_command(cdrom_drive_t *d)
Packit cb6d3d
{
Packit cb6d3d
  int i;
Packit cb6d3d
  int16_t *buff=malloc(CDIO_CD_FRAMESIZE_RAW);
Packit cb6d3d
  int audioflag=0;
Packit cb6d3d
  int i_test_flags = d->i_test_flags;
Packit cb6d3d
Packit cb6d3d
  d->i_test_flags = 0;
Packit cb6d3d
Packit cb6d3d
  cdmessage(d,"Verifying drive can read CDDA...\n");
Packit cb6d3d
Packit cb6d3d
  d->enable_cdda(d,1);
Packit cb6d3d
Packit cb6d3d
  for(i=1;i<=d->tracks;i++){
Packit cb6d3d
    if(cdda_track_audiop(d,i)==1){
Packit cb6d3d
      long firstsector=cdda_track_firstsector(d,i);
Packit cb6d3d
      long lastsector=cdda_track_lastsector(d,i);
Packit cb6d3d
      long sector=(firstsector+lastsector)>>1;
Packit cb6d3d
      audioflag=1;
Packit cb6d3d
Packit cb6d3d
      if(d->read_audio(d,buff,sector,1)>0){
Packit cb6d3d
	cdmessage(d,"\tExpected command set reads OK.\n");
Packit cb6d3d
	d->enable_cdda(d,0);
Packit cb6d3d
	free(buff);
Packit cb6d3d
	d->i_test_flags = i_test_flags;
Packit cb6d3d
	return(0);
Packit cb6d3d
      }
Packit cb6d3d
    }
Packit cb6d3d
  }
Packit cb6d3d
Packit cb6d3d
  d->enable_cdda(d,0);
Packit cb6d3d
Packit cb6d3d
  if(!audioflag){
Packit cb6d3d
    cdmessage(d,"\tCould not find any audio tracks on this disk.\n");
Packit cb6d3d
    free(buff);
Packit cb6d3d
    return(-403);
Packit cb6d3d
  }
Packit cb6d3d
Packit cb6d3d
  cdmessage(d,"\n\tUnable to read any data; "
Packit cb6d3d
	    "drive probably not CDDA capable.\n");
Packit cb6d3d
Packit cb6d3d
  cderror(d,"006: Could not read any data from drive\n");
Packit cb6d3d
Packit cb6d3d
  free(buff);
Packit cb6d3d
  return(-6);
Packit cb6d3d
}
Packit cb6d3d
Packit cb6d3d
#include "drive_exceptions.h"
Packit cb6d3d
Packit cb6d3d
#ifdef HAVE_LINUX_MAJOR_H
Packit cb6d3d
static void
Packit cb6d3d
check_exceptions(cdrom_drive_t *d, const exception_t *list)
Packit cb6d3d
{
Packit cb6d3d
Packit cb6d3d
  int i=0;
Packit cb6d3d
  while(list[i].model){
Packit cb6d3d
    if(!strncmp(list[i].model,d->drive_model,strlen(list[i].model))){
Packit cb6d3d
      if(list[i].bigendianp!=-1)d->bigendianp=list[i].bigendianp;
Packit cb6d3d
      return;
Packit cb6d3d
    }
Packit cb6d3d
    i++;
Packit cb6d3d
  }
Packit cb6d3d
}
Packit cb6d3d
#endif /* HAVE_LINUX_MAJOR_H */
Packit cb6d3d
Packit cb6d3d
/* set function pointers to use the ioctl routines */
Packit cb6d3d
int
Packit cb6d3d
cddap_init_drive (cdrom_drive_t *d)
Packit cb6d3d
{
Packit cb6d3d
  int ret;
Packit cb6d3d
Packit cb6d3d
#if HAVE_LINUX_MAJOR_H
Packit cb6d3d
  switch(d->drive_type){
Packit cb6d3d
  case MATSUSHITA_CDROM_MAJOR:	/* sbpcd 1 */
Packit cb6d3d
  case MATSUSHITA_CDROM2_MAJOR:	/* sbpcd 2 */
Packit cb6d3d
  case MATSUSHITA_CDROM3_MAJOR:	/* sbpcd 3 */
Packit cb6d3d
  case MATSUSHITA_CDROM4_MAJOR:	/* sbpcd 4 */
Packit cb6d3d
    /* don't make the buffer too big; this sucker don't preempt */
Packit cb6d3d
Packit cb6d3d
    cdmessage(d,"Attempting to set sbpcd buffer size...\n");
Packit cb6d3d
Packit cb6d3d
    d->nsectors=8;
Packit cb6d3d
Packit cb6d3d
#if BUFSIZE_DETERMINATION_FIXED
Packit cb6d3d
    while(1){
Packit cb6d3d
Packit cb6d3d
      /* this ioctl returns zero on error; exactly wrong, but that's
Packit cb6d3d
         what it does. */
Packit cb6d3d
Packit cb6d3d
      if (ioctl(d->ioctl_fd, CDROMAUDIOBUFSIZ, d->nsectors)==0) {
Packit cb6d3d
	d->nsectors>>=1;
Packit cb6d3d
	if(d->nsectors==0){
Packit cb6d3d
	  char buffer[256];
Packit cb6d3d
	  d->nsectors=8;
Packit cb6d3d
	  sprintf(buffer,"\tTrouble setting buffer size.  Defaulting to %d sectors.\n",
Packit cb6d3d
		  d->nsectors);
Packit cb6d3d
	  cdmessage(d,buffer);
Packit cb6d3d
	  break; /* Oh, well.  Try to read anyway.*/
Packit cb6d3d
	}
Packit cb6d3d
      } else {
Packit cb6d3d
	char buffer[256];
Packit cb6d3d
	sprintf(buffer,"\tSetting read block size at %d sectors (%ld bytes).\n",
Packit cb6d3d
		d->nsectors,(long)d->nsectors*CDIO_CD_FRAMESIZE_RAW);
Packit cb6d3d
	cdmessage(d,buffer);
Packit cb6d3d
	break;
Packit cb6d3d
      }
Packit cb6d3d
    }
Packit cb6d3d
#endif /* BUFSIZE_DETERMINATION_FIXED */
Packit cb6d3d
Packit cb6d3d
    break;
Packit cb6d3d
  case IDE0_MAJOR:
Packit cb6d3d
  case IDE1_MAJOR:
Packit cb6d3d
  case IDE2_MAJOR:
Packit cb6d3d
  case IDE3_MAJOR:
Packit cb6d3d
    d->nsectors=8; /* it's a define in the linux kernel; we have no
Packit cb6d3d
		      way of determining other than this guess tho */
Packit cb6d3d
    d->bigendianp=0;
Packit cb6d3d
    d->is_atapi=1;
Packit cb6d3d
Packit cb6d3d
    check_exceptions(d, atapi_list);
Packit cb6d3d
Packit cb6d3d
    break;
Packit cb6d3d
  default:
Packit cb6d3d
    d->nsectors=25;  /* The max for SCSI MMC2 */
Packit cb6d3d
  }
Packit cb6d3d
#else
Packit cb6d3d
  {
Packit cb6d3d
    char buffer[256];
Packit cb6d3d
    d->nsectors = 8;
Packit cb6d3d
    sprintf(buffer,"\tSetting read block size at %d sectors (%ld bytes).\n",
Packit cb6d3d
	    d->nsectors,(long)d->nsectors*CDIO_CD_FRAMESIZE_RAW);
Packit cb6d3d
    cdmessage(d,buffer);
Packit cb6d3d
  }
Packit cb6d3d
#endif /*HAVE_LINUX_MAJOR_H*/
Packit cb6d3d
Packit cb6d3d
  d->enable_cdda = dummy_exception;
Packit cb6d3d
  d->set_speed   = cddap_setspeed;
Packit cb6d3d
  d->read_toc    = cddap_readtoc;
Packit cb6d3d
  d->read_audio  = cddap_read;
Packit cb6d3d
Packit cb6d3d
  ret = d->tracks = d->read_toc(d);
Packit cb6d3d
  if(d->tracks<1)
Packit cb6d3d
    return(ret);
Packit cb6d3d
Packit cb6d3d
  d->opened=1;
Packit cb6d3d
Packit cb6d3d
  if( (ret=verify_read_command(d)) ) return(ret);
Packit cb6d3d
Packit cb6d3d
  d->error_retry=1;
Packit cb6d3d
Packit cb6d3d
  return(0);
Packit cb6d3d
}