/* @(#)ioctl.c 1.39 10/12/19 Copyright 1998,1999,2000 Heiko Eissfeldt, Copyright 2006-2010 J. Schilling */ #include "config.h" #ifndef lint static UConst char sccsid[] = "@(#)ioctl.c 1.39 10/12/19 Copyright 1998,1999,2000 Heiko Eissfeldt, Copyright 2006-2010 J. Schilling"; #endif /* * Copyright (C) 1999 Heiko Eissfeldt heiko@colossus.escape.de * Copyright (c) 2006-2010 J. Schilling * * Ioctl interface module for cdrom drive access * * Solaris ATAPI cdrom drives are untested! * */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 # include # endif # if !defined(CDROM_SELECT_SPEED) # include # endif # endif #endif #include "mytype.h" #include "byteorder.h" #include "interface.h" #include "toc.h" #include "cdda2wav.h" #include "ioctl.h" #include "global.h" #include "exitcodes.h" #include #include #if defined(HAVE_IOCTL_INTERFACE) #if !defined(sun) && !defined(__sun) && \ !((defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && \ (__FreeBSD_version >= 501112)) static struct cdrom_read_audio arg; #endif #if ((defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && \ __FreeBSD_version >= 400014) || \ defined(__DragonFly__) static unsigned sector_size; #endif static int err; static void EnableCdda_cooked __PR((SCSI *scgp, int fAudioMode, unsigned uSectorsize)); /* ARGSUSED */ static void EnableCdda_cooked(scgp, fAudioMode, uSectorsize) SCSI *scgp; int fAudioMode; unsigned uSectorsize; { #if ((defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && \ __FreeBSD_version >= 400014) || \ defined(__DragonFly__) if (scgp && scgp->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 (scgp && scgp->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 __PR((SCSI *x)); /* read the table of contents (toc) via the ioctl interface */ static unsigned ReadToc_cooked(x) 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")); } errmsg(_("Cooked: Error in read TOC.\n")); exit(DEVICE_ERROR); } else { errmsgno(EX_BAD, _("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 */ errmsg(("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 */ errmsg(_("Can't get TocEntry LEADOUT msf (error %d).\n"), err); exit(MEDIA_ERROR); } tracks = hdr.cdth_trk1+1; #ifdef nonono 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; } #endif /* * 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 */ errmsg(_("Can't get TocEntry #%d lba (error %d).\n"), i+1, err); exit(MEDIA_ERROR); } #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) 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 */ errmsg(_("Can't get TocEntry LEADOUT lba (error %d).\n"), err); exit(MEDIA_ERROR); } #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) 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 __PR((UINT4 *p, unsigned lSector, unsigned SectorBurstVal)); static void trash_cache_cooked(p, lSector, SectorBurstVal) UINT4 *p; unsigned lSector; unsigned SectorBurstVal; { /* * trash the cache */ #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ defined(__DragonFly__) #if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && \ __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 __PR((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(x, p, lSector, SectorBurstVal) 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) { errmsg(_("Cannot seek sector.\n")); } if ((retval = read(global.cooked_fd, p, SectorBurstVal*CD_FRAMESIZE)) != (int)SectorBurstVal*CD_FRAMESIZE) { errmsg(_("Cannot read sector.\n")); } } static int ReadCdRom_cooked __PR((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(x, p, lSector, SectorBurstVal) 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(__FreeBSD_kernel__) || \ defined(__DragonFly__) #if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && \ __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")); } errmsg(_("Cooked: Error read cdda sector %u + %u, buffer %p + %x\n"), lSector, SectorBurstVal, p, global.shmsize); } else { errmsgno(EX_BAD, _("Can't read frame #%u (error %d).\n"), lSector, err); } } return (SectorBurstVal - 1); } else { nothing_read = 0; } return (SectorBurstVal); } static int StopPlay_cooked __PR((SCSI *x)); static int StopPlay_cooked(x) 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 __PR((SCSI *x, unsigned int from_sector, unsigned int sectors)); static int Play_at_cooked(x, from_sector, sectors) 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) { errmsg(_("Cannot do ioctl(%d, CDROMSTART, 0).\n"), global.cooked_fd); } #endif if ((retval = ioctl(global.cooked_fd, CDROMPLAYMSF, &cmsf)) != 0) { errmsg(_("Cannot do ioctl(%d, CDROMPLAYMSF, ...).\n"), global.cooked_fd); } return (retval); } #if defined PROTOTYPES static subq_chnl *ReadSubQ_cooked __PR((SCSI *x, unsigned char sq_format, unsigned char track)) #else /* * request sub-q-channel information. This function may cause confusion * for a drive, when called in the sampling process. */ static subq_chnl * ReadSubQ_cooked(x, sq_format, track) SCSI *x; unsigned char sq_format; unsigned char track; #endif { struct cdrom_subchnl sub_ch; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) 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(__FreeBSD_kernel__) || defined(__DragonFly__) 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")); errmsg(_("Cooked: Error in read subq.\n")); exit(DEVICE_ERROR); } else { errmsgno(EX_BAD, _("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 __PR((SCSI *x, unsigned speed)); /* ARGSUSED */ static void SpeedSelect_cooked(x, speed) 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")); } errmsg(_("Cooked: Error in speed select.\n")); /* exit(err); */ } else { errmsgno(EX_BAD, _("Can't set speed %d (error %d).\n"), speed, err); exit(DEVICE_ERROR); } } #endif } /* * set function pointers to use the ioctl routines */ void SetupCookedIoctl(pdev_name) char *pdev_name; { #if (HAVE_ST_RDEV == 1) struct stat statstruct; if (fstat(global.cooked_fd, &statstruct)) { errmsg(_("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) { errmsg(_("Error doing ioctl(CDROMAUDIOBUFSIZ).\n")); } } #endif #endif EnableCdda = EnableCdda_cooked; ReadCdRom = ReadCdRom_cooked; ReadCdRomData = (int (*) __PR((SCSI *, unsigned char *, unsigned, unsigned))) ReadCdRomData_cooked; doReadToc = ReadToc_cooked; ReadTocText = (void (*) __PR((SCSI *)))NULL; ReadSubQ = ReadSubQ_cooked; ReadSubChannels = (subq_chnl * (*) __PR((SCSI *, unsigned)))NULL; SelectSpeed = SpeedSelect_cooked; Play_at = Play_at_cooked; StopPlay = StopPlay_cooked; trash_cache = trash_cache_cooked; ReadLastAudio = (unsigned (*) __PR((SCSI *)))NULL; } #endif