Blame lib/driver/FreeBSD/freebsd.c

Packit dd8086
/*
Packit dd8086
  Copyright (C) 2003, 2004-2005, 2008-2011, 2014, 2017
Packit dd8086
  Rocky Bernstein <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 FreeBSD-specific code and implements low-level
Packit dd8086
   control of the CD drive. Culled initially I think from xine's or
Packit dd8086
   mplayer's FreeBSD code with lots of modifications.
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
#include "freebsd.h"
Packit dd8086
Packit dd8086
#ifdef HAVE_FREEBSD_CDROM
Packit dd8086
Packit dd8086
#ifdef HAVE_SYS_PARAM_H
Packit dd8086
#include <sys/param.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include <netinet/in.h>
Packit dd8086
Packit dd8086
/* For freebsd_dev_lock() */
Packit dd8086
#include <sys/file.h>
Packit dd8086
Packit dd8086
#ifdef HAVE_SYS_TYPES_H
Packit dd8086
# include <sys/types.h>
Packit dd8086
#endif
Packit dd8086
#ifdef _HAVE_SYS_STAT_H
Packit dd8086
# include <sys/stat.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_FCNTL_H
Packit dd8086
# include <fcntl.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include <cdio/sector.h>
Packit dd8086
Packit dd8086
static lba_t get_track_lba_freebsd(void *p_user_data, track_t i_track);
Packit dd8086
Packit dd8086
static access_mode_t
Packit dd8086
str_to_access_mode_freebsd(const char *psz_access_mode)
Packit dd8086
{
Packit dd8086
  const access_mode_t default_access_mode = DEFAULT_FREEBSD_AM;
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, "CAM"))
Packit dd8086
    return _AM_CAM;
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 used.",
Packit dd8086
	       psz_access_mode);
Packit dd8086
    return default_access_mode;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
static void
Packit dd8086
free_freebsd (void *p_obj)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_obj;
Packit dd8086
Packit dd8086
  if (NULL == p_env) return;
Packit dd8086
Packit dd8086
  if (NULL != p_env->device) free(p_env->device);
