Blame lib/driver/gnu_linux.c

Packit dd8086
/*
Packit dd8086
  Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org>
Packit dd8086
  Copyright (C) 2002-2006, 2008-2013, 2017 Rocky Bernstein
Packit dd8086
  <rocky@gnu.org>
Packit dd8086
Packit dd8086
  This program is free software: you can redistribute it and/or modify
Packit dd8086
  it under the terms of the GNU General Public License as published by
Packit dd8086
  the Free Software Foundation, either version 3 of the License, or
Packit dd8086
  (at your option) any later version.
Packit dd8086
Packit dd8086
  This program is distributed in the hope that it will be useful,
Packit dd8086
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit dd8086
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit dd8086
  GNU General Public License for more details.
Packit dd8086
Packit dd8086
  You should have received a copy of the GNU General Public License
Packit dd8086
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit dd8086
*/
Packit dd8086
Packit dd8086
/* This file contains Linux-specific code and implements low-level
Packit dd8086
   control of the CD drive.
Packit dd8086
*/
Packit dd8086

Packit dd8086
#ifdef HAVE_CONFIG_H
Packit dd8086
# include "config.h"
Packit dd8086
# define __CDIO_CONFIG_H__ 1
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STRING_H
Packit dd8086
#include <string.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include <cdio/sector.h>
Packit dd8086
#include <cdio/util.h>
Packit dd8086
#include <cdio/types.h>
Packit dd8086
#include <cdio/mmc_cmds.h>
Packit dd8086
#include <cdio/audio.h>
Packit dd8086
#include <cdio/cdtext.h>
Packit dd8086
#include "cdio_assert.h"
Packit dd8086
#include "cdio_private.h"
Packit dd8086
Packit dd8086
#ifdef HAVE_LINUX_CDROM
Packit dd8086
Packit dd8086
#include <sys/types.h>
Packit dd8086
#include <sys/wait.h>
Packit dd8086
#include <limits.h>
Packit dd8086
Packit dd8086
#if defined(HAVE_LINUX_VERSION_H)
Packit dd8086
# include <linux/version.h>
Packit dd8086
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,16)
Packit dd8086
#   define __CDIO_LINUXCD_BUILD
Packit dd8086
# else
Packit dd8086
#  error "You need a kernel greater than 2.2.16 to have CDROM support"
Packit dd8086
# endif
Packit dd8086
#else
Packit dd8086
#  error "You need <linux/version.h> to have CDROM support"
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include <stdio.h>
Packit dd8086
#include <stdlib.h>
Packit dd8086
#include <errno.h>
Packit dd8086
#include <unistd.h>
Packit dd8086
#include <fcntl.h>
Packit dd8086
#include <mntent.h>
Packit dd8086
Packit dd8086
#include <linux/cdrom.h>
Packit dd8086
#include <scsi/scsi.h>
Packit dd8086
#include <scsi/sg.h>
Packit dd8086
#include <scsi/scsi_ioctl.h>
Packit dd8086
#include <sys/mount.h>
Packit dd8086
Packit dd8086
#include <sys/stat.h>
Packit dd8086
#include <sys/types.h>
Packit dd8086
#include <sys/ioctl.h>
Packit dd8086
Packit dd8086
#ifndef PATH_MAX
Packit dd8086
#define PATH_MAX 4096
Packit dd8086
#endif
Packit dd8086
Packit dd8086
typedef enum {
Packit dd8086
  _AM_NONE,
Packit dd8086
  _AM_IOCTL,
Packit dd8086
  _AM_READ_CD,
Packit dd8086
  _AM_READ_10,
Packit dd8086
  _AM_MMC_RDWR,
Packit dd8086
  _AM_MMC_RDWR_EXCL,
Packit dd8086
} access_mode_t;
Packit dd8086
Packit dd8086
typedef struct {
Packit dd8086
  /* Things common to all drivers like this.
Packit dd8086
     This must be first. */
Packit dd8086
  generic_img_private_t gen;
Packit dd8086
Packit dd8086
  access_mode_t access_mode;
Packit dd8086
Packit dd8086
  /* Some of the more OS specific things. */
Packit dd8086
  /* Entry info for each track, add 1 for leadout. */
Packit dd8086
  struct cdrom_tocentry  tocent[CDIO_CD_MAX_TRACKS+1];
Packit dd8086
Packit dd8086
  struct cdrom_tochdr    tochdr;
Packit dd8086
Packit dd8086
} _img_private_t;
Packit dd8086
Packit dd8086
/* Some ioctl() errno values which occur when the tray is empty */
Packit dd8086
#define ERRNO_TRAYEMPTY(errno)  \
Packit dd8086
        ((errno == EIO) || (errno == ENOENT) || (errno == EINVAL))
Packit dd8086
Packit dd8086
/**** prototypes for static functions ****/
Packit dd8086
static bool is_cdrom_linux(const char *drive, char *mnttype);
Packit dd8086
static bool read_toc_linux (void *p_user_data);
Packit dd8086
static driver_return_code_t
Packit dd8086
run_mmc_cmd_linux( void *p_user_data,
Packit dd8086
                   unsigned int i_timeout,
Packit dd8086
                   unsigned int i_cdb,
Packit dd8086
                   const mmc_cdb_t *p_cdb,
Packit dd8086
                   cdio_mmc_direction_t e_direction,
Packit dd8086
                   unsigned int i_buf,
Packit dd8086
                   /*in/out*/ void *p_buf );
