This repository has been archived on 2025-05-24. You can view files and clone it, but cannot push or open issues or pull requests.
Files
libcdio-osx/lib/FreeBSD/freebsd_cam.c
rocky 0441b2f09d CAM mode2 form1 and form2 reading fixes.
Default device lists default to CAM device name.
Simplify, correct and make more complete CAM support.

With the above we can finally use libcdio for cd-read, vcdimager and
probably vlc and xine.

Many thanks to Heiner.
2004-06-05 02:47:49 +00:00

422 lines
12 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
$Id: freebsd_cam.c,v 1.10 2004/06/05 02:47:49 rocky Exp $
Copyright (C) 2004 Rocky Bernstein <rocky@panix.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* This file contains FreeBSD-specific code and implements low-level
control of the CD drive via SCSI emulation.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static const char _rcsid[] = "$Id: freebsd_cam.c,v 1.10 2004/06/05 02:47:49 rocky Exp $";
#ifdef HAVE_FREEBSD_CDROM
#include "freebsd.h"
#include <cdio/scsi_mmc.h>
static const u_char scsi_cdblen[8] = {6, 10, 10, 12, 12, 12, 10, 10};
static int
_scsi_cmd (_img_private_t * env)
{
int retval;
env->ccb.csio.cdb_len = scsi_cdblen[(env->ccb.csio.cdb_io.cdb_bytes[0] >> 5) & 7];
if ((retval = cam_send_ccb(env->cam, &env->ccb)) < 0)
{
cdio_warn ("transport failed: ", retval);
return -1;
}
if ((env->ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
{
return 0;
}
errno = EIO;
retval = ERRCODE(((unsigned char *)&env->ccb.csio.sense_data));
if (retval == 0)
retval = -1;
else
CREAM_ON_ERRNO(((unsigned char *)&env->ccb.csio.sense_data));
cdio_error ("transport failed: ", retval);
return retval;
}
bool
init_freebsd_cam (_img_private_t *env)
{
char pass[100];
env->cam=NULL;
memset (&env->ccb, 0, sizeof(env->ccb));
env->ccb.ccb_h.func_code = XPT_GDEVLIST;
if (-1 == env->gen.fd)
env->gen.fd = open (env->device, O_RDONLY, 0);
if (env->gen.fd < 0)
{
cdio_error ("open (%s): %s", env->device, strerror (errno));
return false;
}
if (ioctl (env->gen.fd, CAMGETPASSTHRU, &env->ccb) < 0)
{
cdio_error ("open: %s", strerror (errno));
return false;
}
sprintf (pass,"/dev/%.15s%u",
env->ccb.cgdl.periph_name,
env->ccb.cgdl.unit_number);
env->cam = cam_open_pass (pass,O_RDWR,NULL);
env->gen.init = true;
env->b_cam_init = true;
return true;
}
void
free_freebsd_cam (void *user_data)
{
_img_private_t *env = user_data;
if (NULL == env) return;
if (env->gen.fd > 0)
close (env->gen.fd);
env->gen.fd = -1;
if(env->cam)
cam_close_device(env->cam);
free (env);
}
/****
The below are really rough guesses. They are here in the hope
they can be used as a starting point for someone who knows what
they are doing.
*******/
#if 0
/*!
Return the the kind of drive capabilities of device.
Note: string is malloc'd so caller should free() then returned
string when done with it.
*/
static char *
get_drive_mcn_freebsd_cam (void *user_data)
{
char buf[192] = { 0, };
_img_private_t *env = user_data;
int rc;
memset(&env->ccb, 0, sizeof(env->ccb));
env->ccb.ccb_h.path_id = env->cam->path_id;
env->ccb.ccb_h.target_id = env->cam->target_id;
env->ccb.ccb_h.target_lun = env->cam->target_lun;
cam_fill_csio (&(env->ccb.csio), 1, NULL,
CAM_DEV_QFRZDIS, MSG_SIMPLE_Q_TAG, NULL, 0,
sizeof(env->ccb.csio.sense_data), 0, 30*1000);
/* Initialize my_scsi_cdb as a Mode Select(6) */
CDIO_MMC_SET_COMMAND(env->ccb.csio.cdb_io.cdb_bytes,
CDIO_MMC_GPCMD_READ_SUBCHANNEL);
env->ccb.csio.cdb_io.cdb_bytes[1] = 0x0;
env->ccb.csio.cdb_io.cdb_bytes[2] = 0x40;
env->ccb.csio.cdb_io.cdb_bytes[3] = 02; /* Get media catalog number. */
env->ccb.csio.cdb_io.cdb_bytes[4] = 0; /* Not used */
env->ccb.csio.cdb_io.cdb_bytes[5] = 0; /* Not used */
env->ccb.csio.cdb_io.cdb_bytes[6] = 0; /* Not used */
env->ccb.csio.cdb_io.cdb_bytes[7] = 0; /* Not used */
env->ccb.csio.cdb_io.cdb_bytes[8] = 28;
env->ccb.csio.cdb_io.cdb_bytes[9] = 0; /* Not used */
/* suc.suc_timeout = 500; */
env->ccb.csio.ccb_h.flags = CAM_DIR_OUT;
env->ccb.csio.data_ptr = buf;
env->ccb.csio.dxfer_len = sizeof(buf);
rc = _scsi_cmd (env);
if(rc == 0) {
return strdup(&buf[9]);
}
return NULL;
}
/*!
Return the the kind of drive capabilities of device.
Note: string is malloc'd so caller should free() then returned
string when done with it.
*/
static cdio_drive_cap_t
get_drive_cap_freebsd_cam (void *user_data)
{
int32_t i_drivetype = 0;
char buf[192] = { 0, };
_img_private_t *env = user_data;
int rc;
memset(&env->ccb, 0, sizeof(env->ccb));
env->ccb.ccb_h.path_id = env->cam->path_id;
env->ccb.ccb_h.target_id = env->cam->target_id;
env->ccb.ccb_h.target_lun = env->cam->target_lun;
cam_fill_csio (&(env->ccb.csio), 1, NULL,
CAM_DEV_QFRZDIS, MSG_SIMPLE_Q_TAG, NULL, 0,
sizeof(env->ccb.csio.sense_data), 0, 30*1000);
/* Initialize my_scsi_cdb as a Mode Select(6) */
CDIO_MMC_SET_COMMAND(env->ccb.csio.cdb_io.cdb_bytes, CDIO_MMC_MODE_SENSE);
env->ccb.csio.cdb_io.cdb_bytes[1] = 0x0;
env->ccb.csio.cdb_io.cdb_bytes[2] = 0x2a; /* MODE_PAGE_CAPABILITIES*/
env->ccb.csio.cdb_io.cdb_bytes[3] = 0; /* Not used */
env->ccb.csio.cdb_io.cdb_bytes[4] = 128;
env->ccb.csio.cdb_io.cdb_bytes[5] = 0; /* Not used */
/* suc.suc_timeout = 500; */
env->ccb.csio.ccb_h.flags = CAM_DIR_OUT;
env->ccb.csio.data_ptr = buf;
env->ccb.csio.dxfer_len = sizeof(buf);
rc = _scsi_cmd (env);
if(rc == 0) {
unsigned int n=buf[3]+4;
/* Reader? */
if (buf[n+5] & 0x01) i_drivetype |= CDIO_DRIVE_CAP_CD_AUDIO;
if (buf[n+2] & 0x02) i_drivetype |= CDIO_DRIVE_CAP_CD_RW;
if (buf[n+2] & 0x08) i_drivetype |= CDIO_DRIVE_CAP_DVD;
/* Writer? */
if (buf[n+3] & 0x01) i_drivetype |= CDIO_DRIVE_CAP_CD_R;
if (buf[n+3] & 0x10) i_drivetype |= CDIO_DRIVE_CAP_DVD_R;
if (buf[n+3] & 0x20) i_drivetype |= CDIO_DRIVE_CAP_DVD_RAM;
if (buf[n+6] & 0x08) i_drivetype |= CDIO_DRIVE_CAP_OPEN_TRAY;
if (buf[n+6] >> 5 != 0) i_drivetype |= CDIO_DRIVE_CAP_CLOSE_TRAY;
} else {
i_drivetype = CDIO_DRIVE_CAP_CD_AUDIO | CDIO_DRIVE_CAP_UNKNOWN;
}
return i_drivetype;
}
#endif
static int
_set_bsize (_img_private_t *env, unsigned int bsize)
{
struct
{
uint8_t reserved1;
uint8_t medium;
uint8_t reserved2;
uint8_t block_desc_length;
uint8_t density;
uint8_t number_of_blocks_hi;
uint8_t number_of_blocks_med;
uint8_t number_of_blocks_lo;
uint8_t reserved3;
uint8_t block_length_hi;
uint8_t block_length_med;
uint8_t block_length_lo;
} mh;
memset(&env->ccb, 0, sizeof(env->ccb));
memset (&mh, 0, sizeof (mh));
env->ccb.ccb_h.path_id = env->cam->path_id;
env->ccb.ccb_h.target_id = env->cam->target_id;
env->ccb.ccb_h.target_lun = env->cam->target_lun;
cam_fill_csio (&(env->ccb.csio), 1, NULL,
CAM_DEV_QFRZDIS, MSG_SIMPLE_Q_TAG, NULL, 0,
sizeof(env->ccb.csio.sense_data), 0, 30*1000);
env->ccb.csio.cdb_len = 4+1;
env->ccb.csio.cdb_io.cdb_bytes[0] = 0x15;
env->ccb.csio.cdb_io.cdb_bytes[1] = 1 << 4;
env->ccb.csio.cdb_io.cdb_bytes[4] = 12;
env->ccb.csio.data_ptr = (u_char *)&mh;
env->ccb.csio.dxfer_len = sizeof (mh);;
/* suc.suc_timeout = 500; */
env->ccb.csio.ccb_h.flags = CAM_DIR_OUT;
mh.block_desc_length = 0x08;
mh.block_length_hi = (bsize >> 16) & 0xff;
mh.block_length_med = (bsize >> 8) & 0xff;
mh.block_length_lo = (bsize >> 0) & 0xff;
return _scsi_cmd (env);
}
int
read_mode2_sector_freebsd_cam (_img_private_t *env, void *data, lsn_t lsn,
bool b_form2)
{
if ( b_form2 )
return read_mode2_sectors_freebsd_cam(env, data, lsn, 1);
else {
/* Need to pick out the data portion from a mode2 form2 frame */
char buf[M2RAW_SECTOR_SIZE] = { 0, };
int retval = read_mode2_sectors_freebsd_cam(env, buf, lsn, 1);
if ( retval ) return retval;
memcpy (((char *)data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE);
return 0;
}
}
int
read_mode2_sectors_freebsd_cam (_img_private_t *env, void *buf, lsn_t lsn,
unsigned int nblocks)
{
int retval = 0;
bool b_read_10 = false;
memset(&env->ccb,0,sizeof(env->ccb));
env->ccb.ccb_h.path_id = env->cam->path_id;
env->ccb.ccb_h.target_id = env->cam->target_id;
env->ccb.ccb_h.target_lun = env->cam->target_lun;
cam_fill_csio (&(env->ccb.csio), 1, NULL,
CAM_DEV_QFRZDIS, MSG_SIMPLE_Q_TAG, NULL, 0,
sizeof(env->ccb.csio.sense_data), 0, 30*1000);
env->ccb.csio.cdb_len = (b_read_10 ? 8 : 9) + 1;
CDIO_MMC_SET_COMMAND(env->ccb.csio.cdb_io.cdb_bytes, b_read_10
? CDIO_MMC_GPCMD_READ_10 : CDIO_MMC_GPCMD_READ_CD);
CDIO_MMC_SET_READ_LBA(env->ccb.csio.cdb_io.cdb_bytes, lsn);
CDIO_MMC_SET_READ_LENGTH(env->ccb.csio.cdb_io.cdb_bytes, nblocks);
if (!b_read_10) {
env->ccb.csio.cdb_io.cdb_bytes[1] = 0; /* sector size mode2 */
env->ccb.csio.cdb_io.cdb_bytes[9] = 0x58; /* 2336 mode2 mixed form */
}
env->ccb.csio.dxfer_len = M2RAW_SECTOR_SIZE * nblocks;
env->ccb.csio.data_ptr = buf;
/* suc.suc_timeout = 500; */
env->ccb.csio.ccb_h.flags = CAM_DIR_IN;
if (b_read_10)
{
if ((retval = _set_bsize (env, M2RAW_SECTOR_SIZE)))
goto out;
if ((retval = _scsi_cmd(env)))
{
_set_bsize (env, CDIO_CD_FRAMESIZE);
goto out;
}
retval = _set_bsize (env, CDIO_CD_FRAMESIZE);
}
else
retval = _scsi_cmd(env);
out:
return retval;
}
/*!
Return the size of the CD in logical block address (LBA) units.
*/
uint32_t
stat_size_freebsd_cam (_img_private_t *env)
{
uint8_t buf[12] = { 0, };
uint32_t retval;
memset(&env->ccb,0,sizeof(env->ccb));
env->ccb.ccb_h.path_id = env->cam->path_id;
env->ccb.ccb_h.target_id = env->cam->target_id;
env->ccb.ccb_h.target_lun = env->cam->target_lun;
cam_fill_csio (&(env->ccb.csio), 1, NULL, CAM_DEV_QFRZDIS,
MSG_SIMPLE_Q_TAG, NULL, 0,
sizeof(env->ccb.csio.sense_data), 0, 30*1000);
env->ccb.csio.cdb_len = 8+1;
CDIO_MMC_SET_COMMAND(env->ccb.csio.cdb_io.cdb_bytes, CDIO_MMC_READ_TOC);
env->ccb.csio.cdb_io.cdb_bytes[1] = 0; /* lba; msf: 0x2 */
CDIO_MMC_SET_START_TRACK(env->ccb.csio.cdb_io.cdb_bytes,
CDIO_CDROM_LEADOUT_TRACK);
env->ccb.csio.cdb_io.cdb_bytes[8] = 12; /* ? */
env->ccb.csio.data_ptr = buf;
env->ccb.csio.dxfer_len = sizeof (buf);
/*suc.suc_timeout = 500; */
env->ccb.csio.ccb_h.flags = CAM_DIR_IN;
if (_scsi_cmd(env))
return 0;
{
int i;
retval = 0;
for (i = 8; i < 12; i++)
{
retval <<= 8;
retval += buf[i];
}
}
return retval;
}
int
eject_media_freebsd_cam (_img_private_t *env)
{
if (env->gen.fd == -1)
return 2;
memset(&env->ccb,0,sizeof(env->ccb));
env->ccb.ccb_h.path_id = env->cam->path_id;
env->ccb.ccb_h.target_id = env->cam->target_id;
env->ccb.ccb_h.target_lun = env->cam->target_lun;
cam_fill_csio (&(env->ccb.csio), 1, NULL, CAM_DEV_QFRZDIS,
MSG_SIMPLE_Q_TAG, NULL, 0, sizeof(env->ccb.csio.sense_data),
0, 30*1000);
env->ccb.csio.cdb_len = 5+1;
env->ccb.csio.cdb_io.cdb_bytes[0] = CDIO_MMC_START_STOP;
env->ccb.csio.cdb_io.cdb_bytes[1] = 0x1; /* immediate */
env->ccb.csio.cdb_io.cdb_bytes[4] = 0x2; /* eject */
env->ccb.csio.data_ptr = 0;
env->ccb.csio.dxfer_len = 0;
/* suc.suc_timeout = 500; */
env->ccb.csio.ccb_h.flags = CAM_DIR_IN;
return(_scsi_cmd(env));
}
#endif /* HAVE_FREEBSD_CDROM */