Packit dd8086
Packit dd8086
  switch (p_env->access_mode) {
Packit dd8086
    case _AM_CAM:
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
Packit dd8086
      free_freebsd_cam(p_env);
Packit dd8086
      break;
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      cdio_generic_free(p_obj);
Packit dd8086
      break;
Packit dd8086
    case _AM_NONE:
Packit dd8086
      break;
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
cdio_is_cdrom(char *drive, char *mnttype)
Packit dd8086
{
Packit dd8086
  return cdio_is_cdrom_freebsd_ioctl(drive, mnttype);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads i_blocks of audio sectors from cd device into data starting from lsn.
Packit dd8086
   Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
read_audio_sectors_freebsd (void *p_user_data, void *p_buf, lsn_t i_lsn,
Packit dd8086
			     unsigned int i_blocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  switch (p_env->access_mode) {
Packit dd8086
    case _AM_CAM:
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
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
    case _AM_IOCTL:
Packit dd8086
      return read_audio_sectors_freebsd_ioctl(p_user_data, p_buf, i_lsn,
Packit dd8086
					      i_blocks);
Packit dd8086
    case _AM_NONE:
Packit dd8086
      cdio_info ("access mode not set");
Packit dd8086
      return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_ERROR;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads a single mode2 sector from cd device into data starting
Packit dd8086
   from i_lsn. Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
read_mode2_sector_freebsd (void *p_user_data, void *data, lsn_t i_lsn,
Packit dd8086
			   bool b_form2)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  switch (p_env->access_mode) {
Packit dd8086
    case _AM_CAM:
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
Packit dd8086
    return read_mode2_sector_freebsd_cam(p_env, data, i_lsn, b_form2);
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      return read_mode2_sector_freebsd_ioctl(p_env, data, i_lsn, b_form2);
Packit dd8086
    case _AM_NONE:
Packit dd8086
      cdio_info ("access mode not set");
Packit dd8086
      return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_ERROR;
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
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
read_mode2_sectors_freebsd (void *p_user_data, void *p_data, lsn_t i_lsn,
Packit dd8086
			    bool b_form2, unsigned int i_blocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if ( (p_env->access_mode == _AM_CAM ||
Packit dd8086
	p_env->access_mode == _AM_MMC_RDWR ||
Packit dd8086
	p_env->access_mode == _AM_MMC_RDWR_EXCL)
Packit dd8086
       && b_form2 ) {
Packit dd8086
    /* We have a routine that covers this case without looping. */
Packit dd8086
    return read_mode2_sectors_freebsd_cam(p_env, p_data, i_lsn, i_blocks);
Packit dd8086
  } else {
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 = read_mode2_sector_freebsd (p_env,
Packit dd8086
					       ((char *)p_data) +
Packit dd8086
					       (i_blocksize * i),
Packit dd8086
					       i_lsn + i, b_form2);
Packit dd8086
      if (retval) return retval;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_SUCCESS;
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
static lsn_t
Packit dd8086
get_disc_last_lsn_freebsd (void *p_obj)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_obj;
Packit dd8086
Packit dd8086
  if (!p_env) return CDIO_INVALID_LSN;
Packit dd8086
Packit dd8086
  switch (p_env->access_mode) {
Packit dd8086
    case _AM_CAM:
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
Packit dd8086
      return get_disc_last_lsn_mmc(p_env);
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      return get_disc_last_lsn_freebsd_ioctl(p_env);
Packit dd8086
    case _AM_NONE:
Packit dd8086
      cdio_info ("access mode not set");
Packit dd8086
      return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_ERROR;
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_freebsd (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_freebsd(value);
Packit dd8086
      if (p_env->access_mode == _AM_CAM && !p_env->b_cam_init)
Packit dd8086
	return init_freebsd_cam(p_env)
Packit dd8086
	  ? DRIVER_OP_SUCCESS : DRIVER_OP_ERROR;
Packit dd8086
    }
Packit dd8086
  else return DRIVER_OP_ERROR;
Packit dd8086
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
Packit dd8086
}
Packit dd8086
Packit dd8086
/* Set CD-ROM drive speed */
Packit dd8086
static int
Packit dd8086
set_speed_freebsd (void *p_user_data, int i_speed)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if (!p_env) return -1;
Packit dd8086
#ifdef CDRIOCREADSPEED
Packit dd8086
  i_speed *= 177;
Packit dd8086
  return ioctl(p_env->gen.fd, CDRIOCREADSPEED, &i_speed);
Packit dd8086
#else
Packit dd8086
  return -2;
Packit dd8086
#endif
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 unsuccessful;
Packit dd8086
*/
Packit dd8086
static bool
Packit dd8086
read_toc_freebsd (void *p_user_data)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  track_t i, j;
Packit dd8086
Packit dd8086
  /* read TOC header */
Packit dd8086
  if ( ioctl(p_env->gen.fd, CDIOREADTOCHEADER, &p_env->tochdr) == -1 ) {
Packit dd8086
    cdio_warn("error in ioctl(CDIOREADTOCHEADER): %s\n", strerror(errno));
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  p_env->gen.i_first_track = p_env->tochdr.starting_track;
Packit dd8086
  p_env->gen.i_tracks      = p_env->tochdr.ending_track -
Packit dd8086
    p_env->gen.i_first_track + 1;
Packit dd8086
Packit dd8086
  j=0;
Packit dd8086
  for (i=p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++, j++) {
Packit dd8086
    struct ioc_read_toc_single_entry *p_toc =
Packit dd8086
      &(p_env->tocent[i-p_env->gen.i_first_track]);
Packit dd8086
    p_toc->track = i;
Packit dd8086
    p_toc->address_format = CD_LBA_FORMAT;
Packit dd8086
Packit dd8086
    if ( ioctl(p_env->gen.fd, CDIOREADTOCENTRY, p_toc) ) {
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->entry.control);
Packit dd8086
Packit dd8086
  }
Packit dd8086
Packit dd8086
  p_env->tocent[j].track          = CDIO_CDROM_LEADOUT_TRACK;
Packit dd8086
  p_env->tocent[j].address_format = CD_LBA_FORMAT;
Packit dd8086
  if ( ioctl(p_env->gen.fd, CDIOREADTOCENTRY, &(p_env->tocent[j]) ) ){
Packit dd8086
    cdio_warn("%s: %s\n",
Packit dd8086
	       "error in ioctl CDROMREADTOCENTRY for leadout track",
Packit dd8086
	       strerror(errno));
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  p_env->gen.toc_init = true;
Packit dd8086
  return true;
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_freebsd (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, CDIOCGETVOL, 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_freebsd (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, CDIOCPAUSE);
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_freebsd (void *p_user_data, msf_t *p_start_msf,
Packit dd8086
			msf_t *p_end_msf)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  struct ioc_play_msf freebsd_play_msf;
Packit dd8086
Packit dd8086
  freebsd_play_msf.start_m = cdio_from_bcd8(p_start_msf->m);
Packit dd8086
  freebsd_play_msf.start_s = cdio_from_bcd8(p_start_msf->s);
Packit dd8086
  freebsd_play_msf.start_f = cdio_from_bcd8(p_start_msf->f);
Packit dd8086
Packit dd8086
  freebsd_play_msf.end_m = cdio_from_bcd8(p_end_msf->m);
Packit dd8086
  freebsd_play_msf.end_s = cdio_from_bcd8(p_end_msf->s);
Packit dd8086
  freebsd_play_msf.end_f = cdio_from_bcd8(p_end_msf->f);
Packit dd8086
Packit dd8086
  return ioctl(p_env->gen.fd, CDIOCPLAYMSF, &freebsd_play_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_user_data 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_freebsd (void *p_user_data,
Packit dd8086
				cdio_track_index_t *p_track_index)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  msf_t start_msf;
Packit dd8086
  msf_t end_msf;
Packit dd8086
  struct ioc_play_msf freebsd_play_msf;
Packit dd8086
  lsn_t i_lsn = get_track_lba_freebsd(p_user_data,
Packit dd8086
				      p_track_index->i_start_track);
Packit dd8086
Packit dd8086
  cdio_lsn_to_msf(i_lsn, &start_msf);
Packit dd8086
  i_lsn = get_track_lba_freebsd(p_user_data, p_track_index->i_end_track);
Packit dd8086
  cdio_lsn_to_msf(i_lsn, &end_msf);
Packit dd8086
Packit dd8086
  freebsd_play_msf.start_m = start_msf.m;
Packit dd8086
  freebsd_play_msf.start_s = start_msf.s;
Packit dd8086
  freebsd_play_msf.start_f = start_msf.f;
Packit dd8086
Packit dd8086
  freebsd_play_msf.end_m = end_msf.m;
Packit dd8086
  freebsd_play_msf.end_s = end_msf.s;
Packit dd8086
  freebsd_play_msf.end_f = end_msf.f;
Packit dd8086
Packit dd8086
  return ioctl(p_env->gen.fd, CDIOCPLAYMSF, &freebsd_play_msf);
Packit dd8086
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
#if 1
Packit dd8086
static driver_return_code_t
Packit dd8086
audio_read_subchannel_freebsd (void *p_user_data,
Packit dd8086
			       /*out*/ cdio_subchannel_t *p_subchannel)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  int i_rc;
Packit dd8086
  struct cd_sub_channel_info bsdinfo;
Packit dd8086
  struct ioc_read_subchannel read_subchannel;
Packit dd8086
  memset(& bsdinfo, 0, sizeof(struct cd_sub_channel_info));
Packit dd8086
  read_subchannel.address_format = CD_MSF_FORMAT;
Packit dd8086
  read_subchannel.data_format = CD_CURRENT_POSITION;
Packit dd8086
  read_subchannel.track = 0;
Packit dd8086
  read_subchannel.data_len = sizeof(struct cd_sub_channel_info);
Packit dd8086
  read_subchannel.data = & bsdinfo;
Packit dd8086
  i_rc = ioctl(p_env->gen.fd, CDIOCREADSUBCHANNEL, &read_subchannel);
Packit dd8086
  if (0 == i_rc) {
Packit dd8086
    p_subchannel->audio_status = bsdinfo.header.audio_status;
Packit dd8086
    p_subchannel->address      = bsdinfo.what.position.addr_type;
Packit dd8086
Packit dd8086
    p_subchannel->control      = bsdinfo.what.position.control;
Packit dd8086
    p_subchannel->track        = bsdinfo.what.position.track_number;
Packit dd8086
    p_subchannel->index        = bsdinfo.what.position.index_number;
Packit dd8086
Packit dd8086
    p_subchannel->abs_addr.m = cdio_to_bcd8 (bsdinfo.what.position.absaddr.msf.minute);
Packit dd8086
    p_subchannel->abs_addr.s = cdio_to_bcd8 (bsdinfo.what.position.absaddr.msf.second);
Packit dd8086
    p_subchannel->abs_addr.f = cdio_to_bcd8 (bsdinfo.what.position.absaddr.msf.frame);
Packit dd8086
    p_subchannel->rel_addr.m = cdio_to_bcd8 (bsdinfo.what.position.reladdr.msf.minute);
Packit dd8086
    p_subchannel->rel_addr.s = cdio_to_bcd8 (bsdinfo.what.position.reladdr.msf.second);
Packit dd8086
    p_subchannel->rel_addr.f = cdio_to_bcd8 (bsdinfo.what.position.reladdr.msf.frame);
Packit dd8086
 }
Packit dd8086
  return i_rc;
Packit dd8086
}
Packit dd8086
#endif
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_freebsd (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, CDIOCRESUME, 0);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Set the volume of 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_set_volume_freebsd (void *p_user_data,
Packit dd8086
			  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, CDIOCSETVOL, p_volume);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Eject media. Return 1 if successful, 0 otherwise.
Packit dd8086
 */
Packit dd8086
static int
Packit dd8086
eject_media_freebsd (void *p_user_data)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  switch (p_env->access_mode) {
Packit dd8086
    case _AM_CAM:
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
Packit dd8086
      return eject_media_freebsd_cam(p_env);
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      return eject_media_freebsd_ioctl(p_env);
Packit dd8086
    case _AM_NONE:
Packit dd8086
      cdio_info ("access mode not set");
Packit dd8086
      return 0;
Packit dd8086
  }
Packit dd8086
  return 0;
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_freebsd (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, CDIOCSTOP);
Packit dd8086
}
Packit dd8086
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
  Channel has no meaning on FreeBSD. Expect it to be 0. It is only in
Packit dd8086
  the text to avoid an unnecessary difference in format.
Packit dd8086
  Bus and Host will always be the same.
Packit dd8086
  To be accessed via cdio_get_arg("scsi-tuple-freebsd") or "scsi-tuple".
Packit dd8086
  For simplicity the FreeBSD driver also replies on "scsi-tuple-linux".
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_freebsd(_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;
Packit dd8086
  char tuple[160];
Packit dd8086
Packit dd8086
  ret = obtain_scsi_adr_freebsd_cam(env->gen.source_name,
Packit dd8086
                                   &bus_no, &host_no, &channel_no,
Packit dd8086
                                   &target_no, &lun_no);
Packit dd8086
  if (ret != 1)
Packit dd8086
    return 0;
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
    env->gen.scsi_tuple = strdup("");
Packit dd8086
    return 0;
Packit dd8086
  }
Packit dd8086
  sprintf(tuple, "%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
Packit dd8086
static bool
Packit dd8086
is_mmc_supported(void *user_data)
Packit dd8086
{
Packit dd8086
    _img_private_t *env = user_data;
Packit dd8086
    switch (env->access_mode) {
Packit dd8086
      case _AM_IOCTL:
Packit dd8086
      case _AM_NONE:
Packit dd8086
	return false;
Packit dd8086
      case _AM_CAM:
Packit dd8086
      case _AM_MMC_RDWR:
Packit dd8086
      case _AM_MMC_RDWR_EXCL:
Packit dd8086
	return true;
Packit dd8086
    }
Packit dd8086
    /* Not reached. */
Packit dd8086
    return false;
Packit dd8086
}
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_freebsd (void *user_data, const char key[])
Packit dd8086
{
Packit dd8086
  _img_private_t *env = user_data;
Packit dd8086
Packit dd8086
  if (!strcmp (key, "source")) {
Packit dd8086
    return env->gen.source_name;
Packit dd8086
  } else if (!strcmp (key, "access-mode")) {
Packit dd8086
    switch (env->access_mode) {
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      return "ioctl";
Packit dd8086
    case _AM_CAM:
Packit dd8086
      return "CAM";
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") == 0) {
Packit dd8086
    if (env->gen.scsi_tuple == NULL)
Packit dd8086
      set_scsi_tuple_freebsd(env);
Packit dd8086
    return env->gen.scsi_tuple;
Packit dd8086
  } else if (!strcmp (key, "mmc-supported?")) {
Packit dd8086
    return is_mmc_supported(user_data) ? "true" : "false";
Packit dd8086
  }
Packit dd8086
  return NULL;
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
  FIXME: This is just a guess.
Packit dd8086
Packit dd8086
 */
Packit dd8086
static char *
Packit dd8086
get_mcn_freebsd (const void *p_user_data) {
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  switch (p_env->access_mode) {
Packit dd8086
    case _AM_CAM:
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
Packit dd8086
      return mmc_get_mcn(p_env->gen.cdio);
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      return mmc_get_mcn(p_env->gen.cdio);
Packit dd8086
    case _AM_NONE:
Packit dd8086
      cdio_info ("access mode not set");
Packit dd8086
      return NULL;
Packit dd8086
  }
Packit dd8086
  return NULL;
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_freebsd (const void *p_user_data,
Packit dd8086
			track_t i_track) {
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  switch (p_env->access_mode) {
Packit dd8086
    case _AM_CAM:
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
Packit dd8086
      return mmc_get_track_isrc(p_env->gen.cdio, i_track);
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      return mmc_get_track_isrc(p_env->gen.cdio, i_track);
Packit dd8086
    case _AM_NONE:
Packit dd8086
      cdio_info ("access mode not set");
Packit dd8086
      return NULL;
Packit dd8086
  }
Packit dd8086
  return NULL;
Packit dd8086
}
Packit dd8086
Packit dd8086
static void
Packit dd8086
get_drive_cap_freebsd (const void *p_user_data,
Packit dd8086
		       cdio_drive_read_cap_t  *p_read_cap,
Packit dd8086
		       cdio_drive_write_cap_t *p_write_cap,
Packit dd8086
		       cdio_drive_misc_cap_t  *p_misc_cap)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  switch (p_env->access_mode) {
Packit dd8086
    case _AM_CAM:
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
Packit dd8086
      get_drive_cap_mmc (p_user_data, p_read_cap, p_write_cap, p_misc_cap);
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      cdio_info ("get_drive_cap not supported in ioctl access mode");
Packit dd8086
      return;
Packit dd8086
    case _AM_NONE:
Packit dd8086
      cdio_info ("access mode not set");
Packit dd8086
      return;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Run a SCSI MMC command.
Packit dd8086
Packit dd8086
  p_user_data   internal CD structure.
Packit dd8086
  i_timeout_ms  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
  i_cdb	        Size of p_cdb
Packit dd8086
  p_cdb	        CDB bytes.
Packit dd8086
  e_direction	direction the transfer is to go.
Packit dd8086
  i_buf	        Size of buffer
Packit dd8086
  p_buf	        Buffer for data, both sending and receiving
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
run_mmc_cmd_freebsd( void *p_user_data, 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
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  int ret;
Packit dd8086
Packit dd8086
  switch (p_env->access_mode) {
Packit dd8086
    case _AM_CAM:
Packit dd8086
    case _AM_MMC_RDWR:
Packit dd8086
    case _AM_MMC_RDWR_EXCL:
Packit dd8086
      ret = run_mmc_cmd_freebsd_cam( p_user_data, i_timeout_ms, i_cdb, p_cdb,
Packit dd8086
                                     e_direction, i_buf, p_buf );
Packit dd8086
      if (ret != 0)
Packit dd8086
        return DRIVER_OP_ERROR;
Packit dd8086
      return 0;
Packit dd8086
    case _AM_IOCTL:
Packit dd8086
      return DRIVER_OP_UNSUPPORTED;
Packit dd8086
    case _AM_NONE:
Packit dd8086
      cdio_info ("access mode not set");
Packit dd8086
      return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_ERROR;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Get format of track.
Packit dd8086
Packit dd8086
  FIXME: We're just guessing this from the GNU/Linux code.
Packit dd8086
Packit dd8086
*/
Packit dd8086
static track_format_t
Packit dd8086
get_track_format_freebsd(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_freebsd (p_user_data) ;
Packit dd8086
Packit dd8086
  if (i_track > TOTAL_TRACKS || i_track == 0)
Packit dd8086
    return TRACK_FORMAT_ERROR;
Packit dd8086
Packit dd8086
  i_track -= FIRST_TRACK_NUM;
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].entry.control & CDIO_CDROM_DATA_TRACK) {
Packit dd8086
    if (p_env->tocent[i_track].address_format == CDIO_CDROM_CDI_TRACK)
Packit dd8086
      return TRACK_FORMAT_CDI;
Packit dd8086
    else if (p_env->tocent[i_track].address_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_freebsd(void *user_data, track_t i_track)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = user_data;
Packit dd8086
Packit dd8086
  if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = TOTAL_TRACKS+1;
Packit dd8086
Packit dd8086
  if (i_track > TOTAL_TRACKS+1 || i_track == 0)
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  /* FIXME: Dunno if this is the right way, but it's what
Packit dd8086
     I was using in cdinfo for a while.
Packit dd8086
   */
Packit dd8086
  return ((p_env->tocent[i_track-FIRST_TRACK_NUM].entry.control & 2) != 0);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the starting LSN track number
Packit dd8086
  i_track in obj.  Track numbers start at 1.
Packit dd8086
  The "leadout" track is specified either by
Packit dd8086
  using i_track LEADOUT_TRACK or the total tracks+1.
Packit dd8086
  CDIO_INVALID_LBA is returned if there is no track entry.
Packit dd8086
*/
Packit dd8086
static lba_t
Packit dd8086
get_track_lba_freebsd(void *user_data, track_t i_track)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = user_data;
Packit dd8086
Packit dd8086
  if (!p_env->gen.toc_init) read_toc_freebsd (p_env) ;
Packit dd8086
Packit dd8086
  if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = TOTAL_TRACKS+1;
Packit dd8086
Packit dd8086
  if (i_track > TOTAL_TRACKS+1 || i_track == 0 || !p_env->gen.toc_init) {
Packit dd8086
    return CDIO_INVALID_LBA;
Packit dd8086
  } else {
Packit dd8086
    return cdio_lsn_to_lba(ntohl(p_env->tocent[i_track-FIRST_TRACK_NUM].entry.addr.lba));
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
#endif /* HAVE_FREEBSD_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_freebsd (void)
Packit dd8086
{
Packit dd8086
#ifndef HAVE_FREEBSD_CDROM
Packit dd8086
  return NULL;
Packit dd8086
#else
Packit dd8086
  char drive[40];
Packit dd8086
  char **drives = NULL;
Packit dd8086
  unsigned int num_drives=0;
Packit dd8086
  bool exists = true;
Packit dd8086
  char c;
Packit dd8086
Packit dd8086
  /* Scan the system for CD-ROM drives.
Packit dd8086
  */
Packit dd8086
Packit dd8086
#ifdef USE_ETC_FSTAB
Packit dd8086
Packit dd8086
  struct fstab *fs;
Packit dd8086
  setfsent();
Packit dd8086
Packit dd8086
  /* Check what's in /etc/fstab... */
Packit dd8086
  while ( (fs = getfsent()) )
Packit dd8086
    {
Packit dd8086
      if (strncmp(fs->fs_spec, "/dev/sr", 7))
Packit dd8086
	cdio_add_device_list(&drives, fs->fs_spec, &num_drives);
Packit dd8086
    }
Packit dd8086
Packit dd8086
#endif
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
Packit dd8086
  /* Scan SCSI and CAM devices */
Packit dd8086
  exists = true;
Packit dd8086
  for ( c='0'; exists && c <='9'; c++ ) {
Packit dd8086
    sprintf(drive, "/dev/cd%c%s", c, DEVICE_POSTFIX);
Packit dd8086
    exists = cdio_is_cdrom(drive, NULL);
Packit dd8086
    if ( exists ) {
Packit dd8086
      cdio_add_device_list(&drives, drive, &num_drives);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Scan ATAPI devices */
Packit dd8086
Packit dd8086
  /* ??? ts 9 Jan 2009
Packit dd8086
     For now i assume atapicam running if a cdN device was found.
Packit dd8086
     man atapicam strongly discourages to mix usage of CAM and ATAPI device.
Packit dd8086
     So on the risk to sabotage systems without atapicam but with real old
Packit dd8086
     SCSI drives, i list no ATAPI addresses if there was a CAM/SCSI address.
Packit dd8086
Packit dd8086
  exists = !have_cam_drive;
Packit dd8086
Packit dd8086
     ts 13 Jan 2009
Packit dd8086
     Regrettably USB drives appear as SCSI drives. We rather need to determine
Packit dd8086
     whether atapicam runs, or to make pairs of cd and acd.
Packit dd8086
Packit dd8086
  */
Packit dd8086
  for ( c='0'; exists && c <='9'; c++ ) {
Packit dd8086
    sprintf(drive, "/dev/acd%c%s", c, DEVICE_POSTFIX);
Packit dd8086
    exists = cdio_is_cdrom(drive, NULL);
Packit dd8086
    if ( exists ) {
Packit dd8086
      cdio_add_device_list(&drives, drive, &num_drives);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  cdio_add_device_list(&drives, NULL, &num_drives);
Packit dd8086
  return drives;
Packit dd8086
#endif /*HAVE_FREEBSD_CDROM*/
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return a string containing the default CD device if none is specified.
Packit dd8086
 */
Packit dd8086
char *
Packit dd8086
cdio_get_default_device_freebsd()
Packit dd8086
{
Packit dd8086
#ifndef HAVE_FREEBSD_CDROM
Packit dd8086
  return NULL;
Packit dd8086
#else
Packit dd8086
  char drive[40];
Packit dd8086
  bool exists=true;
Packit dd8086
  char c;
Packit dd8086
Packit dd8086
  /* Scan the system for CD-ROM drives.
Packit dd8086
  */
Packit dd8086
Packit dd8086
#ifdef USE_ETC_FSTAB
Packit dd8086
Packit dd8086
  struct fstab *fs;
Packit dd8086
  setfsent();
Packit dd8086
Packit dd8086
  /* Check what's in /etc/fstab... */
Packit dd8086
  while ( (fs = getfsent()) )
Packit dd8086
    {
Packit dd8086
      if (strncmp(fs->fs_spec, "/dev/sr", 7))
Packit dd8086
	return strdup(fs->fs_spec);
Packit dd8086
    }
Packit dd8086
Packit dd8086
#endif
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
Packit dd8086
  /* Scan SCSI and CAM devices */
Packit dd8086
  for ( c='0'; exists && c <='9'; c++ ) {
Packit dd8086
    sprintf(drive, "/dev/cd%c%s", c, DEVICE_POSTFIX);
Packit dd8086
    exists = cdio_is_cdrom(drive, NULL);
Packit dd8086
    if ( exists ) {
Packit dd8086
      return strdup(drive);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Scan are ATAPI devices */
Packit dd8086
  exists = true;
Packit dd8086
Packit dd8086
  for ( c='0'; exists && c <='9'; c++ ) {
Packit dd8086
    sprintf(drive, "/dev/acd%c%s", c, DEVICE_POSTFIX);
Packit dd8086
    exists = cdio_is_cdrom(drive, NULL);
Packit dd8086
    if ( exists ) {
Packit dd8086
      return strdup(drive);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  return NULL;
Packit dd8086
#endif /*HAVE_FREEBSD_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_freebsd (const char *psz_device)
Packit dd8086
{
Packit dd8086
#ifdef HAVE_FREEBSD_CDROM
Packit dd8086
  int fd = open (psz_device, O_RDONLY|O_NONBLOCK, 0);
Packit dd8086
  int i_rc;
Packit dd8086
Packit dd8086
  if((i_rc = ioctl(fd, CDIOCCLOSE)) != 0) {
Packit dd8086
    cdio_warn ("ioctl CDIOCCLOSE failed: %s\n", strerror(errno));
Packit dd8086
    close(fd);
Packit dd8086
    return DRIVER_OP_ERROR;
Packit dd8086
  }
Packit dd8086
  close(fd);
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
#else
Packit dd8086
  return DRIVER_OP_NO_DRIVER;
Packit dd8086
#endif /*HAVE_FREEBSD_CDROM*/
Packit dd8086
}
Packit dd8086
Packit dd8086
/*! Find out if media has changed since the last call.  @param
Packit dd8086
  p_user_data the environment of the CD 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
Packit dd8086
#ifdef HAVE_FREEBSD_CDROM
Packit dd8086
int
Packit dd8086
get_media_changed_freebsd (const void *p_user_data)
Packit dd8086
{
Packit dd8086
  const _img_private_t *p_env = p_user_data;
Packit dd8086
  int changed = 0 ;
Packit dd8086
Packit dd8086
  changed = mmc_get_media_changed( p_env->gen.cdio );
Packit dd8086
Packit dd8086
  return ((changed > 0) ? changed : 0);
Packit dd8086
}
Packit dd8086
Packit dd8086
static const char*
Packit dd8086
get_access_mode(const char *psz_source)
Packit dd8086
{
Packit dd8086
    char *psz_src = NULL;
Packit dd8086
    if (!psz_source) {
Packit dd8086
        psz_src = cdio_get_default_device_freebsd();
Packit dd8086
    } else {
Packit dd8086
        psz_src = strdup(psz_source);
Packit dd8086
    }
Packit dd8086
Packit dd8086
    if (psz_src != NULL) {
Packit dd8086
        if (!(strncmp(psz_src, "/dev/acd", 8))) {
Packit dd8086
            free(psz_src);
Packit dd8086
            return "ioctl";
Packit dd8086
	} else {
Packit dd8086
            char devname[256];
Packit dd8086
            int  bytes = readlink(psz_src, devname, 255);
Packit dd8086
Packit dd8086
            if (bytes > 0) {
Packit dd8086
                devname[bytes]=0;
Packit dd8086
                if (!(strncmp(devname, "acd", 3))) {
Packit dd8086
		    free(psz_src);
Packit dd8086
                    return "ioctl";
Packit dd8086
		}
Packit dd8086
            }
Packit dd8086
        }
Packit dd8086
    }
Packit dd8086
    if (psz_src != NULL)
Packit dd8086
      free(psz_src);
Packit dd8086
    return "CAM";
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* Lock the inode associated to dev_fd and the inode associated to devname.
Packit dd8086
   Return OS errno, number of pass device of dev_fd, locked fd to devname,
Packit dd8086
   error message.
Packit dd8086
   A return value of > 0 means success, <= 0 means failure.
Packit dd8086
*/
Packit dd8086
static int freebsd_dev_lock(int dev_fd, char *devname,
Packit dd8086
   int *os_errno, int *pass_dev_no, int *lock_fd, char msg[4096],
Packit dd8086
   int flag)
Packit dd8086
{
Packit dd8086
  int lock_denied = 0, fd_stbuf_valid, name_stbuf_valid, i, pass_l = 100;
Packit dd8086
  int max_retry = 3, tries;
Packit dd8086
  struct stat fd_stbuf, name_stbuf;
Packit dd8086
  char pass_name[16], *lock_name;
Packit dd8086
Packit dd8086
  *os_errno = 0;
Packit dd8086
  *pass_dev_no = -1;
Packit dd8086
  *lock_fd = -1;
Packit dd8086
  msg[0] = 0;
Packit dd8086
Packit dd8086
  fd_stbuf_valid = !fstat(dev_fd, &fd_stbuf);
Packit dd8086
Packit dd8086
  /* Try to find name of pass device by inode number */
Packit dd8086
  lock_name = (char *) "effective device";
Packit dd8086
  if(fd_stbuf_valid) {
Packit dd8086
    for (i = 0; i < pass_l; i++) {
Packit dd8086
      sprintf(pass_name, "/dev/pass%d", i);
Packit dd8086
      if (stat(pass_name, &name_stbuf) != -1)
Packit dd8086
        if(fd_stbuf.st_ino == name_stbuf.st_ino &&
Packit dd8086
           fd_stbuf.st_dev == name_stbuf.st_dev)
Packit dd8086
    break;
Packit dd8086
    }
Packit dd8086
    if (i < pass_l) {
Packit dd8086
      lock_name = pass_name;
Packit dd8086
      *pass_dev_no = i;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  name_stbuf_valid = !stat(devname, &name_stbuf);
Packit dd8086
  for (tries= 0; tries <= max_retry; tries++) {
Packit dd8086
    lock_denied = flock(dev_fd, LOCK_EX | LOCK_NB);
Packit dd8086
    *os_errno = errno;
Packit dd8086
    if (lock_denied) {
Packit dd8086
      if (errno == EAGAIN && tries < max_retry) {
Packit dd8086
        /* <<< debugging
Packit dd8086
        fprintf(stderr, "\nlibcdio_DEBUG: EAGAIN , tries= %d\n", tries);
Packit dd8086
        */
Packit dd8086
        usleep(2000000);
Packit dd8086
  continue;
Packit dd8086
      }
Packit dd8086
      sprintf(msg,
Packit dd8086
        "Device busy. flock(LOCK_EX) failed on %s of %s",
Packit dd8086
        strlen(lock_name) > 2000 || *pass_dev_no < 0 ?
Packit dd8086
               "pass device" : lock_name,
Packit dd8086
        strlen(devname) > 2000 ? "drive" : devname);
Packit dd8086
      return 0;
Packit dd8086
    }
Packit dd8086
  break;
Packit dd8086
  }
Packit dd8086
Packit dd8086
/*
Packit dd8086
  fprintf(stderr, "libburn_DEBUG: flock obtained on %s of %s\n",
Packit dd8086
                  lock_name, devname);
Packit dd8086
*/
Packit dd8086
Packit dd8086
  /* Eventually lock the official device node too */
Packit dd8086
  if (fd_stbuf_valid && name_stbuf_valid &&
Packit dd8086
    (fd_stbuf.st_ino != name_stbuf.st_ino ||
Packit dd8086
     fd_stbuf.st_dev != name_stbuf.st_dev)) {
Packit dd8086
Packit dd8086
    *lock_fd = open(devname, O_RDONLY);
Packit dd8086
    if (*lock_fd == 0) {
Packit dd8086
      close(*lock_fd);
Packit dd8086
      *lock_fd = -1;
Packit dd8086
    } if (*lock_fd > 0) {
Packit dd8086
      for (tries = 0; tries <= max_retry; tries++) {
Packit dd8086
        lock_denied = flock(*lock_fd, LOCK_EX | LOCK_NB);
Packit dd8086
        if (lock_denied) {
Packit dd8086
          if (errno == EAGAIN && tries < max_retry) {
Packit dd8086
            usleep(2000000);
Packit dd8086
      continue;
Packit dd8086
          }
Packit dd8086
          close(*lock_fd);
Packit dd8086
          *lock_fd = -1;
Packit dd8086
          sprintf(msg, "Device busy. flock(LOCK_EX) failed on %s",
Packit dd8086
                  strlen(devname) > 4000 ? "drive" : devname);
Packit dd8086
          return 0;
Packit dd8086
        }
Packit dd8086
      break;
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
Packit dd8086
/*
Packit dd8086
    fprintf(stderr, "libburn_DEBUG: flock obtained on %s\n",
Packit dd8086
        devname);
Packit dd8086
*/
Packit dd8086
Packit dd8086
  }
Packit dd8086
  return 1;
Packit dd8086
}
Packit dd8086
Packit dd8086
#endif /*HAVE_FREEBSD_CDROM*/
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 *
Packit dd8086
cdio_open_freebsd (const char *psz_source_name)
Packit dd8086
{
Packit dd8086
  return cdio_open_am_freebsd(psz_source_name, NULL);
Packit dd8086
}
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 *
Packit dd8086
cdio_open_am_freebsd (const char *psz_orig_source_name,
Packit dd8086
		      const char *psz_access_mode)
Packit dd8086
{
Packit dd8086
Packit dd8086
#ifdef HAVE_FREEBSD_CDROM
Packit dd8086
  CdIo *ret;
Packit dd8086
  _img_private_t *_data;
Packit dd8086
  char *psz_source_name;
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_freebsd,
Packit dd8086
    .audio_pause            = audio_pause_freebsd,
Packit dd8086
    .audio_play_msf         = audio_play_msf_freebsd,
Packit dd8086
    .audio_play_track_index = audio_play_track_index_freebsd,
Packit dd8086
    .audio_read_subchannel  = audio_read_subchannel_freebsd,
Packit dd8086
    .audio_resume           = audio_resume_freebsd,
Packit dd8086
    .audio_set_volume       = audio_set_volume_freebsd,
Packit dd8086
    .audio_stop             = audio_stop_freebsd,
Packit dd8086
    .eject_media            = eject_media_freebsd,
Packit dd8086
    .free                   = free_freebsd,
Packit dd8086
    .get_arg                = get_arg_freebsd,
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_freebsd,
Packit dd8086
    .get_devices            = cdio_get_devices_freebsd,
Packit dd8086
    .get_disc_last_lsn      = get_disc_last_lsn_freebsd,
Packit dd8086
    .get_discmode           = get_discmode_generic,
Packit dd8086
    .get_drive_cap          = get_drive_cap_freebsd,
Packit dd8086
    .get_first_track_num    = get_first_track_num_generic,
Packit dd8086
    .get_media_changed      = get_media_changed_freebsd,
Packit dd8086
    .get_mcn                = get_mcn_freebsd,
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_freebsd,
Packit dd8086
    .get_track_green        = get_track_green_freebsd,
Packit dd8086
    .get_track_lba          = get_track_lba_freebsd,
Packit dd8086
    .get_track_preemphasis  = get_track_preemphasis_generic,
Packit dd8086
    .get_track_msf          = NULL,
Packit dd8086
    .get_track_isrc         = get_track_isrc_freebsd,
Packit dd8086
    .lseek                  = cdio_generic_lseek,
Packit dd8086
    .read                   = cdio_generic_read,
Packit dd8086
    .read_audio_sectors     = read_audio_sectors_freebsd,
Packit dd8086
    .read_data_sectors      = read_data_sectors_mmc,
Packit dd8086
    .read_mode2_sector      = read_mode2_sector_freebsd,
Packit dd8086
    .read_mode2_sectors     = read_mode2_sectors_freebsd,
Packit dd8086
    .read_toc               = read_toc_freebsd,
Packit dd8086
    .run_mmc_cmd            = run_mmc_cmd_freebsd,
Packit dd8086
    .set_arg                = set_arg_freebsd,
Packit dd8086
    .set_blocksize          = set_blocksize_mmc,
Packit dd8086
    .set_speed              = set_speed_freebsd,
Packit dd8086
  };
Packit dd8086
Packit dd8086
  if (!psz_access_mode)
Packit dd8086
      psz_access_mode = get_access_mode(psz_orig_source_name);
Packit dd8086
Packit dd8086
  _data                     = calloc(1, sizeof (_img_private_t));
Packit dd8086
  _data->access_mode        = str_to_access_mode_freebsd(psz_access_mode);
Packit dd8086
  _data->gen.init           = false;
Packit dd8086
  _data->gen.fd             = -1;
Packit dd8086
  _data->gen.toc_init       = false;
Packit dd8086
  _data->gen.b_cdtext_error = false;
Packit dd8086
Packit dd8086
  if (NULL == psz_orig_source_name) {
Packit dd8086
    psz_source_name=cdio_get_default_device_freebsd();
Packit dd8086
    if (NULL == psz_source_name) {
Packit dd8086
      cdio_generic_free (_data);
Packit dd8086
      return NULL;
Packit dd8086
    }
Packit dd8086
    _data->device  = psz_source_name;
Packit dd8086
    set_arg_freebsd(_data, "source", psz_source_name);
Packit dd8086
  } else {
Packit dd8086
    if (cdio_is_device_generic(psz_orig_source_name)) {
Packit dd8086
      set_arg_freebsd(_data, "source", psz_orig_source_name);
Packit dd8086
      _data->device  = strdup(psz_orig_source_name);
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 a not a device", psz_orig_source_name);
Packit dd8086
#endif
Packit dd8086
      cdio_generic_free (_data);
Packit dd8086
      return NULL;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  ret = cdio_new ((void *)_data, &_funcs);
Packit dd8086
  if (ret == NULL) {
Packit dd8086
    cdio_generic_free (_data);
Packit dd8086
    return NULL;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  open_access_mode = 0;
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;
Packit dd8086
  } else {
Packit dd8086
    open_access_mode |= O_RDONLY;
Packit dd8086
  }
Packit dd8086
/*
Packit dd8086
  fprintf(stderr,
Packit dd8086
      "libcdio_DEBUG: am = %d (MMC_RDWR_EXCL = %d), open = %d (O_RDWR = %d)\n",
Packit dd8086
      _data->access_mode, _AM_MMC_RDWR_EXCL, open_access_mode, O_RDWR);
Packit dd8086
*/
Packit dd8086
Packit dd8086
  if (cdio_generic_init(_data, open_access_mode)) {
Packit dd8086
    if (_AM_MMC_RDWR_EXCL == _data->access_mode) {
Packit dd8086
      int os_errno, pass_dev_no = -1, flock_fd = -1, lock_result;
Packit dd8086
      char msg[4096];
Packit dd8086
Packit dd8086
      lock_result = freebsd_dev_lock(_data->gen.fd, _data->gen.source_name,
Packit dd8086
                                   &os_errno, &pass_dev_no, &flock_fd, msg, 0);
Packit dd8086
      if (lock_result <= 0) {
Packit dd8086
        cdio_warn ("%s", msg);
Packit dd8086
	goto err_exit;
Packit dd8086
      }
Packit dd8086
      /* One should rather keep this fd open until _data->gen.fd gets closed.
Packit dd8086
         It eventually locks a device sibling of _data->gen.source_name.
Packit dd8086
      */
Packit dd8086
      if (flock_fd > 0)
Packit dd8086
        close(flock_fd);
Packit dd8086
    }
Packit dd8086
Packit dd8086
    if ( _data->access_mode == _AM_IOCTL ) {
Packit dd8086
      return ret;
Packit dd8086
    } else {
Packit dd8086
      if (init_freebsd_cam(_data))
Packit dd8086
	return ret;
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
Packit dd8086
 err_exit:
Packit dd8086
    free(ret);
Packit dd8086
    cdio_generic_free(_data);
Packit dd8086
    return NULL;
Packit dd8086
Packit dd8086
#else
Packit dd8086
  return NULL;
Packit dd8086
#endif /* HAVE_FREEBSD_CDROM */
Packit dd8086
}
Packit dd8086
Packit dd8086
bool
Packit dd8086
cdio_have_freebsd (void)
Packit dd8086
{
Packit dd8086
#ifdef HAVE_FREEBSD_CDROM
Packit dd8086
  return true;
Packit dd8086
#else
Packit dd8086
  return false;
Packit dd8086
#endif /* HAVE_FREEBSD_CDROM */
Packit dd8086
}