diff options
Diffstat (limited to 'icedax/ioctl.c')
-rw-r--r-- | icedax/ioctl.c | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/icedax/ioctl.c b/icedax/ioctl.c new file mode 100644 index 0000000..c3bd627 --- /dev/null +++ b/icedax/ioctl.c @@ -0,0 +1,578 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)ioctl.c 1.22 06/02/19 Copyright 1998,1999,2000 Heiko Eissfeldt, Copyright 2006 J. Schilling */ +/*** + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) 1999 Heiko Eissfeldt heiko@colossus.escape.de + * + * Ioctl interface module for cdrom drive access + * + * Solaris ATAPI cdrom drives are untested! + * + */ +#include "config.h" +#include <stdio.h> +#include <standard.h> +#include <stdxlib.h> +#include <unixstd.h> +#include <strdefs.h> +#include <errno.h> +#include <signal.h> +#include <fctldefs.h> +#include <assert.h> + +#include <sys/ioctl.h> +#include <statdefs.h> +#include <schily.h> +#include <device.h> + +#include <usal/scsitransp.h> + +#include "mycdrom.h" +#include "lowlevel.h" +/* some include file locations have changed with newer kernels */ +#if defined (__linux__) +# if LINUX_VERSION_CODE > 0x10300 + 97 +# if LINUX_VERSION_CODE < 0x200ff +# include <linux/sbpcd.h> +# include <linux/ucdrom.h> +# endif +# if !defined(CDROM_SELECT_SPEED) +# include <linux/ucdrom.h> +# endif +# endif +#endif + +#include "mytype.h" +#include "byteorder.h" +#include "interface.h" +#include "toc.h" +#include "icedax.h" +#include "ioctl.h" +#include "global.h" +#include "exitcodes.h" + +#include <utypes.h> +#include <wodim.h> + +#if defined (HAVE_IOCTL_INTERFACE) +#if !defined(sun) && !defined(__sun) && !(defined(__FreeBSD__) && (__FreeBSD_version >= 501112)) +static struct cdrom_read_audio arg; +#endif + +#if (defined(__FreeBSD__) && __FreeBSD_version >= 400014) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +static unsigned sector_size; +#endif + +static int err; + +static void EnableCdda_cooked(SCSI *usalp, int fAudioMode, unsigned uSectorsize); +/* ARGSUSED */ +static void EnableCdda_cooked(SCSI *usalp, int fAudioMode, unsigned uSectorsize) +{ +#if (defined(__FreeBSD__) && __FreeBSD_version >= 400014) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + if (usalp && usalp->verbose) + fprintf(stderr, "EnableCdda_cooked (CDRIOCSETBLOCKSIZE)...\n"); + + if (fAudioMode) { + if (ioctl(global.cooked_fd, CDRIOCGETBLOCKSIZE, §or_size) ==-1) + sector_size = CD_FRAMESIZE; + ioctl(global.cooked_fd, CDRIOCSETBLOCKSIZE, &uSectorsize); + } else + ioctl(global.cooked_fd, CDRIOCSETBLOCKSIZE, §or_size); +#else +#if defined CDIOCSETCDDA + if (usalp && usalp->verbose) { + fprintf(stderr, "EnableCdda_cooked (CDIOCSETCDDA)...\n"); + if (uSectorsize != CD_FRAMESIZE_RAW) + fprintf(stderr, "non audio sector size is ignored.\n"); + } + + ioctl(global.cooked_fd, CDIOCSETCDDA, &fAudioMode); +#else + fprintf(stderr, "EnableCdda_cooked (CDIOCSETCDDA) is not available...\n"); +#endif +#endif + +} + + +static unsigned ReadToc_cooked(SCSI *x); + +/* read the table of contents (toc) via the ioctl interface */ +static unsigned ReadToc_cooked(SCSI *x) +{ + unsigned i; + unsigned tracks; + struct cdrom_tochdr hdr; + struct cdrom_tocentry entry[100]; + struct cdrom_tocentry entryMSF[100]; + + if (x && x->verbose) { + fprintf(stderr, "ReadToc_cooked (CDROMREADTOCHDR)...\n"); + } + + /* get TocHeader to find out how many entries there are */ + err = ioctl( global.cooked_fd, CDROMREADTOCHDR, &hdr ); + if ( err != 0 ) { + /* error handling */ + if (err == -1) { + if (errno == EPERM) + fprintf( stderr, "Please run this program setuid root.\n"); + perror("cooked: Read TOC "); + exit( DEVICE_ERROR ); + } else { + fprintf( stderr, "can't get TocHeader (error %d).\n", err ); + exit( MEDIA_ERROR ); + } + } + /* get all TocEntries */ + for ( i = 0; i < hdr.cdth_trk1; i++ ) { + entryMSF[i].cdte_track = 1+i; + entryMSF[i].cdte_format = CDROM_MSF; + err = ioctl( global.cooked_fd, CDROMREADTOCENTRY, &entryMSF[i] ); + if ( err != 0 ) { + /* error handling */ + fprintf( stderr, "can't get TocEntry #%d msf (error %d).\n", i+1, err ); + exit( MEDIA_ERROR ); + } + } + entryMSF[i].cdte_track = CDROM_LEADOUT; + entryMSF[i].cdte_format = CDROM_MSF; + err = ioctl( global.cooked_fd, CDROMREADTOCENTRY, &entryMSF[i] ); + if ( err != 0 ) { + /* error handling */ + fprintf( stderr, "can't get TocEntry LEADOUT msf (error %d).\n", err ); + exit( MEDIA_ERROR ); + } + tracks = hdr.cdth_trk1+1; +/* + for (i = 0; i < tracks; i++) { + toc[i].bFlags = (entry[i].cdte_adr << 4) | (entry[i].cdte_ctrl & 0x0f); + toc[i].bTrack = entry[i].cdte_track; + toc[i].mins = entry[i].cdte_addr.msf.minute; + toc[i].secs = entry[i].cdte_addr.msf.second; + toc[i].frms = entry[i].cdte_addr.msf.frame; + } +*/ + /* get all TocEntries now in lba format */ + for ( i = 0; i < hdr.cdth_trk1; i++ ) { + entry[i].cdte_track = 1+i; + entry[i].cdte_format = CDROM_LBA; + err = ioctl( global.cooked_fd, CDROMREADTOCENTRY, &entry[i] ); + if ( err != 0 ) { + /* error handling */ + fprintf( stderr, "can't get TocEntry #%d lba (error %d).\n", i+1, err ); + exit( MEDIA_ERROR ); + } +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + entry[i].cdte_addr.lba = be32_to_cpu(entry[i].cdte_addr.lba); +#endif + } + entry[i].cdte_track = CDROM_LEADOUT; + entry[i].cdte_format = CDROM_LBA; + err = ioctl( global.cooked_fd, CDROMREADTOCENTRY, &entry[i] ); + if ( err != 0 ) { + /* error handling */ + fprintf( stderr, "can't get TocEntry LEADOUT lba (error %d).\n", err ); + exit( MEDIA_ERROR ); + } +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + entry[i].cdte_addr.lba = be32_to_cpu(entry[i].cdte_addr.lba); +#endif + + for (i = 0; i < tracks; i++) { + toc_entry(i+1, + (entry[i].cdte_adr << 4) | (entry[i].cdte_ctrl & 0x0f), + entry[i].cdte_track, + NULL /* ISRC */, + entry[i].cdte_addr.lba, + entryMSF[i].cdte_addr.msf.minute, + entryMSF[i].cdte_addr.msf.second, + entryMSF[i].cdte_addr.msf.frame); + } + bufferTOC[0] = '\0'; + bufferTOC[1] = '\0'; + return --tracks; /* without lead-out */ +} + +static void trash_cache_cooked(UINT4 *p, unsigned lSector, + unsigned SectorBurstVal); + +static void trash_cache_cooked(UINT4 *p, unsigned lSector, + unsigned SectorBurstVal) +{ + /* trash the cache */ + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#if defined(__FreeBSD__) && __FreeBSD_version >= 501112 + pread(global.cooked_fd, (void *) &p[0], 3*CD_FRAMESIZE_RAW, + find_an_off_sector(lSector, SectorBurstVal)*CD_FRAMESIZE_RAW); +#else + static struct cdrom_read_audio arg2; + + arg2.address.lba = find_an_off_sector(lSector, SectorBurstVal); + arg2.addr_format = CDROM_LBA; + arg2.nframes = 3; + arg2.buffer = (unsigned char *) &p[0]; + + ioctl(global.cooked_fd, CDROMREADAUDIO, &arg2); +#endif +#endif +#if defined __linux__ + static struct cdrom_read_audio arg2; + + arg2.addr.lba = find_an_off_sector(lSector, SectorBurstVal); + arg2.addr_format = CDROM_LBA; + arg2.nframes = 3; + arg2.buf = (unsigned char *) &p[0]; + + ioctl(global.cooked_fd, CDROMREADAUDIO, &arg2); +#endif +#if defined __sun || (defined HAVE_SYS_CDIO_H && defined CDROM_DA_NO_SUBCODE) + struct cdrom_cdda suncdda; + + suncdda.cdda_addr = lSector; + suncdda.cdda_length = SectorBurstVal*CD_FRAMESIZE_RAW; + suncdda.cdda_data = (char *) &p[0]; + suncdda.cdda_subcode = CDROM_DA_NO_SUBCODE; + + ioctl(global.cooked_fd, CDROMCDDA, &suncdda); +#endif +} + +static void ReadCdRomData_cooked(SCSI *x, UINT4 *p, unsigned lSector, + unsigned SectorBurstVal); +/* read 'SectorBurst' adjacent sectors of data sectors + * to Buffer '*p' beginning at sector 'lSector' + */ +static void ReadCdRomData_cooked(SCSI *x, UINT4 *p, unsigned lSector, + unsigned SectorBurstVal) +{ + int retval; + + if (x && x->verbose) { + fprintf(stderr, "ReadCdRomData_cooked (lseek & read)...\n"); + } + + if ((retval = lseek(global.cooked_fd, lSector*CD_FRAMESIZE, SEEK_SET)) + != (int)lSector*CD_FRAMESIZE) { perror("cannot seek sector"); } + if ((retval = read(global.cooked_fd, p, SectorBurstVal*CD_FRAMESIZE)) + != (int)SectorBurstVal*CD_FRAMESIZE) { perror("cannot read sector"); } + + return; +} + +static int ReadCdRom_cooked(SCSI *x, UINT4 *p, unsigned lSector, + unsigned SectorBurstVal); +/* read 'SectorBurst' adjacent sectors of audio sectors + * to Buffer '*p' beginning at sector 'lSector' + */ +static int ReadCdRom_cooked(SCSI *x, UINT4 *p, unsigned lSector, + unsigned SectorBurstVal) +{ + int retry_count=0; + static int nothing_read = 1; + +/* read 2352 bytes audio data */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#if defined(__FreeBSD__) && __FreeBSD_version >= 501112 + if (x && x->verbose) { + fprintf(stderr, "ReadCdRom_cooked (pread)...\n"); + } + + do { + err = 0; + if (pread(global.cooked_fd, (void *) &p[0], SectorBurstVal*CD_FRAMESIZE_RAW, + lSector*CD_FRAMESIZE_RAW) == -1) + err = -1; +#else + arg.address.lba = lSector; + arg.addr_format = CDROM_LBA; + arg.nframes = SectorBurstVal; + arg.buffer = (unsigned char *) &p[0]; + + if (x && x->verbose) { + fprintf(stderr, "ReadCdRom_cooked (CDROMREADAUDIO)...\n"); + } + + do { + err = ioctl(global.cooked_fd, CDROMREADAUDIO, &arg); +#endif +#endif +#if defined __linux__ + arg.addr.lba = lSector; + arg.addr_format = CDROM_LBA; + arg.nframes = SectorBurstVal; + arg.buf = (unsigned char *) &p[0]; + + if (x && x->verbose) { + fprintf(stderr, "ReadCdRom_cooked (CDROMREADAUDIO)...\n"); + } + + do { + err = ioctl(global.cooked_fd, CDROMREADAUDIO, &arg); +#endif +#if defined __sun || (defined HAVE_SYS_CDIO_H && defined CDROM_DA_NO_SUBCODE) + struct cdrom_cdda suncdda; + + suncdda.cdda_addr = lSector; + suncdda.cdda_length = SectorBurstVal*CD_FRAMESIZE_RAW; + suncdda.cdda_data = (char *) &p[0]; + suncdda.cdda_subcode = CDROM_DA_NO_SUBCODE; + + if (x && x->verbose) { + fprintf(stderr, "ReadCdRom_cooked (CDROMCDDA)...\n"); + } + + do { + err = ioctl(global.cooked_fd, CDROMCDDA, &suncdda); +#endif + retry_count++; + + if (err) { + trash_cache_cooked(p, lSector, SectorBurstVal); + } + + } while ((err) && (retry_count < 30)); + if (err != 0) { + if (x->silent == 0) { + /* error handling */ + if (err == -1) { + if (nothing_read && (errno == EINVAL || errno == EIO)) + fprintf( stderr, "Sorry, this driver and/or drive does not support cdda reading.\n"); + perror("cooked: Read cdda "); + fprintf(stderr, " sector %u + %u, buffer %p + %x\n", lSector, SectorBurstVal, p, global.shmsize); + } else { + fprintf(stderr, "can't read frame #%u (error %d).\n", + lSector, err); + } + } + return SectorBurstVal - 1; + } else { + nothing_read = 0; + } + + return SectorBurstVal; +} + +static int StopPlay_cooked(SCSI *x); +static int StopPlay_cooked(SCSI *x) +{ + if (x && x->verbose) { + fprintf(stderr, "StopPlay_cooked (CDROMSTOP)...\n"); + } + + return ioctl( global.cooked_fd, CDROMSTOP, 0 ) ? 0 : -1; +} + +static int Play_at_cooked(SCSI *x, unsigned int from_sector, + unsigned int sectors); +static int Play_at_cooked(SCSI *x, unsigned int from_sector, + unsigned int sectors) +{ + struct cdrom_msf cmsf; + int retval; + + if (x && x->verbose) { + fprintf(stderr, "Play_at_cooked (CDROMSTART & CDROMPLAYMSF)... (%u-%u)", + from_sector, from_sector+sectors-1); + + fprintf(stderr, "\n"); + } + + cmsf.cdmsf_min0 = (from_sector + 150) / (60*75); + cmsf.cdmsf_sec0 = ((from_sector + 150) / 75) % 60; + cmsf.cdmsf_frame0 = (from_sector + 150) % 75; + cmsf.cdmsf_min1 = (from_sector + 150 + sectors) / (60*75); + cmsf.cdmsf_sec1 = ((from_sector + 150 + sectors) / 75) % 60; + cmsf.cdmsf_frame1 = (from_sector + 150 + sectors) % 75; + +#if 0 +/* makes index scanning under FreeBSD too slow */ + if (( retval = ioctl( global.cooked_fd, CDROMSTART, 0 )) != 0){ + perror(""); + } +#endif + if (( retval = ioctl( global.cooked_fd, CDROMPLAYMSF, &cmsf )) != 0){ + perror(""); + } + return retval; +} + +/* request sub-q-channel information. This function may cause confusion + * for a drive, when called in the sampling process. + */ +static subq_chnl *ReadSubQ_cooked(SCSI *x, unsigned char sq_format, + unsigned char track) +{ + struct cdrom_subchnl sub_ch; + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + struct cd_sub_channel_info sub_ch_info; + + if (x && x->verbose) { + fprintf(stderr, "ReadSubQ_cooked (CDROM_GET_MCN or CDROMSUBCHNL)...\n"); + } + + sub_ch.address_format = CD_MSF_FORMAT; + sub_ch.track = track; + sub_ch.data_len = sizeof(struct cd_sub_channel_info); + sub_ch.data = &sub_ch_info; + + switch (sq_format) { + case GET_CATALOGNUMBER: + sub_ch.data_format = CD_MEDIA_CATALOG; +#else + if (x && x->verbose) { + fprintf(stderr, "ReadSubQ_cooked (CDROM_GET_MCN or CDROMSUBCHNL)...\n"); + } + + switch (sq_format) { + case GET_CATALOGNUMBER: +#endif +#if defined CDROM_GET_MCN + if (!(err = ioctl(global.cooked_fd, CDROM_GET_MCN, (struct cdrom_mcn *) SubQbuffer))) { + subq_chnl *SQp = (subq_chnl *) SubQbuffer; + subq_catalog *SQPp = (subq_catalog *) &SQp->data; + + memmove(SQPp->media_catalog_number, SQp, sizeof (SQPp->media_catalog_number)); + SQPp->zero = 0; + SQPp->mc_valid = 0x80; + break; + } else +#endif + { + return NULL; + } + case GET_POSITIONDATA: +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + sub_ch.data_format = CD_CURRENT_POSITION; +#endif +#if defined (__linux__) + sub_ch.cdsc_format = CDROM_MSF; +#endif + if (!(err = ioctl(global.cooked_fd, CDROMSUBCHNL, &sub_ch))) { + /* copy to SubQbuffer */ + subq_chnl *SQp = (subq_chnl *) (SubQbuffer); + subq_position *SQPp = (subq_position *) SQp->data; + SQp->audio_status = sub_ch.cdsc_audiostatus; + SQp->format = sub_ch.cdsc_format; + SQp->control_adr = (sub_ch.cdsc_adr << 4) | (sub_ch.cdsc_ctrl & 0x0f); + SQp->track = sub_ch.cdsc_trk; + SQp->index = sub_ch.cdsc_ind; + SQPp->abs_min = sub_ch.cdsc_absaddr.msf.minute; + SQPp->abs_sec = sub_ch.cdsc_absaddr.msf.second; + SQPp->abs_frame = sub_ch.cdsc_absaddr.msf.frame; + SQPp->trel_min = sub_ch.cdsc_reladdr.msf.minute; + SQPp->trel_sec = sub_ch.cdsc_reladdr.msf.second; + SQPp->trel_frame = sub_ch.cdsc_reladdr.msf.frame; + } else { + if (err == -1) { + if (errno == EPERM) + fprintf( stderr, "Please run this program setuid root.\n"); + perror("cooked: Read subq "); + exit( DEVICE_ERROR ); + } else { + fprintf(stderr, "can't read sub q channel (error %d).\n", err); + exit( DEVICE_ERROR ); + } + } + break; + default: + return NULL; + } /* switch */ + return (subq_chnl *)(SubQbuffer); +} + +/* Speed control */ +static void SpeedSelect_cooked(SCSI *x, unsigned speed); +/* ARGSUSED */ +static void SpeedSelect_cooked(SCSI *x, unsigned speed) +{ + if (x && x->verbose) { + fprintf(stderr, "SpeedSelect_cooked (CDROM_SELECT_SPEED)...\n"); + } + +#ifdef CDROM_SELECT_SPEED + /* CAUTION!!!!! Non standard ioctl parameter types here!!!! */ + if ((err = ioctl(global.cooked_fd, CDROM_SELECT_SPEED, speed))) { + if (err == -1) { + if (errno == EPERM) + fprintf( stderr, "Please run this program setuid root.\n"); + perror("cooked: Speed select "); + /*exit( err ); */ + } else { + fprintf(stderr, "can't set speed %d (error %d).\n", speed, err); + exit( DEVICE_ERROR ); + } + } +#endif +} + +/* set function pointers to use the ioctl routines */ +void SetupCookedIoctl(char *pdev_name) +{ +#if (HAVE_ST_RDEV == 1) + struct stat statstruct; + + if (fstat(global.cooked_fd, &statstruct)) { + fprintf(stderr, "cannot stat cd %d (%s)\n",global.cooked_fd, pdev_name); + exit(STAT_ERROR); + } +#if defined __linux__ + switch (major(statstruct.st_rdev)) { + case CDU31A_CDROM_MAJOR: /* sony cdu-31a/33a */ + global.nsectors = 13; + if (global.nsectors >= 14) { + global.overlap = 10; + } + break; + case MATSUSHITA_CDROM_MAJOR: /* sbpcd 1 */ + case MATSUSHITA_CDROM2_MAJOR: /* sbpcd 2 */ + case MATSUSHITA_CDROM3_MAJOR: /* sbpcd 3 */ + case MATSUSHITA_CDROM4_MAJOR: /* sbpcd 4 */ + /* some are more compatible than others */ + global.nsectors = 13; + break; + default: + global.nsectors = 8; + break; + } + err = ioctl(global.cooked_fd, CDROMAUDIOBUFSIZ, global.nsectors); + + switch (major(statstruct.st_rdev)) { + case MATSUSHITA_CDROM_MAJOR: /* sbpcd 1 */ + case MATSUSHITA_CDROM2_MAJOR: /* sbpcd 2 */ + case MATSUSHITA_CDROM3_MAJOR: /* sbpcd 3 */ + case MATSUSHITA_CDROM4_MAJOR: /* sbpcd 4 */ + if (err == -1) { + perror("ioctl(CDROMAUDIOBUFSIZ)"); + } + } +#endif +#endif + EnableCdda = EnableCdda_cooked; + ReadCdRom = ReadCdRom_cooked; + ReadCdRomData = (int (*)(SCSI *, unsigned char *, unsigned, unsigned)) ReadCdRomData_cooked; + doReadToc = ReadToc_cooked; + ReadTocText = NULL; + ReadSubQ = ReadSubQ_cooked; + ReadSubChannels = NULL; + SelectSpeed = SpeedSelect_cooked; + Play_at = Play_at_cooked; + StopPlay = StopPlay_cooked; + trash_cache = trash_cache_cooked; + ReadLastAudio = NULL; +} +#endif |