Packit dd8086
Packit dd8086
static access_mode_t
Packit dd8086
str_to_access_mode_linux(const char *psz_access_mode)
Packit dd8086
{
Packit dd8086
  const access_mode_t default_access_mode = _AM_IOCTL;
Packit dd8086
Packit dd8086
  if (NULL==psz_access_mode) return default_access_mode;
Packit dd8086
Packit dd8086
  if (!strcmp(psz_access_mode, "IOCTL"))
Packit dd8086
    return _AM_IOCTL;
Packit dd8086
  else if (!strcmp(psz_access_mode, "READ_CD"))
Packit dd8086
    return _AM_READ_CD;
Packit dd8086
  else if (!strcmp(psz_access_mode, "READ_10"))
Packit dd8086
    return _AM_READ_10;
Packit dd8086
  else if (!strcmp(psz_access_mode, "MMC_RDWR"))
Packit dd8086
    return _AM_MMC_RDWR;
Packit dd8086
  else if (!strcmp(psz_access_mode, "MMC_RDWR_EXCL"))
Packit dd8086
    return _AM_MMC_RDWR_EXCL;
Packit dd8086
  else {
Packit dd8086
    cdio_warn ("unknown access type: %s. Default IOCTL used.",
Packit dd8086
               psz_access_mode);
Packit dd8086
    return default_access_mode;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
static char *
Packit dd8086
check_mounts_linux(const char *mtab)
Packit dd8086
{
Packit dd8086
  FILE *mntfp;
Packit dd8086
  struct mntent *mntent;
Packit dd8086
Packit dd8086
  mntfp = setmntent(mtab, "r");
Packit dd8086
  if ( mntfp != NULL ) {
Packit dd8086
    char *tmp;
Packit dd8086
    char *mnt_type;
Packit dd8086
    char *mnt_dev;
Packit dd8086
    unsigned int i_mnt_type;
Packit dd8086
    unsigned int i_mnt_dev;
Packit dd8086
Packit dd8086
    while ( (mntent=getmntent(mntfp)) != NULL ) {
Packit dd8086
      i_mnt_type = strlen(mntent->mnt_type) + 1;
Packit dd8086
      mnt_type = calloc(1, i_mnt_type);
Packit dd8086
      if (mnt_type == NULL)
Packit dd8086
        continue;  /* maybe you'll get lucky next time. */
Packit dd8086
Packit dd8086
      i_mnt_dev = strlen(mntent->mnt_fsname) + 1;
Packit dd8086
      mnt_dev = calloc(1, i_mnt_dev);
Packit dd8086
      if (mnt_dev == NULL) {
Packit dd8086
        free(mnt_type);
Packit dd8086
        continue;
Packit dd8086
      }
Packit dd8086
Packit dd8086
      strncpy(mnt_type, mntent->mnt_type, i_mnt_type);
Packit dd8086
      strncpy(mnt_dev, mntent->mnt_fsname, i_mnt_dev);
Packit dd8086
Packit dd8086
      /* Handle "supermount" filesystem mounts */
Packit dd8086
      if ( strcmp(mnt_type, "supermount") == 0 ) {
Packit dd8086
        tmp = strstr(mntent->mnt_opts, "fs=");
Packit dd8086
        if ( tmp ) {
Packit dd8086
          free(mnt_type);
Packit dd8086
          mnt_type = strdup(tmp + strlen("fs="));
Packit dd8086
          if ( mnt_type ) {
Packit dd8086
            tmp = strchr(mnt_type, ',');
Packit dd8086
            if ( tmp ) {
Packit dd8086
              *tmp = '\0';
Packit dd8086
            }
Packit dd8086
          }
Packit dd8086
        }
Packit dd8086
        tmp = strstr(mntent->mnt_opts, "dev=");
Packit dd8086
        if ( tmp ) {
Packit dd8086
          free(mnt_dev);
Packit dd8086
          mnt_dev = strdup(tmp + strlen("dev="));
Packit dd8086
          if ( mnt_dev ) {
Packit dd8086
            tmp = strchr(mnt_dev, ',');
Packit dd8086
            if ( tmp ) {
Packit dd8086
              *tmp = '\0';
Packit dd8086
            }
Packit dd8086
          }
Packit dd8086
        }
Packit dd8086
      }
Packit dd8086
      if ( mnt_type && mnt_dev ) {
Packit dd8086
	if ( strcmp(mnt_type, "iso9660") == 0 ) {
Packit dd8086
	  if (is_cdrom_linux(mnt_dev, mnt_type) > 0) {
Packit dd8086
	    free(mnt_type);
Packit dd8086
	    endmntent(mntfp);
Packit dd8086
	    return mnt_dev;
Packit dd8086
	  }
Packit dd8086
        }
Packit dd8086
      }
Packit dd8086
      free(mnt_dev);
Packit dd8086
      free(mnt_type);
Packit dd8086
    }
Packit dd8086
    endmntent(mntfp);
Packit dd8086
  }
Packit dd8086
  return NULL;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Get the volume of an audio CD.
Packit dd8086
Packit dd8086
  @param p_cdio the CD object to be acted upon.
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
audio_get_volume_linux (void *p_user_data,
Packit dd8086
                        /*out*/ cdio_audio_volume_t *p_volume)
Packit dd8086
{
Packit dd8086
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  return ioctl(p_env->gen.fd, CDROMVOLREAD, p_volume);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Pause playing CD through analog output
Packit dd8086
Packit dd8086
  @param p_cdio the CD object to be acted upon.
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
audio_pause_linux (void *p_user_data)
Packit dd8086
{
Packit dd8086
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  return ioctl(p_env->gen.fd, CDROMPAUSE);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Playing starting at given MSF through analog output
Packit dd8086
Packit dd8086
  @param p_cdio the CD object to be acted upon.
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
audio_play_msf_linux (void *p_user_data, msf_t *p_start_msf, msf_t *p_end_msf)
Packit dd8086
{
Packit dd8086
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  struct cdrom_msf cdrom_msf;
Packit dd8086
Packit dd8086
  cdrom_msf.cdmsf_min0   = cdio_from_bcd8(p_start_msf->m);
Packit dd8086
  cdrom_msf.cdmsf_sec0   = cdio_from_bcd8(p_start_msf->s);
Packit dd8086
  cdrom_msf.cdmsf_frame0 = cdio_from_bcd8(p_start_msf->f);
Packit dd8086
Packit dd8086
  cdrom_msf.cdmsf_min1   = cdio_from_bcd8(p_end_msf->m);
Packit dd8086
  cdrom_msf.cdmsf_sec1   = cdio_from_bcd8(p_end_msf->s);
Packit dd8086
  cdrom_msf.cdmsf_frame1 = cdio_from_bcd8(p_end_msf->f);
Packit dd8086
Packit dd8086
  return ioctl(p_env->gen.fd, CDROMPLAYMSF, &cdrom_msf);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Playing CD through analog output at the desired track and index
Packit dd8086
Packit dd8086
  @param p_cdio the CD object to be acted upon.
Packit dd8086
  @param p_track_index location to start/end.
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
audio_play_track_index_linux (void *p_user_data,
Packit dd8086
                              cdio_track_index_t *p_track_index)
Packit dd8086
{
Packit dd8086
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  return ioctl(p_env->gen.fd, CDROMPLAYTRKIND, p_track_index);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Read Audio Subchannel information
Packit dd8086
Packit dd8086
  @param p_user_data the CD object to be acted upon.
Packit dd8086
  @param p_subchannel returned information
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
audio_read_subchannel_linux (void *p_user_data,
Packit dd8086
                             /*out*/ cdio_subchannel_t *p_subchannel)
Packit dd8086
{
Packit dd8086
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  struct cdrom_subchnl subchannel;
Packit dd8086
  int   i_rc;
Packit dd8086
Packit dd8086
  subchannel.cdsc_format = CDIO_CDROM_MSF;
Packit dd8086
  i_rc = ioctl(p_env->gen.fd, CDROMSUBCHNL, &subchannel);
Packit dd8086
  if (0 == i_rc) {
Packit dd8086
    p_subchannel->control      = subchannel.cdsc_ctrl;
Packit dd8086
    p_subchannel->track        = subchannel.cdsc_trk;
Packit dd8086
    p_subchannel->index        = subchannel.cdsc_ind;
Packit dd8086
Packit dd8086
    p_subchannel->abs_addr.m   =
Packit dd8086
      cdio_to_bcd8(subchannel.cdsc_absaddr.msf.minute);
Packit dd8086
    p_subchannel->abs_addr.s   =
Packit dd8086
      cdio_to_bcd8(subchannel.cdsc_absaddr.msf.second);
Packit dd8086
    p_subchannel->abs_addr.f   =
Packit dd8086
      cdio_to_bcd8(subchannel.cdsc_absaddr.msf.frame);
Packit dd8086
    p_subchannel->rel_addr.m   =
Packit dd8086
      cdio_to_bcd8(subchannel.cdsc_reladdr.msf.minute);
Packit dd8086
    p_subchannel->rel_addr.s   =
Packit dd8086
      cdio_to_bcd8(subchannel.cdsc_reladdr.msf.second);
Packit dd8086
    p_subchannel->rel_addr.f   =
Packit dd8086
      cdio_to_bcd8(subchannel.cdsc_reladdr.msf.frame);
Packit dd8086
    p_subchannel->audio_status = subchannel.cdsc_audiostatus;
Packit dd8086
Packit dd8086
    return DRIVER_OP_SUCCESS;
Packit dd8086
  } else {
Packit dd8086
    cdio_info ("ioctl CDROMSUBCHNL failed: %s\n", strerror(errno));
Packit dd8086
    return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Resume playing an audio CD.
Packit dd8086
Packit dd8086
  @param p_cdio the CD object to be acted upon.
Packit dd8086
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
audio_resume_linux (void *p_user_data)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  return ioctl(p_env->gen.fd, CDROMRESUME, 0);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Set the volume of an audio CD.
Packit dd8086
Packit dd8086
  @param p_user_data the CD object to be acted upon.
Packit dd8086
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
audio_set_volume_linux (void *p_user_data, cdio_audio_volume_t *p_volume)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  return ioctl(p_env->gen.fd, CDROMVOLCTRL, p_volume);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Stop playing an audio CD.
Packit dd8086
Packit dd8086
  @param p_user_data the CD object to be acted upon.
Packit dd8086
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
audio_stop_linux (void *p_user_data)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  return ioctl(p_env->gen.fd, CDROMSTOP);
Packit dd8086
}
Packit dd8086
Packit dd8086
static bool
Packit dd8086
is_mmc_supported(void *user_data)
Packit dd8086
{
Packit dd8086
    _img_private_t *env = user_data;
Packit dd8086
    return (_AM_NONE == env->access_mode) ? false : true;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the value associated with the key "arg".
Packit dd8086
*/
Packit dd8086
static const char *
Packit dd8086
get_arg_linux (void *env, const char key[])
Packit dd8086
{
Packit dd8086
  _img_private_t *_obj = env;
Packit dd8086
Packit dd8086
  if (!strcmp (key, "source")) {
Packit dd8086
    return _obj->gen.source_name;
Packit dd8086
  } else if (!strcmp (key, "access-mode")) {
Packit dd8086
    switch (_obj->access_mode) {
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      return "IOCTL";
Packit dd8086
    case _AM_READ_CD:
Packit dd8086
      return "READ_CD";
Packit dd8086
    case _AM_READ_10:
Packit dd8086
      return "READ_10";
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
      return "MMC_RDWR";
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
Packit dd8086
      return "MMC_RDWR_EXCL";
Packit dd8086
    case _AM_NONE:
Packit dd8086
      return "no access method";
Packit dd8086
    }
Packit dd8086
  } else if (!strcmp (key, "scsi-tuple")) {
Packit dd8086
    return _obj->gen.scsi_tuple;
Packit dd8086
  } else if (!strcmp (key, "mmc-supported?")) {
Packit dd8086
      return is_mmc_supported(env) ? "true" : "false";
Packit dd8086
  }
Packit dd8086
  return NULL;
Packit dd8086
}
Packit dd8086
Packit dd8086
#undef USE_LINUX_CAP
Packit dd8086
#ifdef USE_LINUX_CAP
Packit dd8086
/*!
Packit dd8086
  Return the the kind of drive capabilities of device.
Packit dd8086
Packit dd8086
  Note: string is malloc'd so caller should free() then returned
Packit dd8086
  string when done with it.
Packit dd8086
Packit dd8086
 */
Packit dd8086
static void
Packit dd8086
get_drive_cap_linux (const void *p_user_data,
Packit dd8086
                     /*out*/ cdio_drive_read_cap_t  *p_read_cap,
Packit dd8086
                     /*out*/ cdio_drive_write_cap_t *p_write_cap,
Packit dd8086
                     /*out*/ cdio_drive_misc_cap_t  *p_misc_cap)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  int32_t i_drivetype;
Packit dd8086
Packit dd8086
  i_drivetype = ioctl (p_env->gen.fd, CDROM_GET_CAPABILITY, CDSL_CURRENT);
Packit dd8086
Packit dd8086
  if (i_drivetype < 0) {
Packit dd8086
    *p_read_cap  = CDIO_DRIVE_CAP_ERROR;
Packit dd8086
    *p_write_cap = CDIO_DRIVE_CAP_ERROR;
Packit dd8086
    *p_misc_cap  = CDIO_DRIVE_CAP_ERROR;
Packit dd8086
    return;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  *p_read_cap  = 0;
Packit dd8086
  *p_write_cap = 0;
Packit dd8086
  *p_misc_cap  = 0;
Packit dd8086
Packit dd8086
  /* Reader */
Packit dd8086
  if (i_drivetype & CDC_PLAY_AUDIO)
Packit dd8086
    *p_read_cap  |= CDIO_DRIVE_CAP_READ_AUDIO;
Packit dd8086
  if (i_drivetype & CDC_CD_R)
Packit dd8086
    *p_read_cap  |= CDIO_DRIVE_CAP_READ_CD_R;
Packit dd8086
  if (i_drivetype & CDC_CD_RW)
Packit dd8086
    *p_read_cap  |= CDIO_DRIVE_CAP_READ_CD_RW;
Packit dd8086
  if (i_drivetype & CDC_DVD)
Packit dd8086
    *p_read_cap  |= CDIO_DRIVE_CAP_READ_DVD_ROM;
Packit dd8086
Packit dd8086
  /* Writer */
Packit dd8086
  if (i_drivetype & CDC_CD_RW)
Packit dd8086
    *p_read_cap  |= CDIO_DRIVE_CAP_WRITE_CD_RW;
Packit dd8086
  if (i_drivetype & CDC_DVD_R)
Packit dd8086
    *p_read_cap  |= CDIO_DRIVE_CAP_WRITE_DVD_R;
Packit dd8086
  if (i_drivetype & CDC_DVD_RAM)
Packit dd8086
    *p_read_cap  |= CDIO_DRIVE_CAP_WRITE_DVD_RAM;
Packit dd8086
Packit dd8086
  /* Misc */
Packit dd8086
  if (i_drivetype & CDC_CLOSE_TRAY)
Packit dd8086
    *p_misc_cap  |= CDIO_DRIVE_CAP_MISC_CLOSE_TRAY;
Packit dd8086
  if (i_drivetype & CDC_OPEN_TRAY)
Packit dd8086
    *p_misc_cap  |= CDIO_DRIVE_CAP_MISC_EJECT;
Packit dd8086
  if (i_drivetype & CDC_LOCK)
Packit dd8086
    *p_misc_cap  |= CDIO_DRIVE_CAP_MISC_LOCK;
Packit dd8086
  if (i_drivetype & CDC_SELECT_SPEED)
Packit dd8086
    *p_misc_cap  |= CDIO_DRIVE_CAP_MISC_SELECT_SPEED;
Packit dd8086
  if (i_drivetype & CDC_SELECT_DISC)
Packit dd8086
    *p_misc_cap  |= CDIO_DRIVE_CAP_MISC_SELECT_DISC;
Packit dd8086
  if (i_drivetype & CDC_MULTI_SESSION)
Packit dd8086
    *p_misc_cap  |= CDIO_DRIVE_CAP_MISC_MULTI_SESSION;
Packit dd8086
  if (i_drivetype & CDC_MEDIA_CHANGED)
Packit dd8086
    *p_misc_cap  |= CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED;
Packit dd8086
  if (i_drivetype & CDC_RESET)
Packit dd8086
    *p_misc_cap  |= CDIO_DRIVE_CAP_MISC_RESET;
Packit dd8086
}
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/*! Get the LSN of the first track of the last session of
Packit dd8086
  on the CD.
Packit dd8086
Packit dd8086
  @param p_cdio the CD object to be acted upon.
Packit dd8086
  @param i_last_session pointer to the session number to be returned.
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
get_last_session_linux (void *p_user_data,
Packit dd8086
                        /*out*/ lsn_t *i_last_session)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  struct cdrom_multisession ms;
Packit dd8086
  int i_rc;
Packit dd8086
Packit dd8086
  ms.addr_format = CDROM_LBA;
Packit dd8086
  i_rc = ioctl(p_env->gen.fd, CDROMMULTISESSION, &ms);
Packit dd8086
  if (0 == i_rc) {
Packit dd8086
    *i_last_session = ms.addr.lba;
Packit dd8086
    return DRIVER_OP_SUCCESS;
Packit dd8086
  } else {
Packit dd8086
    cdio_warn ("ioctl CDROMMULTISESSION failed: %s\n", strerror(errno));
Packit dd8086
    return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Find out if media has changed since the last call.
Packit dd8086
  @param p_user_data the environment object to be acted upon.
Packit dd8086
  @return 1 if media has changed since last call, 0 if not. Error
Packit dd8086
  return codes are the same as driver_return_code_t
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
get_media_changed_linux (const void *p_user_data) {
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  return ioctl(p_env->gen.fd, CDROM_MEDIA_CHANGED, 0);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the media catalog number MCN.
Packit dd8086
Packit dd8086
  Note: string is malloc'd so caller should free() then returned
Packit dd8086
  string when done with it.
Packit dd8086
Packit dd8086
 */
Packit dd8086
static char *
Packit dd8086
get_mcn_linux (const void *p_user_data) {
Packit dd8086
Packit dd8086
  struct cdrom_mcn mcn;
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  memset(&mcn, 0, sizeof(mcn));
Packit dd8086
  if (ioctl(p_env->gen.fd, CDROM_GET_MCN, &mcn) != 0)
Packit dd8086
    return NULL;
Packit dd8086
  return strdup((char *)mcn.medium_catalog_number);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the international standard recording code ISRC.
Packit dd8086
Packit dd8086
  Note: string is malloc'd so caller should free() then returned
Packit dd8086
  string when done with it.
Packit dd8086
Packit dd8086
 */
Packit dd8086
static char *
Packit dd8086
get_track_isrc_linux (const void *p_user_data, track_t i_track) {
Packit dd8086
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  return mmc_get_track_isrc( p_env->gen.cdio, i_track );
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Get format of track.
Packit dd8086
*/
Packit dd8086
static track_format_t
Packit dd8086
get_track_format_linux(void *p_user_data, track_t i_track)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if ( !p_env ) return TRACK_FORMAT_ERROR;
Packit dd8086
Packit dd8086
  if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ;
Packit dd8086
Packit dd8086
  if (i_track > (p_env->gen.i_tracks+p_env->gen.i_first_track)
Packit dd8086
      || i_track < p_env->gen.i_first_track)
Packit dd8086
    return TRACK_FORMAT_ERROR;
Packit dd8086
Packit dd8086
  i_track -= p_env->gen.i_first_track;
Packit dd8086
Packit dd8086
  /* This is pretty much copied from the "badly broken" cdrom_count_tracks
Packit dd8086
     in linux/cdrom.c.
Packit dd8086
   */
Packit dd8086
  if (p_env->tocent[i_track].cdte_ctrl & CDIO_CDROM_DATA_TRACK) {
Packit dd8086
    if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_CDI_TRACK)
Packit dd8086
      return TRACK_FORMAT_CDI;
Packit dd8086
    else if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_XA_TRACK)
Packit dd8086
      return TRACK_FORMAT_XA;
Packit dd8086
    else
Packit dd8086
      return TRACK_FORMAT_DATA;
Packit dd8086
  } else
Packit dd8086
    return TRACK_FORMAT_AUDIO;
Packit dd8086
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return true if we have XA data (green, mode2 form1) or
Packit dd8086
  XA data (green, mode2 form2). That is track begins:
Packit dd8086
  sync - header - subheader
Packit dd8086
  12     4      -  8
Packit dd8086
Packit dd8086
  FIXME: there's gotta be a better design for this and get_track_format?
Packit dd8086
*/
Packit dd8086
static bool
Packit dd8086
get_track_green_linux(void *p_user_data, track_t i_track)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ;
Packit dd8086
Packit dd8086
  if (i_track >= (p_env->gen.i_tracks+p_env->gen.i_first_track)
Packit dd8086
      || i_track < p_env->gen.i_first_track)
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  i_track -= p_env->gen.i_first_track;
Packit dd8086
Packit dd8086
  /* FIXME: Dunno if this is the right way, but it's what
Packit dd8086
     I was using in cd-info for a while.
Packit dd8086
   */
Packit dd8086
  return ((p_env->tocent[i_track].cdte_ctrl & 2) != 0);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the starting MSF (minutes/secs/frames) for track number
Packit dd8086
  track_num in obj.  Track numbers usually start at something
Packit dd8086
  greater than 0, usually 1.
Packit dd8086
Packit dd8086
  The "leadout" track is specified either by
Packit dd8086
  using i_track LEADOUT_TRACK or the total tracks+1.
Packit dd8086
  False is returned if there is no track entry.
Packit dd8086
*/
Packit dd8086
static bool
Packit dd8086
get_track_msf_linux(void *p_user_data, track_t i_track, msf_t *msf)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if (NULL == msf ||
Packit dd8086
      (i_track > CDIO_CD_MAX_TRACKS && i_track != CDIO_CDROM_LEADOUT_TRACK))
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ;
Packit dd8086
Packit dd8086
  if (i_track == CDIO_CDROM_LEADOUT_TRACK)
Packit dd8086
    i_track = p_env->gen.i_tracks + p_env->gen.i_first_track;
Packit dd8086
Packit dd8086
  if (i_track > (p_env->gen.i_tracks+p_env->gen.i_first_track)
Packit dd8086
      || i_track < p_env->gen.i_first_track) {
Packit dd8086
    return false;
Packit dd8086
  } else {
Packit dd8086
    struct cdrom_msf0  *msf0=
Packit dd8086
      &p_env->tocent[i_track-p_env->gen.i_first_track].cdte_addr.msf;
Packit dd8086
    msf->m = cdio_to_bcd8(msf0->minute);
Packit dd8086
    msf->s = cdio_to_bcd8(msf0->second);
Packit dd8086
    msf->f = cdio_to_bcd8(msf0->frame);
Packit dd8086
    return true;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Check, if a device is mounted and return the target (=mountpoint)
Packit dd8086
  needed for umounting (idea taken from libunieject).
Packit dd8086
 */
Packit dd8086
Packit dd8086
static int is_mounted (const char * device, char * target) {
Packit dd8086
  FILE * fp;
Packit dd8086
  char real_device_1[PATH_MAX];
Packit dd8086
  char real_device_2[PATH_MAX];
Packit dd8086
Packit dd8086
  char file_device[PATH_MAX];
Packit dd8086
  char file_target[PATH_MAX];
Packit dd8086
Packit dd8086
  fp = fopen ( "/proc/mounts", "r");
Packit dd8086
  /* Older systems just have /etc/mtab */
Packit dd8086
  if(!fp)
Packit dd8086
    fp = fopen ( "/etc/mtab", "r");
Packit dd8086
Packit dd8086
  /* Neither /proc/mounts nor /etc/mtab could be opened, give up here */
Packit dd8086
  if(!fp) return 0;
Packit dd8086
Packit dd8086
  /* Get real device */
Packit dd8086
  if (NULL == cdio_realpath(device, real_device_1)) {
Packit dd8086
    cdio_warn("Problems resolving device %s: %s\n", device, strerror(errno));
Packit dd8086
  }
Packit dd8086
Packit dd8086
Packit dd8086
  /* Read entries */
Packit dd8086
Packit dd8086
  while ( fscanf(fp, "%s %s %*s %*s %*d %*d\n", file_device, file_target) != EOF ) {
Packit dd8086
      if (NULL == cdio_realpath(file_device, real_device_2)) {
Packit dd8086
          cdio_debug("Problems resolving device %s: %s\n",
Packit dd8086
                     file_device, strerror(errno));
Packit dd8086
      }
Packit dd8086
    if(!strcmp(real_device_1, real_device_2)) {
Packit dd8086
      strcpy(target, file_target);
Packit dd8086
      fclose(fp);
Packit dd8086
      return 1;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  }
Packit dd8086
  fclose(fp);
Packit dd8086
  return 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Umount a filesystem specified by it's mountpoint. We must do this
Packit dd8086
  by forking and calling the umount command, because the raw umount
Packit dd8086
  (or umount2) system calls will *always* trigger an EPERM even if
Packit dd8086
  we are allowed to umount the filesystem. The umount command is
Packit dd8086
  suid root.
Packit dd8086
Packit dd8086
  Code here is inspired by the standard linux eject command by
Packit dd8086
  Jeff Tranter and Frank Lichtenheld.
Packit dd8086
 */
Packit dd8086
Packit dd8086
static int do_umount(char * target) {
Packit dd8086
  int status;
Packit dd8086
Packit dd8086
  switch (fork()) {
Packit dd8086
  case 0: /* child */
Packit dd8086
    execlp("pumount", "pumount", target, NULL);
Packit dd8086
    execlp("umount", "umount", target, NULL);
Packit dd8086
    return -1;
Packit dd8086
  case -1:
Packit dd8086
    return -1;
Packit dd8086
  default: /* parent */
Packit dd8086
    wait(&status);
Packit dd8086
    if (WIFEXITED(status) == 0) {
Packit dd8086
      return -1;
Packit dd8086
    }
Packit dd8086
    if (WEXITSTATUS(status) != 0) {
Packit dd8086
      return -1;
Packit dd8086
    }
Packit dd8086
    break;
Packit dd8086
  }
Packit dd8086
  return 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Eject media in CD-ROM drive. Return DRIVER_OP_SUCCESS if successful,
Packit dd8086
  DRIVER_OP_ERROR on error.
Packit dd8086
 */
Packit dd8086
Packit dd8086
static driver_return_code_t
Packit dd8086
eject_media_linux (void *p_user_data) {
Packit dd8086
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  driver_return_code_t ret=DRIVER_OP_SUCCESS;
Packit dd8086
  int status;
Packit dd8086
  bool was_open = false;
Packit dd8086
  char mount_target[PATH_MAX];
Packit dd8086
Packit dd8086
  /* Make sure the device is opened in read/write mode. */
Packit dd8086
  if ( p_env->gen.fd >= 0 ) {
Packit dd8086
    close(p_env->gen.fd);
Packit dd8086
    was_open = true;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  p_env->gen.fd = open (p_env->gen.source_name, O_RDWR|O_NONBLOCK);
Packit dd8086
Packit dd8086
  if ( p_env->gen.fd <= -1 ) return DRIVER_OP_ERROR;
Packit dd8086
Packit dd8086
  if ((status = ioctl(p_env->gen.fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) > 0) {
Packit dd8086
    switch(status) {
Packit dd8086
    case CDS_TRAY_OPEN:
Packit dd8086
      cdio_info ("Drive status reports that tray is open\n");
Packit dd8086
      break;
Packit dd8086
    default:
Packit dd8086
      cdio_info ("Unknown state of CD-ROM (%d)\n", status);
Packit dd8086
      /* Fall through */
Packit dd8086
    case CDS_DISC_OK:
Packit dd8086
      /* Some systems automount the drive, so we must umount it.
Packit dd8086
         We check if the drive is actually mounted */
Packit dd8086
      if(is_mounted (p_env->gen.source_name, mount_target)) {
Packit dd8086
        /* Try to umount the drive */
Packit dd8086
        if(do_umount(mount_target)) {
Packit dd8086
          cdio_log(CDIO_LOG_WARN, "Could not umount %s\n",
Packit dd8086
                   p_env->gen.source_name);
Packit dd8086
          ret=DRIVER_OP_ERROR;
Packit dd8086
          break;
Packit dd8086
        }
Packit dd8086
        /* For some reason, we must close and reopen the device after
Packit dd8086
           it got umounted (at least the commandline eject program
Packit dd8086
           opens the device just after umounting it) */
Packit dd8086
        close(p_env->gen.fd);
Packit dd8086
        p_env->gen.fd = open (p_env->gen.source_name, O_RDWR|O_NONBLOCK);
Packit dd8086
      }
Packit dd8086
Packit dd8086
      if((ret = ioctl(p_env->gen.fd, CDROMEJECT)) != 0) {
Packit dd8086
        int eject_error = errno;
Packit dd8086
        /* Try ejecting the MMC way... */
Packit dd8086
        ret = mmc_eject_media(p_env->gen.cdio);
Packit dd8086
        if (0 != ret) {
Packit dd8086
          cdio_info("ioctl CDROMEJECT and MMC eject failed: %s",
Packit dd8086
                    strerror(eject_error));
Packit dd8086
          ret = DRIVER_OP_ERROR;
Packit dd8086
        }
Packit dd8086
      }
Packit dd8086
      /* force kernel to reread partition table when new disc inserted */
Packit dd8086
      if (0 != ioctl(p_env->gen.fd, BLKRRPART)) {
Packit dd8086
        cdio_info ("BLKRRPART request failed: %s\n", strerror(errno));
Packit dd8086
      }
Packit dd8086
      break;
Packit dd8086
    }
Packit dd8086
  } else {
Packit dd8086
    cdio_warn ("CDROM_DRIVE_STATUS failed: %s\n", strerror(errno));
Packit dd8086
    ret=DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
  if(!was_open) {
Packit dd8086
    close(p_env->gen.fd);
Packit dd8086
    p_env->gen.fd = -1;
Packit dd8086
  }
Packit dd8086
  return ret;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Get disc type associated with cd object.
Packit dd8086
*/
Packit dd8086
static discmode_t
Packit dd8086
dvd_discmode_linux (_img_private_t *p_env)
Packit dd8086
{
Packit dd8086
  discmode_t discmode=CDIO_DISC_MODE_NO_INFO;
Packit dd8086
Packit dd8086
  /* See if this is a DVD. */
Packit dd8086
  cdio_dvd_struct_t dvd;  /* DVD READ STRUCT for layer 0. */
Packit dd8086
Packit dd8086
  memset(&dvd, 0, sizeof(dvd));
Packit dd8086
Packit dd8086
  dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL;
Packit dd8086
  dvd.physical.layer_num = 0;
Packit dd8086
  if (0 == ioctl (p_env->gen.fd, DVD_READ_STRUCT, &dvd)) {
Packit dd8086
    switch(dvd.physical.layer[0].book_type) {
Packit dd8086
    case CDIO_DVD_BOOK_DVD_ROM:  return CDIO_DISC_MODE_DVD_ROM;
Packit dd8086
    case CDIO_DVD_BOOK_DVD_RAM:  return CDIO_DISC_MODE_DVD_RAM;
Packit dd8086
    case CDIO_DVD_BOOK_DVD_R:    return CDIO_DISC_MODE_DVD_R;
Packit dd8086
    case CDIO_DVD_BOOK_DVD_RW:   return CDIO_DISC_MODE_DVD_RW;
Packit dd8086
    case CDIO_DVD_BOOK_DVD_PR:   return CDIO_DISC_MODE_DVD_PR;
Packit dd8086
    case CDIO_DVD_BOOK_DVD_PRW:  return CDIO_DISC_MODE_DVD_PRW;
Packit dd8086
    default:                     return CDIO_DISC_MODE_DVD_OTHER;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  return discmode;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Get disc type associated with the cd object.
Packit dd8086
*/
Packit dd8086
static discmode_t
Packit dd8086
get_discmode_linux (void *p_user_data)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  discmode_t discmode;
Packit dd8086
Packit dd8086
  if (!p_env) return CDIO_DISC_MODE_ERROR;
Packit dd8086
Packit dd8086
  /* Try DVD types first. See note below. */
Packit dd8086
  discmode = dvd_discmode_linux(p_env);
Packit dd8086
Packit dd8086
  if (CDIO_DISC_MODE_NO_INFO != discmode) return discmode;
Packit dd8086
  /*
Packit dd8086
     Justin B Ruggles <jruggle@earthlink.net> reports that the
Packit dd8086
     GNU/Linux ioctl(.., CDROM_DISC_STATUS) does not return "CD DATA
Packit dd8086
     Form 2" for SVCD's even though they are are form 2.  In
Packit dd8086
     mmc_get_discmode we issue a SCSI MMC-2 TOC command first to
Packit dd8086
     try get more accurate information. But we took care above *not*
Packit dd8086
     to issue a FULL TOC on DVD media.
Packit dd8086
  */
Packit dd8086
  discmode = mmc_get_discmode(p_env->gen.cdio);
Packit dd8086
  if (CDIO_DISC_MODE_NO_INFO != discmode)
Packit dd8086
    return discmode;
Packit dd8086
  else {
Packit dd8086
    int32_t i_discmode = ioctl (p_env->gen.fd, CDROM_DISC_STATUS);
Packit dd8086
Packit dd8086
    if (i_discmode < 0) return CDIO_DISC_MODE_ERROR;
Packit dd8086
Packit dd8086
    switch(i_discmode) {
Packit dd8086
    case CDS_AUDIO:
Packit dd8086
      return CDIO_DISC_MODE_CD_DA;
Packit dd8086
    case CDS_DATA_1:
Packit dd8086
    case CDS_DATA_2: /* Actually, recent GNU/Linux kernels don't return
Packit dd8086
                        CDS_DATA_2, but just in case. */
Packit dd8086
      return CDIO_DISC_MODE_CD_DATA;
Packit dd8086
    case CDS_MIXED:
Packit dd8086
      return CDIO_DISC_MODE_CD_MIXED;
Packit dd8086
    case CDS_XA_2_1:
Packit dd8086
    case CDS_XA_2_2:
Packit dd8086
      return CDIO_DISC_MODE_CD_XA;
Packit dd8086
    case CDS_NO_INFO:
Packit dd8086
      return CDIO_DISC_MODE_NO_INFO;
Packit dd8086
    default:
Packit dd8086
      return CDIO_DISC_MODE_ERROR;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/* Check a drive to see if it is a CD-ROM
Packit dd8086
   Return 1 if a CD-ROM. 0 if it exists but isn't a CD-ROM drive
Packit dd8086
   and -1 if no device exists .
Packit dd8086
*/
Packit dd8086
static bool
Packit dd8086
is_cdrom_linux(const char *drive, char *mnttype)
Packit dd8086
{
Packit dd8086
  bool is_cd=false;
Packit dd8086
  int cdfd;
Packit dd8086
Packit dd8086
  /* If it doesn't exist, return -1 */
Packit dd8086
  if ( !cdio_is_device_quiet_generic(drive) ) {
Packit dd8086
    return(false);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* If it does exist, verify that it's an available CD-ROM */
Packit dd8086
  cdfd = open(drive, (O_RDONLY|O_NONBLOCK), 0);
Packit dd8086
  if ( cdfd >= 0 ) {
Packit dd8086
    if ( ioctl(cdfd, CDROM_GET_CAPABILITY, 0) != -1 ) {
Packit dd8086
      is_cd = true;
Packit dd8086
    }
Packit dd8086
    close(cdfd);
Packit dd8086
    }
Packit dd8086
  /* Even if we can't read it, it might be mounted */
Packit dd8086
  else if ( mnttype && (strcmp(mnttype, "iso9660") == 0) ) {
Packit dd8086
    is_cd = true;
Packit dd8086
  }
Packit dd8086
  return(is_cd);
Packit dd8086
}
Packit dd8086
Packit dd8086
/* MMC driver to read audio sectors.
Packit dd8086
   Can read only up to 25 blocks.
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
read_audio_sectors_linux (void *p_user_data, void *p_buf, lsn_t i_lsn,
Packit dd8086
                           uint32_t i_blocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  return mmc_read_sectors( p_env->gen.cdio, p_buf, i_lsn,
Packit dd8086
                           CDIO_MMC_READ_TYPE_CDDA, i_blocks);
Packit dd8086
}
Packit dd8086
Packit dd8086
/* Packet driver to read mode2 sectors.
Packit dd8086
   Can read only up to 25 blocks.
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
_read_mode2_sectors_mmc (_img_private_t *p_env, void *p_buf, lba_t lba,
Packit dd8086
                         uint32_t i_blocks, bool b_read_10)
Packit dd8086
{
Packit dd8086
  mmc_cdb_t cdb = {{0, }};
Packit dd8086
Packit dd8086
  CDIO_MMC_SET_READ_LBA(cdb.field, lba);
Packit dd8086
Packit dd8086
  if (b_read_10) {
Packit dd8086
    int retval;
Packit dd8086
Packit dd8086
    CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_10);
Packit dd8086
    CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_blocks);
Packit dd8086
Packit dd8086
    if ((retval = mmc_set_blocksize (p_env->gen.cdio, M2RAW_SECTOR_SIZE)))
Packit dd8086
      return retval;
Packit dd8086
Packit dd8086
    if ((retval = run_mmc_cmd_linux (p_env, 0,
Packit dd8086
                                     mmc_get_cmd_len(cdb.field[0]),
Packit dd8086
                                     &cdb,
Packit dd8086
                                     SCSI_MMC_DATA_READ,
Packit dd8086
                                     M2RAW_SECTOR_SIZE * i_blocks,
Packit dd8086
                                     p_buf)))
Packit dd8086
      {
Packit dd8086
        mmc_set_blocksize (p_env->gen.cdio, CDIO_CD_FRAMESIZE);
Packit dd8086
        return retval;
Packit dd8086
      }
Packit dd8086
Packit dd8086
    /* Restore blocksize. */
Packit dd8086
    retval = mmc_set_blocksize (p_env->gen.cdio, CDIO_CD_FRAMESIZE);
Packit dd8086
    return retval;
Packit dd8086
  } else {
Packit dd8086
Packit dd8086
    cdb.field[1] = 0; /* sector size mode2 */
Packit dd8086
    cdb.field[9] = 0x58; /* 2336 mode2 */
Packit dd8086
Packit dd8086
    CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD);
Packit dd8086
    CDIO_MMC_SET_READ_LENGTH24(cdb.field, i_blocks);
Packit dd8086
Packit dd8086
    return run_mmc_cmd_linux (p_env, 0,
Packit dd8086
                              mmc_get_cmd_len(cdb.field[0]), &cdb,
Packit dd8086
                              SCSI_MMC_DATA_READ,
Packit dd8086
                              M2RAW_SECTOR_SIZE * i_blocks, p_buf);
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
static driver_return_code_t
Packit dd8086
_read_mode2_sectors (_img_private_t *p_env, void *p_buf, lba_t lba,
Packit dd8086
                     uint32_t i_blocks, bool b_read_10)
Packit dd8086
{
Packit dd8086
  unsigned int l = 0;
Packit dd8086
  int retval = 0;
Packit dd8086
Packit dd8086
  while (i_blocks > 0)
Packit dd8086
    {
Packit dd8086
      const unsigned i_blocks2 = (i_blocks > 25) ? 25 : i_blocks;
Packit dd8086
      void *p_buf2 = ((char *)p_buf ) + (l * M2RAW_SECTOR_SIZE);
Packit dd8086
Packit dd8086
      retval |= _read_mode2_sectors_mmc (p_env, p_buf2, lba + l,
Packit dd8086
                                         i_blocks2, b_read_10);
Packit dd8086
Packit dd8086
      if (retval)
Packit dd8086
        break;
Packit dd8086
Packit dd8086
      i_blocks -= i_blocks2;
Packit dd8086
      l += i_blocks2;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  return retval;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads a single mode1 sector from cd device into data starting
Packit dd8086
   from lsn. Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
_read_mode1_sector_linux (void *p_user_data, void *p_data, lsn_t lsn,
Packit dd8086
                          bool b_form2)
Packit dd8086
{
Packit dd8086
Packit dd8086
#if 0
Packit dd8086
  char buf[M2RAW_SECTOR_SIZE] = { 0, };
Packit dd8086
  struct cdrom_msf *p_msf = (struct cdrom_msf *) &buf;
Packit dd8086
  msf_t _msf;
Packit dd8086
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  cdio_lsn_to_msf (lsn, &_msf);
Packit dd8086
  p_msf->cdmsf_min0 = cdio_from_bcd8(_msf.m);
Packit dd8086
  p_msf->cdmsf_sec0 = cdio_from_bcd8(_msf.s);
Packit dd8086
  p_msf->cdmsf_frame0 = cdio_from_bcd8(_msf.f);
Packit dd8086
Packit dd8086
 retry:
Packit dd8086
  switch (p_env->access_mode)
Packit dd8086
    {
Packit dd8086
    case _AM_NONE:
Packit dd8086
      cdio_warn ("no way to read mode1");
Packit dd8086
      return 1;
Packit dd8086
      break;
Packit dd8086
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      if (ioctl (p_env->gen.fd, CDROMREADMODE1, &buf) == -1)
Packit dd8086
        {
Packit dd8086
          perror ("ioctl()");
Packit dd8086
          return 1;
Packit dd8086
          /* exit (EXIT_FAILURE); */
Packit dd8086
        }
Packit dd8086
      break;
Packit dd8086
Packit dd8086
    case _AM_READ_CD:
Packit dd8086
    case _AM_READ_10:
Packit dd8086
      if (_read_mode2_sectors (p_env, buf, lsn, 1,
Packit dd8086
                               (p_env->access_mode == _AM_READ_10)))
Packit dd8086
        {
Packit dd8086
          perror ("ioctl()");
Packit dd8086
          if (p_env->access_mode == _AM_READ_CD)
Packit dd8086
            {
Packit dd8086
              cdio_info ("READ_CD failed; switching to READ_10 mode...");
Packit dd8086
              p_env->access_mode = _AM_READ_10;
Packit dd8086
              goto retry;
Packit dd8086
            }
Packit dd8086
          else
Packit dd8086
            {
Packit dd8086
              cdio_info ("READ_10 failed; switching to ioctl(CDROMREADMODE2) mode...");
Packit dd8086
              p_env->access_mode = _AM_IOCTL;
Packit dd8086
              goto retry;
Packit dd8086
            }
Packit dd8086
          return 1;
Packit dd8086
        }
Packit dd8086
      break;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  memcpy (p_data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE,
Packit dd8086
          b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE);
Packit dd8086
Packit dd8086
#else
Packit dd8086
  return cdio_generic_read_form1_sector(p_user_data, p_data, lsn);
Packit dd8086
#endif
Packit dd8086
  return 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads i_blocks of mode2 sectors from cd device into data starting
Packit dd8086
   from lsn.
Packit dd8086
   Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
_read_mode1_sectors_linux (void *p_user_data, void *p_data, lsn_t lsn,
Packit dd8086
                           bool b_form2, uint32_t i_blocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  unsigned int i;
Packit dd8086
  int retval;
Packit dd8086
  unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
Packit dd8086
Packit dd8086
  for (i = 0; i < i_blocks; i++) {
Packit dd8086
    if ( (retval = _read_mode1_sector_linux (p_env,
Packit dd8086
                                            ((char *)p_data) + (blocksize*i),
Packit dd8086
                                            lsn + i, b_form2)) )
Packit dd8086
      return retval;
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads a single mode2 sector from cd device into data starting
Packit dd8086
   from lsn. Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
_read_mode2_sector_linux (void *p_user_data, void *p_data, lsn_t lsn,
Packit dd8086
                          bool b_form2)
Packit dd8086
{
Packit dd8086
  char buf[M2RAW_SECTOR_SIZE] = { 0, };
Packit dd8086
  struct cdrom_msf *msf = (struct cdrom_msf *) &buf;
Packit dd8086
  msf_t _msf;
Packit dd8086
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf);
Packit dd8086
  msf->cdmsf_min0 = cdio_from_bcd8(_msf.m);
Packit dd8086
  msf->cdmsf_sec0 = cdio_from_bcd8(_msf.s);
Packit dd8086
  msf->cdmsf_frame0 = cdio_from_bcd8(_msf.f);
Packit dd8086
Packit dd8086
 retry:
Packit dd8086
  switch (p_env->access_mode)
Packit dd8086
    {
Packit dd8086
    case _AM_NONE:
Packit dd8086
      cdio_warn ("no way to read mode2");
Packit dd8086
      return 1;
Packit dd8086
      break;
Packit dd8086
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
Packit dd8086
      if (ioctl (p_env->gen.fd, CDROMREADMODE2, &buf) == -1)
Packit dd8086
        {
Packit dd8086
          perror ("ioctl()");
Packit dd8086
          return 1;
Packit dd8086
          /* exit (EXIT_FAILURE); */
Packit dd8086
        }
Packit dd8086
      break;
Packit dd8086
Packit dd8086
    case _AM_READ_CD:
Packit dd8086
    case _AM_READ_10:
Packit dd8086
      if (_read_mode2_sectors (p_env, buf, lsn, 1,
Packit dd8086
                               (p_env->access_mode == _AM_READ_10)))
Packit dd8086
        {
Packit dd8086
          perror ("ioctl()");
Packit dd8086
          if (p_env->access_mode == _AM_READ_CD)
Packit dd8086
            {
Packit dd8086
              cdio_info ("READ_CD failed; switching to READ_10 mode...");
Packit dd8086
              p_env->access_mode = _AM_READ_10;
Packit dd8086
              goto retry;
Packit dd8086
            }
Packit dd8086
          else
Packit dd8086
            {
Packit dd8086
              cdio_info ("READ_10 failed; switching to ioctl(CDROMREADMODE2) mode...");
Packit dd8086
              p_env->access_mode = _AM_IOCTL;
Packit dd8086
              goto retry;
Packit dd8086
            }
Packit dd8086
          return 1;
Packit dd8086
        }
Packit dd8086
      break;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  if (b_form2)
Packit dd8086
    memcpy (p_data, buf, M2RAW_SECTOR_SIZE);
Packit dd8086
  else
Packit dd8086
    memcpy (((char *)p_data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE);
Packit dd8086
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads i_blocks of mode2 sectors from cd device into data starting
Packit dd8086
   from lsn.
Packit dd8086
   Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
_read_mode2_sectors_linux (void *p_user_data, void *data, lsn_t lsn,
Packit dd8086
                           bool b_form2, uint32_t i_blocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  unsigned int i;
Packit dd8086
  uint16_t i_blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
Packit dd8086
Packit dd8086
  /* For each frame, pick out the data part we need */
Packit dd8086
  for (i = 0; i < i_blocks; i++) {
Packit dd8086
    int retval;
Packit dd8086
    if ( (retval = _read_mode2_sector_linux (p_env,
Packit dd8086
                                            ((char *)data) + (i_blocksize*i),
Packit dd8086
                                            lsn + i, b_form2)) )
Packit dd8086
      return retval;
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Read and cache the CD's Track Table of Contents and track info.
Packit dd8086
  Return false if successful or true if an error.
Packit dd8086
*/
Packit dd8086
static bool
Packit dd8086
read_toc_linux (void *p_user_data)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  int i;
Packit dd8086
  unsigned int u_tracks;
Packit dd8086
Packit dd8086
  /* read TOC header */
Packit dd8086
  if ( ioctl(p_env->gen.fd, CDROMREADTOCHDR, &p_env->tochdr) == -1 ) {
Packit dd8086
    cdio_warn("%s: %s\n",
Packit dd8086
            "error in ioctl CDROMREADTOCHDR", strerror(errno));
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  p_env->gen.i_first_track = p_env->tochdr.cdth_trk0;
Packit dd8086
  p_env->gen.i_tracks      = p_env->tochdr.cdth_trk1;
Packit dd8086
Packit dd8086
  u_tracks = p_env->gen.i_tracks - p_env->gen.i_first_track + 1;
Packit dd8086
Packit dd8086
  if (u_tracks > CDIO_CD_MAX_TRACKS) {
Packit dd8086
     cdio_log(CDIO_LOG_WARN, "Number of tracks exceeds maximum (%d vs. %d)\n",
Packit dd8086
              u_tracks, CDIO_CD_MAX_TRACKS);
Packit dd8086
     p_env->gen.i_tracks = CDIO_CD_MAX_TRACKS;
Packit dd8086
  }
Packit dd8086
Packit dd8086
Packit dd8086
  /* read individual tracks */
Packit dd8086
  for (i= p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++) {
Packit dd8086
    struct cdrom_tocentry *p_toc =
Packit dd8086
      &(p_env->tocent[i-p_env->gen.i_first_track]);
Packit dd8086
Packit dd8086
    p_toc->cdte_track = i;
Packit dd8086
    p_toc->cdte_format = CDROM_MSF;
Packit dd8086
    if ( ioctl(p_env->gen.fd, CDROMREADTOCENTRY, p_toc) == -1 ) {
Packit dd8086
      cdio_warn("%s %d: %s\n",
Packit dd8086
              "error in ioctl CDROMREADTOCENTRY for track",
Packit dd8086
              i, strerror(errno));
Packit dd8086
      return false;
Packit dd8086
    }
Packit dd8086
Packit dd8086
    set_track_flags(&(p_env->gen.track_flags[i]), p_toc->cdte_ctrl);
Packit dd8086
Packit dd8086
    /****
Packit dd8086
    struct cdrom_msf0 *msf= &env->tocent[i-1].cdte_addr.msf;
Packit dd8086
Packit dd8086
    fprintf (stdout, "--- track# %d (msf %2.2x:%2.2x:%2.2x)\n",
Packit dd8086
             i, msf->minute, msf->second, msf->frame);
Packit dd8086
    ****/
Packit dd8086
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* read the lead-out track */
Packit dd8086
  p_env->tocent[p_env->gen.i_tracks].cdte_track = CDIO_CDROM_LEADOUT_TRACK;
Packit dd8086
  p_env->tocent[p_env->gen.i_tracks].cdte_format = CDROM_MSF;
Packit dd8086
Packit dd8086
  if (ioctl(p_env->gen.fd, CDROMREADTOCENTRY,
Packit dd8086
            &p_env->tocent[p_env->gen.i_tracks]) == -1 ) {
Packit dd8086
    cdio_warn("%s: %s\n",
Packit dd8086
             "error in ioctl CDROMREADTOCENTRY for lead-out",
Packit dd8086
            strerror(errno));
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /*
Packit dd8086
  struct cdrom_msf0 *msf= &env->tocent[p_env->gen.i_tracks].cdte_addr.msf;
Packit dd8086
Packit dd8086
  fprintf (stdout, "--- track# %d (msf %2.2x:%2.2x:%2.2x)\n",
Packit dd8086
           i, msf->minute, msf->second, msf->frame);
Packit dd8086
  */
Packit dd8086
Packit dd8086
  p_env->gen.toc_init = true;
Packit dd8086
  return true;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Run a SCSI MMC command.
Packit dd8086
Packit dd8086
  cdio          CD structure set by cdio_open().
Packit dd8086
  i_timeout     time in milliseconds we will wait for the command
Packit dd8086
                to complete. If this value is -1, use the default
Packit dd8086
                time-out value.
Packit dd8086
  p_buf         Buffer for data, both sending and receiving
Packit dd8086
  i_buf         Size of buffer
Packit dd8086
  e_direction   direction the transfer is to go.
Packit dd8086
  cdb           CDB bytes. All values that are needed should be set on
Packit dd8086
                input. We'll figure out what the right CDB length should be.
Packit dd8086
Packit dd8086
  We return true if command completed successfully and false if not.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
run_mmc_cmd_linux(void *p_user_data,
Packit dd8086
                  unsigned int i_timeout_ms,
Packit dd8086
                  unsigned int i_cdb, const mmc_cdb_t *p_cdb,
Packit dd8086
                  cdio_mmc_direction_t e_direction,
Packit dd8086
                  unsigned int i_buf, /*in/out*/ void *p_buf)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  struct cdrom_generic_command cgc;
Packit dd8086
  cdio_mmc_request_sense_t sense;
Packit dd8086
Packit dd8086
  p_env->gen.scsi_mmc_sense_valid = 0;
Packit dd8086
Packit dd8086
  memset(&cgc, 0, sizeof (struct cdrom_generic_command));
Packit dd8086
  memset(&sense, 0, sizeof (struct cdio_mmc_request_sense));
Packit dd8086
  memcpy(&cgc.cmd, p_cdb, i_cdb);
Packit dd8086
Packit dd8086
  cgc.buflen = i_buf;
Packit dd8086
  cgc.buffer = p_buf;
Packit dd8086
  cgc.sense  = (struct request_sense *) &sense;
Packit dd8086
Packit dd8086
  cgc.data_direction = (SCSI_MMC_DATA_READ  == e_direction) ? CGC_DATA_READ  :
Packit dd8086
                       (SCSI_MMC_DATA_WRITE == e_direction) ? CGC_DATA_WRITE :
Packit dd8086
                       CGC_DATA_NONE;
Packit dd8086
Packit dd8086
#ifdef HAVE_LINUX_CDROM_TIMEOUT
Packit dd8086
  cgc.timeout = i_timeout_ms;
Packit dd8086
#endif
Packit dd8086
Packit dd8086
  {
Packit dd8086
    int i_rc = ioctl (p_env->gen.fd, CDROM_SEND_PACKET, &cgc);
Packit dd8086
Packit dd8086
    /* Record SCSI sense reply for API call mmc_last_cmd_sense().
Packit dd8086
    */
Packit dd8086
    if (sense.additional_sense_len) {
Packit dd8086
        /* SCSI Primary Command standard
Packit dd8086
           SPC 4.5.3, Table 26: 252 bytes legal, 263 bytes possible */
Packit dd8086
        int sense_size = sense.additional_sense_len + 8;
Packit dd8086
Packit dd8086
        if (sense_size > sizeof(sense))
Packit dd8086
            sense_size = sizeof(sense);
Packit dd8086
        memcpy((void *) p_env->gen.scsi_mmc_sense, &sense, sense_size);
Packit dd8086
        p_env->gen.scsi_mmc_sense_valid = sense_size;
Packit dd8086
    }
Packit dd8086
Packit dd8086
    if (0 == i_rc) return DRIVER_OP_SUCCESS;
Packit dd8086
    if (-1 == i_rc) {
Packit dd8086
      cdio_info("ioctl CDROM_SEND_PACKET for command %s (0x%0x) failed:\n\t%s",
Packit dd8086
                mmc_cmd2str((uint8_t) p_cdb->field[0]),
Packit dd8086
                p_cdb->field[0],
Packit dd8086
                strerror(errno));
Packit dd8086
      switch (errno) {
Packit dd8086
      case EPERM:
Packit dd8086
        return DRIVER_OP_NOT_PERMITTED;
Packit dd8086
        break;
Packit dd8086
      case EINVAL:
Packit dd8086
        return DRIVER_OP_BAD_PARAMETER;
Packit dd8086
        break;
Packit dd8086
      case EFAULT:
Packit dd8086
        return DRIVER_OP_BAD_POINTER;
Packit dd8086
        break;
Packit dd8086
      case EIO:
Packit dd8086
      default:
Packit dd8086
        return DRIVER_OP_ERROR;
Packit dd8086
        break;
Packit dd8086
      }
Packit dd8086
    } else if (i_rc < -1)
Packit dd8086
      return DRIVER_OP_ERROR;
Packit dd8086
    else
Packit dd8086
      /*Not sure if this the best thing, but we'll use anyway. */
Packit dd8086
      return DRIVER_OP_SUCCESS;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Return the size of the CD in logical block address (LBA) units.
Packit dd8086
   @return the lsn. On error return CDIO_INVALID_LSN.
Packit dd8086
Packit dd8086
   As of GNU/Linux 2.6, CDROMTOCENTRY gives
Packit dd8086
   ioctl CDROMREADTOCENTRY failed: Invalid argument
Packit dd8086
Packit dd8086
Packit dd8086
   In some cases CDROMREADTOCHDR seems to fix this, but I haven't been
Packit dd8086
   able to find anything that documents this requirement or behavior. It's
Packit dd8086
   not the way CDROMREADTOCHDR works on other 'nixs.
Packit dd8086
Packit dd8086
   Also note that in one at least one test the corresponding MMC gives
Packit dd8086
   a different answer, so there may be some disagreement about what is in
Packit dd8086
   fact the last lsn.
Packit dd8086
 */
Packit dd8086
static lsn_t
Packit dd8086
get_disc_last_lsn_linux (void *p_user_data)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  struct cdrom_tocentry tocent;
Packit dd8086
  uint32_t i_size;
Packit dd8086
Packit dd8086
  if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ;
Packit dd8086
Packit dd8086
  tocent.cdte_track = CDIO_CDROM_LEADOUT_TRACK;
Packit dd8086
  tocent.cdte_format = CDROM_LBA;
Packit dd8086
  if (ioctl (p_env->gen.fd, CDROMREADTOCENTRY, &tocent) == -1)
Packit dd8086
    {
Packit dd8086
      cdio_warn ("ioctl CDROMREADTOCENTRY failed: %s\n", strerror(errno));
Packit dd8086
      return CDIO_INVALID_LSN;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  i_size = tocent.cdte_addr.lba;
Packit dd8086
Packit dd8086
  return i_size;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Set the arg "key" with "value" in the source device.
Packit dd8086
  Currently "source" and "access-mode" are valid keys.
Packit dd8086
  "source" sets the source device in I/O operations
Packit dd8086
  "access-mode" sets the the method of CD access
Packit dd8086
Packit dd8086
  DRIVER_OP_SUCCESS is returned if no error was found,
Packit dd8086
  and nonzero if there as an error.
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
set_arg_linux (void *p_user_data, const char key[], const char value[])
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if (!strcmp (key, "source"))
Packit dd8086
    {
Packit dd8086
      if (!value) return DRIVER_OP_ERROR;
Packit dd8086
      free (p_env->gen.source_name);
Packit dd8086
      p_env->gen.source_name = strdup (value);
Packit dd8086
    }
Packit dd8086
  else if (!strcmp (key, "access-mode"))
Packit dd8086
    {
Packit dd8086
      p_env->access_mode = str_to_access_mode_linux(key);
Packit dd8086
    }
Packit dd8086
  else return DRIVER_OP_ERROR;
Packit dd8086
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
/* checklist: /dev/cdrom, /dev/dvd /dev/hd?, /dev/scd? /dev/sr? */
Packit dd8086
static const char checklist1[][40] = {
Packit dd8086
  {"cdrom"}, {"dvd"}
Packit dd8086
};
Packit dd8086
static const int checklist1_size = sizeof(checklist1) / sizeof(checklist1[0]);
Packit dd8086
Packit dd8086
static const struct
Packit dd8086
  {
Packit dd8086
    char format[22];
Packit dd8086
    int num_min;
Packit dd8086
    int num_max;
Packit dd8086
  }
Packit dd8086
checklist2[] =
Packit dd8086
  {
Packit dd8086
    { "/dev/hd%c",  'a', 'z' },
Packit dd8086
    { "/dev/scd%d", 0,   27 },
Packit dd8086
    { "/dev/sr%d",  0,   27 },
Packit dd8086
  };
Packit dd8086
static const int checklist2_size = sizeof(checklist2) / sizeof(checklist2[0]);
Packit dd8086
Packit dd8086
Packit dd8086
/* Set CD-ROM drive speed */
Packit dd8086
static driver_return_code_t
Packit dd8086
set_speed_linux (void *p_user_data, int i_drive_speed)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if (!p_env) return DRIVER_OP_UNINIT;
Packit dd8086
  return ioctl(p_env->gen.fd, CDROM_SELECT_SPEED, i_drive_speed);
Packit dd8086
}
Packit dd8086
Packit dd8086
#endif /* HAVE_LINUX_CDROM */
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return an array of strings giving possible CD devices.
Packit dd8086
 */
Packit dd8086
char **
Packit dd8086
cdio_get_devices_linux (void)
Packit dd8086
{
Packit dd8086
#ifndef HAVE_LINUX_CDROM
Packit dd8086
  return NULL;
Packit dd8086
#else
Packit dd8086
  unsigned int i;
Packit dd8086
  char drive[40];
Packit dd8086
  char *ret_drive;
Packit dd8086
  char **drives = NULL;
Packit dd8086
  unsigned int num_drives=0;
Packit dd8086
Packit dd8086
  /* Scan the system for CD-ROM drives.
Packit dd8086
  */
Packit dd8086
  for ( i=0; i < checklist1_size; ++i ) {
Packit dd8086
    if (snprintf(drive, sizeof(drive), "/dev/%s", checklist1[i]) < 0)
Packit dd8086
      continue;
Packit dd8086
    if ( is_cdrom_linux(drive, NULL) > 0 ) {
Packit dd8086
      cdio_add_device_list(&drives, drive, &num_drives);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Now check the currently mounted CD drives */
Packit dd8086
  if (NULL != (ret_drive = check_mounts_linux("/etc/mtab"))) {
Packit dd8086
    cdio_add_device_list(&drives, ret_drive, &num_drives);
Packit dd8086
    free(ret_drive);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Finally check possible mountable drives in /etc/fstab */
Packit dd8086
  if (NULL != (ret_drive = check_mounts_linux("/etc/fstab"))) {
Packit dd8086
    cdio_add_device_list(&drives, ret_drive, &num_drives);
Packit dd8086
    free(ret_drive);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Scan the system for CD-ROM drives.
Packit dd8086
     Not always 100% reliable, so use the USE_MNTENT code above first.
Packit dd8086
  */
Packit dd8086
  for ( i=0; i < checklist2_size; ++i ) {
Packit dd8086
    unsigned int j;
Packit dd8086
    for ( j=checklist2[i].num_min; j<=checklist2[i].num_max; ++j ) {
Packit dd8086
      if (snprintf(drive, sizeof(drive), checklist2[i].format, j) < 0)
Packit dd8086
        continue;
Packit dd8086
      if ( (is_cdrom_linux(drive, NULL)) > 0 ) {
Packit dd8086
        cdio_add_device_list(&drives, drive, &num_drives);
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  cdio_add_device_list(&drives, NULL, &num_drives);
Packit dd8086
  return drives;
Packit dd8086
#endif /*HAVE_LINUX_CDROM*/
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return a string containing the default CD device.
Packit dd8086
 */
Packit dd8086
char *
Packit dd8086
cdio_get_default_device_linux(void)
Packit dd8086
{
Packit dd8086
#ifndef HAVE_LINUX_CDROM
Packit dd8086
  return NULL;
Packit dd8086
Packit dd8086
#else
Packit dd8086
  unsigned int i;
Packit dd8086
  char drive[40];
Packit dd8086
  char *ret_drive;
Packit dd8086
Packit dd8086
  /* Scan the system for CD-ROM drives.
Packit dd8086
  */
Packit dd8086
  for ( i=0; i < checklist1_size; ++i ) {
Packit dd8086
    if (snprintf(drive, sizeof(drive), "/dev/%s", checklist1[i]) < 0)
Packit dd8086
      continue;
Packit dd8086
    if ( is_cdrom_linux(drive, NULL) > 0 ) {
Packit dd8086
      return strdup(drive);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Now check the currently mounted CD drives */
Packit dd8086
  if (NULL != (ret_drive = check_mounts_linux("/etc/mtab")))
Packit dd8086
    return ret_drive;
Packit dd8086
Packit dd8086
  /* Finally check possible mountable drives in /etc/fstab */
Packit dd8086
  if (NULL != (ret_drive = check_mounts_linux("/etc/fstab")))
Packit dd8086
    return ret_drive;
Packit dd8086
Packit dd8086
  /* Scan the system for CD-ROM drives.
Packit dd8086
     Not always 100% reliable, so use the USE_MNTENT code above first.
Packit dd8086
  */
Packit dd8086
  for ( i=0; i < checklist2_size; ++i ) {
Packit dd8086
    unsigned int j;
Packit dd8086
    for ( j=checklist2[i].num_min; j<=checklist2[i].num_max; ++j ) {
Packit dd8086
      if (snprintf(drive, sizeof(drive), checklist2[i].format, j) < 0)
Packit dd8086
        continue;
Packit dd8086
      if ( is_cdrom_linux(drive, NULL) > 0 ) {
Packit dd8086
        return(strdup(drive));
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  return NULL;
Packit dd8086
#endif /*HAVE_LINUX_CDROM*/
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Close tray on CD-ROM.
Packit dd8086
Packit dd8086
  @param psz_device the CD-ROM drive to be closed.
Packit dd8086
Packit dd8086
*/
Packit dd8086
driver_return_code_t
Packit dd8086
close_tray_linux (const char *psz_device)
Packit dd8086
{
Packit dd8086
#ifdef HAVE_LINUX_CDROM
Packit dd8086
  int i_rc;
Packit dd8086
  int fd = open (psz_device, O_RDONLY|O_NONBLOCK);
Packit dd8086
  int status;
Packit dd8086
Packit dd8086
  if ( fd > -1 ) {
Packit dd8086
    if((status = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) > 0) {
Packit dd8086
      switch(status) {
Packit dd8086
      case CDS_TRAY_OPEN:
Packit dd8086
        goto try_anyway;
Packit dd8086
        break;
Packit dd8086
      case CDS_DISC_OK:
Packit dd8086
        cdio_info ("Tray already closed.");
Packit dd8086
        i_rc = DRIVER_OP_SUCCESS;
Packit dd8086
        break;
Packit dd8086
      default:
Packit dd8086
        cdio_info ("Unknown CD-ROM status (%d), trying anyway", status);
Packit dd8086
        goto try_anyway;
Packit dd8086
      }
Packit dd8086
    } else {
Packit dd8086
      cdio_info ("CDROM_DRIVE_STATUS failed: %s, trying anyway",
Packit dd8086
                 strerror(errno));
Packit dd8086
    try_anyway:
Packit dd8086
      if((i_rc = ioctl(fd, CDROMCLOSETRAY)) != 0) {
Packit dd8086
        cdio_warn ("ioctl CDROMCLOSETRAY failed: %s\n", strerror(errno));
Packit dd8086
        i_rc = DRIVER_OP_ERROR;
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
    close(fd);
Packit dd8086
  } else
Packit dd8086
    i_rc = DRIVER_OP_ERROR;
Packit dd8086
  return i_rc;
Packit dd8086
#else
Packit dd8086
  return DRIVER_OP_NO_DRIVER;
Packit dd8086
#endif /*HAVE_LINUX_CDROM*/
Packit dd8086
}
Packit dd8086
Packit dd8086
#ifdef HAVE_LINUX_CDROM
Packit dd8086
/*!
Packit dd8086
  Produce a text composed from the system SCSI address tuple according to
Packit dd8086
  habits of Linux 2.4 and 2.6 :  "Bus,Host,Channel,Target,Lun" and store
Packit dd8086
  it in generic_img_private_t.scsi_tuple.
Packit dd8086
  To be accessed via cdio_get_arg("scsi-tuple-linux") or ("scsi-tuple").
Packit dd8086
  Drivers which implement this code have to return 5 valid decimal numbers
Packit dd8086
  separated by comma, or empty text if no such numbers are available.
Packit dd8086
  @return   1=success , 0=failure
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
set_scsi_tuple_linux(_img_private_t *env)
Packit dd8086
{
Packit dd8086
  int bus_no = -1, host_no = -1, channel_no = -1, target_no = -1, lun_no = -1;
Packit dd8086
  int ret, i;
Packit dd8086
  char tuple[160], hdx[10];
Packit dd8086
#ifdef SCSI_IOCTL_GET_IDLUN
Packit dd8086
  struct my_scsi_idlun {
Packit dd8086
    int x;
Packit dd8086
    int host_unique_id;
Packit dd8086
  };
Packit dd8086
  struct my_scsi_idlun idlun;
Packit dd8086
#endif
Packit dd8086
  struct stat stbuf, env_stbuf;
Packit dd8086
Packit dd8086
  /* Check whether this is a hdX and declare tuple unavailable.
Packit dd8086
     /dev/hdX is traditionally for IDE drives and the ioctls here traditionally
Packit dd8086
     return ok and all 0s for all IDE drives. So the tuples are no unique ids.
Packit dd8086
  */
Packit dd8086
  if (fstat(env->gen.fd, &env_stbuf) == -1)
Packit dd8086
    goto no_tuple;
Packit dd8086
  strcpy(hdx, "/dev/hdX");
Packit dd8086
  for (i = 'a'; i <= 'z'; i++) {
Packit dd8086
    hdx[7] = i;
Packit dd8086
    if (stat(hdx, &stbuf) == -1)
Packit dd8086
  continue;
Packit dd8086
    if (env_stbuf.st_dev == stbuf.st_dev && env_stbuf.st_ino == stbuf.st_ino)
Packit dd8086
      goto no_tuple;
Packit dd8086
  }
Packit dd8086
Packit dd8086
#ifdef SCSI_IOCTL_GET_BUS_NUMBER
Packit dd8086
  if (ioctl(env->gen.fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus_no) == -1)
Packit dd8086
    bus_no = -1;
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef SCSI_IOCTL_GET_IDLUN
Packit dd8086
  /* http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/scsi_g_idlun.html */
Packit dd8086
Packit dd8086
  ret = ioctl(env->gen.fd, SCSI_IOCTL_GET_IDLUN, &idlun);
Packit dd8086
  if (ret != -1) {
Packit dd8086
    host_no= (idlun.x >> 24) & 255;
Packit dd8086
    channel_no= (idlun.x >> 16) & 255;
Packit dd8086
    target_no= (idlun.x) & 255;
Packit dd8086
    lun_no= (idlun.x >> 8) & 255;
Packit dd8086
  }
Packit dd8086
#endif
Packit dd8086
Packit dd8086
  if (env->gen.scsi_tuple != NULL)
Packit dd8086
    free (env->gen.scsi_tuple);
Packit dd8086
  env->gen.scsi_tuple = NULL;
Packit dd8086
  if (bus_no < 0 || host_no < 0 || channel_no < 0 || target_no < 0 ||
Packit dd8086
      lun_no < 0) {
Packit dd8086
no_tuple:;
Packit dd8086
    env->gen.scsi_tuple = strdup("");
Packit dd8086
    return 0;
Packit dd8086
  }
Packit dd8086
  snprintf(tuple, sizeof(tuple)-1, "%d,%d,%d,%d,%d",
Packit dd8086
          bus_no, host_no, channel_no, target_no, lun_no);
Packit dd8086
  env->gen.scsi_tuple = strdup(tuple);
Packit dd8086
  return 1;
Packit dd8086
}
Packit dd8086
#endif
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Initialization routine. This is the only thing that doesn't
Packit dd8086
  get called via a function pointer. In fact *we* are the
Packit dd8086
  ones to set that up.
Packit dd8086
 */
Packit dd8086
CdIo_t *
Packit dd8086
cdio_open_linux (const char *psz_source_name)
Packit dd8086
{
Packit dd8086
#ifdef HAVE_LINUX_CDROM
Packit dd8086
  return cdio_open_am_linux(psz_source_name, NULL);
Packit dd8086
#else
Packit dd8086
  return NULL;
Packit dd8086
#endif /*HAVE_LINUX_CDROM*/
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Initialization routine. This is the only thing that doesn't
Packit dd8086
  get called via a function pointer. In fact *we* are the
Packit dd8086
  ones to set that up.
Packit dd8086
 */
Packit dd8086
CdIo_t *
Packit dd8086
cdio_open_am_linux (const char *psz_orig_source, const char *access_mode)
Packit dd8086
{
Packit dd8086
Packit dd8086
#ifdef HAVE_LINUX_CDROM
Packit dd8086
  CdIo_t *ret;
Packit dd8086
  _img_private_t *_data;
Packit dd8086
  char *psz_source;
Packit dd8086
  int open_access_mode;  /* Access mode passed to cdio_generic_init. */
Packit dd8086
Packit dd8086
  cdio_funcs_t _funcs = {
Packit dd8086
    .audio_get_volume      = audio_get_volume_linux,
Packit dd8086
    .audio_pause           = audio_pause_linux,
Packit dd8086
    .audio_play_msf        = audio_play_msf_linux,
Packit dd8086
    .audio_play_track_index= audio_play_track_index_linux,
Packit dd8086
#ifdef USE_MMC_SUBCHANNEL
Packit dd8086
    .audio_read_subchannel = audio_read_subchannel_mmc,
Packit dd8086
#else
Packit dd8086
    .audio_read_subchannel = audio_read_subchannel_linux,
Packit dd8086
#endif
Packit dd8086
    .audio_resume          = audio_resume_linux,
Packit dd8086
    .audio_set_volume      = audio_set_volume_linux,
Packit dd8086
    .audio_stop            = audio_stop_linux,
Packit dd8086
    .eject_media           = eject_media_linux,
Packit dd8086
    .free                  = cdio_generic_free,
Packit dd8086
    .get_arg               = get_arg_linux,
Packit dd8086
    .get_blocksize         = get_blocksize_mmc,
Packit dd8086
    .get_cdtext            = get_cdtext_generic,
Packit dd8086
    .get_cdtext_raw        = read_cdtext_generic,
Packit dd8086
    .get_default_device    = cdio_get_default_device_linux,
Packit dd8086
    .get_devices           = cdio_get_devices_linux,
Packit dd8086
    .get_disc_last_lsn     = get_disc_last_lsn_linux,
Packit dd8086
    .get_discmode          = get_discmode_linux,
Packit dd8086
#if defined(USE_LINUX_CAP)
Packit dd8086
    .get_drive_cap         = get_drive_cap_linux,
Packit dd8086
#else
Packit dd8086
    .get_drive_cap         = get_drive_cap_mmc,
Packit dd8086
#endif
Packit dd8086
    .get_first_track_num   = get_first_track_num_generic,
Packit dd8086
    .get_hwinfo            = NULL,
Packit dd8086
    .get_last_session      = get_last_session_linux,
Packit dd8086
    .get_media_changed     = get_media_changed_linux,
Packit dd8086
    .get_mcn               = get_mcn_linux,
Packit dd8086
    .get_num_tracks        = get_num_tracks_generic,
Packit dd8086
    .get_track_channels    = get_track_channels_generic,
Packit dd8086
    .get_track_copy_permit = get_track_copy_permit_generic,
Packit dd8086
    .get_track_format      = get_track_format_linux,
Packit dd8086
    .get_track_green       = get_track_green_linux,
Packit dd8086
    .get_track_lba         = NULL, /* This could be implemented if need be. */
Packit dd8086
    .get_track_preemphasis = get_track_preemphasis_generic,
Packit dd8086
    .get_track_msf         = get_track_msf_linux,
Packit dd8086
    .get_track_isrc        = get_track_isrc_linux,
Packit dd8086
    .lseek                 = cdio_generic_lseek,
Packit dd8086
    .read                  = cdio_generic_read,
Packit dd8086
    .read_audio_sectors    = read_audio_sectors_linux,
Packit dd8086
#if 1
Packit dd8086
    .read_data_sectors     = read_data_sectors_generic,
Packit dd8086
#else
Packit dd8086
    .read_data_sectors     = read_data_sectors_mmc,
Packit dd8086
#endif
Packit dd8086
    .read_mode1_sector     = _read_mode1_sector_linux,
Packit dd8086
    .read_mode1_sectors    = _read_mode1_sectors_linux,
Packit dd8086
    .read_mode2_sector     = _read_mode2_sector_linux,
Packit dd8086
    .read_mode2_sectors    = _read_mode2_sectors_linux,
Packit dd8086
    .read_toc              = read_toc_linux,
Packit dd8086
    .run_mmc_cmd           = run_mmc_cmd_linux,
Packit dd8086
    .set_arg               = set_arg_linux,
Packit dd8086
    .set_blocksize         = set_blocksize_mmc,
Packit dd8086
#if 1
Packit dd8086
    .set_speed             = set_speed_linux,
Packit dd8086
#else
Packit dd8086
    .set_speed             = set_speed_mmc,
Packit dd8086
#endif
Packit dd8086
  };
Packit dd8086
Packit dd8086
  _data                 = calloc (1, sizeof (_img_private_t));
Packit dd8086
Packit dd8086
  _data->access_mode    = str_to_access_mode_linux(access_mode);
Packit dd8086
  _data->gen.init       = false;
Packit dd8086
  _data->gen.toc_init   = false;
Packit dd8086
  _data->gen.fd         = -1;
Packit dd8086
  _data->gen.b_cdtext_error = false;
Packit dd8086
Packit dd8086
  if (NULL == psz_orig_source) {
Packit dd8086
    psz_source=cdio_get_default_device_linux();
Packit dd8086
    if (NULL == psz_source) {
Packit dd8086
      goto err_exit;
Packit dd8086
    }
Packit dd8086
Packit dd8086
    set_arg_linux(_data, "source", psz_source);
Packit dd8086
    free(psz_source);
Packit dd8086
  } else {
Packit dd8086
    if (cdio_is_device_generic(psz_orig_source))
Packit dd8086
      set_arg_linux(_data, "source", psz_orig_source);
Packit dd8086
    else {
Packit dd8086
      /* The below would be okay if all device drivers worked this way. */
Packit dd8086
#if 0
Packit dd8086
      cdio_info ("source %s is not a device", psz_orig_source);
Packit dd8086
#endif
Packit dd8086
      goto err_exit;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  ret = cdio_new ((void *)_data, &_funcs);
Packit dd8086
  if (ret == NULL) {
Packit dd8086
    goto err_exit;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  ret->driver_id = DRIVER_LINUX;
Packit dd8086
Packit dd8086
  open_access_mode = O_NONBLOCK;
Packit dd8086
  if (_AM_MMC_RDWR == _data->access_mode)
Packit dd8086
    open_access_mode |= O_RDWR;
Packit dd8086
  else if (_AM_MMC_RDWR_EXCL == _data->access_mode)
Packit dd8086
    open_access_mode |= O_RDWR | O_EXCL;
Packit dd8086
  else
Packit dd8086
    open_access_mode |= O_RDONLY;
Packit dd8086
  if (cdio_generic_init(_data, open_access_mode)) {
Packit dd8086
    set_scsi_tuple_linux(_data);
Packit dd8086
    return ret;
Packit dd8086
  }
Packit dd8086
  free(ret);
Packit dd8086
Packit dd8086
 err_exit:
Packit dd8086
    cdio_generic_free(_data);
Packit dd8086
    return NULL;
Packit dd8086
Packit dd8086
#else
Packit dd8086
  return NULL;
Packit dd8086
#endif /* HAVE_LINUX_CDROM */
Packit dd8086
Packit dd8086
}
Packit dd8086
Packit dd8086
bool
Packit dd8086
cdio_have_linux (void)
Packit dd8086
{
Packit dd8086
#ifdef HAVE_LINUX_CDROM
Packit dd8086
  return true;
Packit dd8086
#else
Packit dd8086
  return false;
Packit dd8086
#endif /* HAVE_LINUX_CDROM */
Packit dd8086
}
Packit dd8086
Packit dd8086

Packit dd8086
/*
Packit dd8086
 * Local variables:
Packit dd8086
 *  c-file-style: "gnu"
Packit dd8086
 *  tab-width: 8
Packit dd8086
 *  indent-tabs-mode: nil
Packit dd8086
 * End:
Packit dd8086
 */