FreeBSD read/write and most-recent scsi sense access courtesy of Thomas Schmitt.

This commit is contained in:
R. Bernstein
2010-01-16 21:02:34 -05:00
parent 7d8b6d0a4d
commit bd096b3eca
7 changed files with 631 additions and 32 deletions

6
NEWS
View File

@@ -1,8 +1,10 @@
version 0.83 (git)
- Add Recording and retrieval of SCSI sense reply.
- Add Write/burning interface.
- Add retrieval SCSI sense reply from the most-recent MMC command.
- Add exclusive read/write access for devices which is used for experimental
writing/burning. Currently only on GNU/Linux and FreeBSD.
- MMC bug fixes
- FreeBSD drive list now shows empty drives.
- Add ability to retrieve SCSI tuple for a name and/or fake one up for
programs that wanto to cd-record compatible.
- Tolerance for OS's without timezone in their struct tm (e.g. Solaris)

View File

@@ -37,6 +37,19 @@ static const char _rcsid[] = "$Id: freebsd.c,v 1.38 2009/10/22 18:30:20 rocky Ex
#include <netinet/in.h>
/* For freebsd_dev_lock() */
#include <sys/file.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef _HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#include <cdio/sector.h>
static lba_t get_track_lba_freebsd(void *p_user_data, track_t i_track);
@@ -52,10 +65,12 @@ str_to_access_mode_freebsd(const char *psz_access_mode)
return _AM_IOCTL;
else if (!strcmp(psz_access_mode, "CAM"))
return _AM_CAM;
else if (!strcmp(psz_access_mode, "MMC_RDWR"))
return _AM_MMC_RDWR;
else if (!strcmp(psz_access_mode, "MMC_RDWR_EXCL"))
return _AM_MMC_RDWR_EXCL;
else {
cdio_warn ("unknown access type: %s. Default ioctl used.",
cdio_warn ("unknown access type: %s. Default used.",
psz_access_mode);
return default_access_mode;
}
@@ -72,6 +87,7 @@ free_freebsd (void *p_obj)
switch (p_env->access_mode) {
case _AM_CAM:
case _AM_MMC_RDWR:
case _AM_MMC_RDWR_EXCL:
free_freebsd_cam(p_env);
break;
@@ -104,6 +120,7 @@ read_audio_sectors_freebsd (void *p_user_data, void *p_buf, lsn_t i_lsn,
_img_private_t *p_env = p_user_data;
switch (p_env->access_mode) {
case _AM_CAM:
case _AM_MMC_RDWR:
case _AM_MMC_RDWR_EXCL:
return mmc_read_sectors( p_env->gen.cdio, p_buf, i_lsn,
CDIO_MMC_READ_TYPE_CDDA, i_blocks);
@@ -129,6 +146,7 @@ read_mode2_sector_freebsd (void *p_user_data, void *data, lsn_t i_lsn,
switch (p_env->access_mode) {
case _AM_CAM:
case _AM_MMC_RDWR:
case _AM_MMC_RDWR_EXCL:
return read_mode2_sector_freebsd_cam(p_env, data, i_lsn, b_form2);
case _AM_IOCTL:
@@ -151,6 +169,7 @@ read_mode2_sectors_freebsd (void *p_user_data, void *p_data, lsn_t i_lsn,
_img_private_t *p_env = p_user_data;
if ( (p_env->access_mode == _AM_CAM ||
p_env->access_mode == _AM_MMC_RDWR ||
p_env->access_mode == _AM_MMC_RDWR_EXCL)
&& b_form2 ) {
/* We have a routine that covers this case without looping. */
@@ -184,6 +203,7 @@ get_disc_last_lsn_freebsd (void *p_obj)
switch (p_env->access_mode) {
case _AM_CAM:
case _AM_MMC_RDWR:
case _AM_MMC_RDWR_EXCL:
return get_disc_last_lsn_mmc(p_env);
case _AM_IOCTL:
@@ -455,6 +475,7 @@ eject_media_freebsd (void *p_user_data)
switch (p_env->access_mode) {
case _AM_CAM:
case _AM_MMC_RDWR:
case _AM_MMC_RDWR_EXCL:
return eject_media_freebsd_cam(p_env);
case _AM_IOCTL:
@@ -479,6 +500,63 @@ audio_stop_freebsd (void *p_user_data)
return ioctl(p_env->gen.fd, CDIOCSTOP);
}
/*!
Produce a text composed from the system SCSI address tuple according to
habits of Linux 2.4 and 2.6 : "Bus,Host,Channel,Target,Lun" and store
it in generic_img_private_t.scsi_tuple.
Channel has no meaning on FreeBSD. Expect it to be 0. It is only in
the text to avoid an unnecessary difference in format.
Bus and Host will always be the same.
To be accessed via cdio_get_arg("scsi-tuple-freebsd") or "scsi-tuple".
For simplicity the FreeBSD driver also replies on "scsi-tuple-linux".
Drivers which implement this code have to return 5 valid decimal numbers
separated by comma, or empty text if no such numbers are available.
@return 1=success , 0=failure
*/
static int
set_scsi_tuple_freebsd(_img_private_t *env)
{
int bus_no = -1, host_no = -1, channel_no = -1, target_no = -1, lun_no = -1;
int ret;
char tuple[160];
ret = obtain_scsi_adr_freebsd_cam(env->gen.source_name,
&bus_no, &host_no, &channel_no,
&target_no, &lun_no);
if (ret != 1)
return 0;
if (env->gen.scsi_tuple != NULL)
free (env->gen.scsi_tuple);
env->gen.scsi_tuple = NULL;
if (bus_no < 0 || host_no < 0 || channel_no < 0 || target_no < 0 ||
lun_no < 0) {
env->gen.scsi_tuple = strdup("");
return 0;
}
sprintf(tuple, "%d,%d,%d,%d,%d",
bus_no, host_no, channel_no, target_no, lun_no);
env->gen.scsi_tuple = strdup(tuple);
return 1;
}
static bool
is_mmc_supported(void *user_data)
{
_img_private_t *env = user_data;
switch (env->access_mode) {
case _AM_IOCTL:
case _AM_NONE:
return false;
case _AM_CAM:
case _AM_MMC_RDWR:
case _AM_MMC_RDWR_EXCL:
return true;
}
/* Not reached. */
return false;
}
/*!
Return the value associated with the key "arg".
*/
@@ -495,11 +573,19 @@ get_arg_freebsd (void *user_data, const char key[])
return "ioctl";
case _AM_CAM:
return "CAM";
case _AM_MMC_RDWR:
return "MMC_RDWR";
case _AM_MMC_RDWR_EXCL:
return "MMC_RDWR_EXCL";
case _AM_NONE:
return "no access method";
}
} else if (strcmp (key, "scsi-tuple") == 0) {
if (env->gen.scsi_tuple == NULL)
set_scsi_tuple_freebsd(env);
return env->gen.scsi_tuple;
} else if (!strcmp (key, "mmc-supported?")) {
is_mmc_supported(user_data) ? "true" : "false";
}
return NULL;
}
@@ -519,6 +605,7 @@ get_mcn_freebsd (const void *p_user_data) {
switch (p_env->access_mode) {
case _AM_CAM:
case _AM_MMC_RDWR:
case _AM_MMC_RDWR_EXCL:
return mmc_get_mcn(p_env->gen.cdio);
case _AM_IOCTL:
@@ -540,6 +627,7 @@ get_drive_cap_freebsd (const void *p_user_data,
switch (p_env->access_mode) {
case _AM_CAM:
case _AM_MMC_RDWR:
case _AM_MMC_RDWR_EXCL:
get_drive_cap_mmc (p_user_data, p_read_cap, p_write_cap, p_misc_cap);
case _AM_IOCTL:
@@ -571,12 +659,17 @@ run_mmc_cmd_freebsd( void *p_user_data, unsigned int i_timeout_ms,
unsigned int i_buf, /*in/out*/ void *p_buf )
{
const _img_private_t *p_env = p_user_data;
int ret;
switch (p_env->access_mode) {
case _AM_CAM:
case _AM_MMC_RDWR:
case _AM_MMC_RDWR_EXCL:
return run_mmc_cmd_freebsd_cam( p_user_data, i_timeout_ms, i_cdb, p_cdb,
e_direction, i_buf, p_buf );
ret = run_mmc_cmd_freebsd_cam( p_user_data, i_timeout_ms, i_cdb, p_cdb,
e_direction, i_buf, p_buf );
if (ret != 0)
return DRIVER_OP_ERROR;
return 0;
case _AM_IOCTL:
return DRIVER_OP_UNSUPPORTED;
case _AM_NONE:
@@ -680,7 +773,7 @@ cdio_get_devices_freebsd (void)
char drive[40];
char **drives = NULL;
unsigned int num_drives=0;
bool exists=true;
bool exists = true, have_cam_drive = false;
char c;
/* Scan the system for CD-ROM drives.
@@ -705,15 +798,31 @@ cdio_get_devices_freebsd (void)
*/
/* Scan SCSI and CAM devices */
exists = true;
for ( c='0'; exists && c <='9'; c++ ) {
sprintf(drive, "/dev/cd%c%s", c, DEVICE_POSTFIX);
exists = cdio_is_cdrom(drive, NULL);
if ( exists ) {
cdio_add_device_list(&drives, drive, &num_drives);
have_cam_drive = true;
}
}
/* Scan are ATAPI devices */
/* Scan ATAPI devices */
/* ??? ts 9 Jan 2009
For now i assume atapicam running if a cdN device was found.
man atapicam strongly discourages to mix usage of CAM and ATAPI device.
So on the risk to sabotage systems without atapicam but with real old
SCSI drives, i list no ATAPI addresses if there was a CAM/SCSI address.
exists = !have_cam_drive;
ts 13 Jan 2009
Regrettably USB drives appear as SCSI drives. We rather need to determine
whether atapicam runs, or to make pairs of cd and acd.
*/
exists = true;
for ( c='0'; exists && c <='9'; c++ ) {
@@ -854,6 +963,108 @@ get_access_mode(const char *psz_source)
}
return "CAM";
}
/* Lock the inode associated to dev_fd and the inode associated to devname.
Return OS errno, number of pass device of dev_fd, locked fd to devname,
error message.
A return value of > 0 means success, <= 0 means failure.
*/
static int freebsd_dev_lock(int dev_fd, char *devname,
int *os_errno, int *pass_dev_no, int *lock_fd, char msg[4096],
int flag)
{
int lock_denied = 0, fd_stbuf_valid, name_stbuf_valid, i, pass_l = 100;
int max_retry = 3, tries;
struct stat fd_stbuf, name_stbuf;
char pass_name[16], *lock_name;
*os_errno = 0;
*pass_dev_no = -1;
*lock_fd = -1;
msg[0] = 0;
fd_stbuf_valid = !fstat(dev_fd, &fd_stbuf);
/* Try to find name of pass device by inode number */
lock_name = (char *) "effective device";
if(fd_stbuf_valid) {
for (i = 0; i < pass_l; i++) {
sprintf(pass_name, "/dev/pass%d", i);
if (stat(pass_name, &name_stbuf) != -1)
if(fd_stbuf.st_ino == name_stbuf.st_ino &&
fd_stbuf.st_dev == name_stbuf.st_dev)
break;
}
if (i < pass_l) {
lock_name = pass_name;
*pass_dev_no = i;
}
}
name_stbuf_valid = !stat(devname, &name_stbuf);
for (tries= 0; tries <= max_retry; tries++) {
lock_denied = flock(dev_fd, LOCK_EX | LOCK_NB);
*os_errno = errno;
if (lock_denied) {
if (errno == EAGAIN && tries < max_retry) {
/* <<< debugging
fprintf(stderr, "\nlibcdio_DEBUG: EAGAIN , tries= %d\n", tries);
*/
usleep(2000000);
continue;
}
sprintf(msg,
"Device busy. flock(LOCK_EX) failed on %s of %s",
strlen(lock_name) > 2000 || *pass_dev_no < 0 ?
"pass device" : lock_name,
strlen(devname) > 2000 ? "drive" : devname);
return 0;
}
break;
}
/*
fprintf(stderr, "libburn_DEBUG: flock obtained on %s of %s\n",
lock_name, devname);
*/
/* Eventually lock the official device node too */
if (fd_stbuf_valid && name_stbuf_valid &&
(fd_stbuf.st_ino != name_stbuf.st_ino ||
fd_stbuf.st_dev != name_stbuf.st_dev)) {
*lock_fd = open(devname, O_RDONLY);
if (*lock_fd == 0) {
close(*lock_fd);
*lock_fd = -1;
} if (*lock_fd > 0) {
for (tries = 0; tries <= max_retry; tries++) {
lock_denied = flock(*lock_fd, LOCK_EX | LOCK_NB);
if (lock_denied) {
if (errno == EAGAIN && tries < max_retry) {
usleep(2000000);
continue;
}
close(*lock_fd);
*lock_fd = -1;
sprintf(msg, "Device busy. flock(LOCK_EX) failed on %s",
strlen(devname) > 4000 ? "drive" : devname);
return 0;
}
break;
}
}
/*
fprintf(stderr, "libburn_DEBUG: flock obtained on %s\n",
devname);
*/
}
return 1;
}
#endif /*HAVE_FREEBSD_CDROM*/
/*!
@@ -882,6 +1093,7 @@ cdio_open_am_freebsd (const char *psz_orig_source_name,
CdIo *ret;
_img_private_t *_data;
char *psz_source_name;
int open_access_mode; /* Access mode passed to cdio_generic_init. */
if (!psz_access_mode)
psz_access_mode = get_access_mode(psz_orig_source_name);
@@ -959,7 +1171,39 @@ cdio_open_am_freebsd (const char *psz_orig_source_name,
ret = cdio_new ((void *)_data, &_funcs);
if (ret == NULL) return NULL;
if (cdio_generic_init(_data, O_RDONLY))
open_access_mode = 0;
if (_AM_MMC_RDWR == _data->access_mode) {
open_access_mode |= O_RDWR;
} else if (_AM_MMC_RDWR_EXCL == _data->access_mode) {
open_access_mode |= O_RDWR;
} else {
open_access_mode |= O_RDONLY;
}
/*
fprintf(stderr,
"libcdio_DEBUG: am = %d (MMC_RDWR_EXCL = %d), open = %d (O_RDWR = %d)\n",
_data->access_mode, _AM_MMC_RDWR_EXCL, open_access_mode, O_RDWR);
*/
if (cdio_generic_init(_data, open_access_mode)) {
if (_AM_MMC_RDWR_EXCL == _data->access_mode) {
int os_errno, pass_dev_no = -1, flock_fd = -1, lock_result;
char msg[4096];
lock_result = freebsd_dev_lock(_data->gen.fd, _data->gen.source_name,
&os_errno, &pass_dev_no, &flock_fd, msg, 0);
if (lock_result <= 0) {
cdio_warn ("%s", msg);
cdio_generic_free (_data);
return NULL;
}
/* One should rather keep this fd open until _data->gen.fd gets closed.
It eventually locks a device sibling of _data->gen.source_name.
*/
if (flock_fd > 0)
close(flock_fd);
}
if ( _data->access_mode == _AM_IOCTL ) {
return ret;
} else {
@@ -970,7 +1214,7 @@ cdio_open_am_freebsd (const char *psz_orig_source_name,
return NULL;
}
}
else {
} else {
cdio_generic_free (_data);
return NULL;
}

View File

@@ -114,6 +114,7 @@ typedef enum {
_AM_NONE,
_AM_IOCTL,
_AM_CAM,
_AM_MMC_RDWR,
_AM_MMC_RDWR_EXCL,
} access_mode_t;
@@ -212,7 +213,7 @@ bool read_toc_freebsd_ioctl (_img_private_t *env);
Return 0 if no error.
*/
int run_mmc_cmd_freebsd_cam( const void *p_user_data,
int run_mmc_cmd_freebsd_cam( void *p_user_data,
unsigned int i_timeout_ms,
unsigned int i_cdb,
const mmc_cdb_t *p_cdb,
@@ -228,4 +229,12 @@ lsn_t get_disc_last_lsn_freebsd_ioctl (_img_private_t *_obj);
bool init_freebsd_cam (_img_private_t *env);
void free_freebsd_cam (void *user_data);
/** Try to obtain SCSI address tuple of path.
@return 1 is success , 0 is failure
*/
int obtain_scsi_adr_freebsd_cam(char *path,
int *bus_no, int *host_no, int *channel_no,
int *target_no, int *lun_no);
#endif /*HAVE_FREEBSD_CDROM*/

View File

@@ -1,7 +1,5 @@
/*
$Id: freebsd_cam.c,v 1.12 2008/04/21 18:30:20 karl Exp $
Copyright (C) 2004, 2005, 2008, 2009 Rocky Bernstein <rocky@gnu.org>
Copyright (C) 2004, 2005, 2008, 2009, 2010 Rocky Bernstein <rocky@gnu.org>
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
@@ -52,16 +50,17 @@ static const char _rcsid[] = "$Id: freebsd_cam.c,v 1.12 2008/04/21 18:30:20 karl
Return 0 if no error.
*/
int
run_mmc_cmd_freebsd_cam( const void *p_user_data, unsigned int i_timeout_ms,
run_mmc_cmd_freebsd_cam( void *p_user_data, unsigned int i_timeout_ms,
unsigned int i_cdb, const mmc_cdb_t *p_cdb,
cdio_mmc_direction_t e_direction,
unsigned int i_buf, /*in/out*/ void *p_buf )
{
const _img_private_t *p_env = p_user_data;
int i_status;
_img_private_t *p_env = p_user_data;
int i_status, sense_size;
int direction = CAM_DEV_QFRZDIS;
union ccb ccb;
p_env->gen.scsi_mmc_sense_valid = 0;
if (!p_env || !p_env->cam) return -2;
memset(&ccb, 0, sizeof(ccb));
@@ -71,6 +70,8 @@ run_mmc_cmd_freebsd_cam( const void *p_user_data, unsigned int i_timeout_ms,
ccb.ccb_h.target_lun = p_env->cam->target_lun;
ccb.ccb_h.timeout = i_timeout_ms;
if (SCSI_MMC_DATA_NONE == e_direction)
i_buf = 0;
if (!i_buf)
direction |= CAM_DIR_NONE;
else
@@ -94,13 +95,28 @@ run_mmc_cmd_freebsd_cam( const void *p_user_data, unsigned int i_timeout_ms,
{
return 0;
}
/* Record SCSI sense reply for API call mmc_last_cmd_sense().
*/
sense_size = ccb.csio.sense_len;
if (sense_size > sizeof(p_env->gen.scsi_mmc_sense))
sense_size = sizeof(p_env->gen.scsi_mmc_sense);
memcpy((void *) p_env->gen.scsi_mmc_sense, &ccb.csio.sense_data, sense_size);
p_env->gen.scsi_mmc_sense_valid = sense_size;
errno = EIO;
i_status = ERRCODE(((unsigned char *)&ccb.csio.sense_data));
if (i_status == 0)
i_status = -1;
else
CREAM_ON_ERRNO(((unsigned char *)&ccb.csio.sense_data));
cdio_warn ("transport failed: %d", i_status);
/* There are many harmless or intentional reasons why to get an SCSI
error condition. Higher levels should decide whether this is an incident
or just a normal outcome.
cdio_warn ("scsi error condition detected : 0x%X", i_status);
*/
return i_status;
}
@@ -257,4 +273,243 @@ eject_media_freebsd_cam (_img_private_t *p_env)
SCSI_MMC_DATA_WRITE, 0, &buf);
}
/* This is a CAM based device enumerator.
Currently its only purpose is to eventually obtain the info needed for
cdio_get_arg("scsi-tuple")
*/
/* Stemming from code in libburn/sg-freebsd.c ,
originally contributed by Alexander Nedotsukov <bland@FreeBSD.org>,
without copyright claim to libburn in October 2006.
Contributed by libburn and adapted to libcdio without copyright claim
in January 2010.
*/
struct burn_drive_enumeration_state {
int fd;
union ccb ccb;
unsigned int i;
int skip_device;
};
typedef struct burn_drive_enumeration_state *burn_drive_enumerator_t;
/* Some helper functions for scsi_give_next_adr() */
static int sg_init_enumerator(burn_drive_enumerator_t *idx_)
{
struct burn_drive_enumeration_state *idx;
int bufsize;
idx = malloc(sizeof(*idx));
if (idx == NULL) {
cdio_warn("cannot malloc memory for CAM based drive enumerator");
return -1;
}
idx->skip_device = 0;
if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) {
cdio_warn("could not open %s (errno = %d \"%s\")",
XPT_DEVICE, errno, strerror(errno));
free(idx);
idx = NULL;
return -1;
}
bzero(&(idx->ccb), sizeof(union ccb));
idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
idx->ccb.ccb_h.func_code = XPT_DEV_MATCH;
bufsize = sizeof(struct dev_match_result) * 100;
idx->ccb.cdm.match_buf_len = bufsize;
idx->ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
if (idx->ccb.cdm.matches == NULL) {
cdio_warn("cannot malloc memory for CAM enumerator matches");
close(idx->fd);
free(idx);
return -1;
}
idx->ccb.cdm.num_matches = 0;
idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */
/*
* We fetch all nodes, since we display most of them in the default
* case, and all in the verbose case.
*/
idx->ccb.cdm.num_patterns = 0;
idx->ccb.cdm.pattern_buf_len = 0;
*idx_ = idx;
return 1;
}
static void sg_destroy_enumerator(burn_drive_enumerator_t *idx_)
{
struct burn_drive_enumeration_state *idx = *idx_;
if(idx->fd != -1)
close(idx->fd);
free(idx->ccb.cdm.matches);
free(idx);
*idx_ = NULL;
}
static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx_)
{
struct burn_drive_enumeration_state *idx = *idx_;
/*
* We do the ioctl multiple times if necessary, in case there are
* more than 100 nodes in the EDT.
*/
if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) {
cdio_warn("error sending CAMIOCOMMAND ioctl, (errno = %d \"%s\")",
errno, strerror(errno));
return -1;
}
if ((idx->ccb.ccb_h.status != CAM_REQ_CMP)
|| ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST)
&& (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
cdio_warn("got CAM error %#x, CDM error %d\n",
idx->ccb.ccb_h.status, idx->ccb.cdm.status);
return -1;
}
return 1;
}
/** Returns the next index object state and the next enumerated drive address.
@param idx An opaque handle. Make no own theories about it.
@param adr Takes the reply
@param adr_size Gives maximum size of reply including final 0
@param initialize 1 = start new,
0 = continue, use no other values for now
-1 = finish
@return 1 = reply is a valid address , 0 = no further address available
-1 = severe error (e.g. adr_size too small)
*/
/* This would be the public interface of the enumerator.
In libcdio it is private for now.
*/
static
int give_next_adr_freebsd_cam(burn_drive_enumerator_t *idx_,
char adr[], int adr_size, int initialize)
{
struct burn_drive_enumeration_state *idx;
int ret;
if (initialize == 1) {
ret = sg_init_enumerator(idx_);
if (ret<=0)
return ret;
} else if (initialize == -1) {
sg_destroy_enumerator(idx_);
return 0;
}
idx = *idx_;
do {
if (idx->i >= idx->ccb.cdm.num_matches) {
ret = sg_next_enumeration_buffer(idx_);
if (ret<=0)
return -1;
idx->i = 0;
} else
(idx->i)++;
while (idx->i < idx->ccb.cdm.num_matches) {
switch (idx->ccb.cdm.matches[idx->i].type) {
case DEV_MATCH_BUS:
break;
case DEV_MATCH_DEVICE: {
struct device_match_result* result;
result = &(idx->ccb.cdm.matches[idx->i].result.device_result);
if (result->flags & DEV_RESULT_UNCONFIGURED)
idx->skip_device = 1;
else
idx->skip_device = 0;
break;
}
case DEV_MATCH_PERIPH: {
struct periph_match_result* result;
result = &(idx->ccb.cdm.matches[idx->i].result.periph_result);
/* A specialized CD drive enumerator would have to test for
strcmp(result->periph_name, "cd") != 0
rather than
strcmp(result->periph_name, "pass") == 0
*/
if (idx->skip_device ||
strcmp(result->periph_name, "pass") == 0)
break;
ret = snprintf(adr, adr_size, "/dev/%s%d",
result->periph_name, result->unit_number);
if(ret >= adr_size)
return -1;
/* Found next enumerable address */
return 1;
}
default:
/* fprintf(stderr, "unknown match type\n"); */
break;
}
(idx->i)++;
}
} while ((idx->ccb.ccb_h.status == CAM_REQ_CMP)
&& (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE));
return 0;
}
/** Try to obtain SCSI address tuple of path.
@return 1 is success , 0 is failure
*/
int obtain_scsi_adr_freebsd_cam(char *path,
int *bus_no, int *host_no, int *channel_no,
int *target_no, int *lun_no)
{
burn_drive_enumerator_t idx;
int ret;
char buf[64];
struct periph_match_result* result;
ret = sg_init_enumerator(&idx);
if (ret <= 0)
return 0;
while(1) {
ret = give_next_adr_freebsd_cam(&idx, buf, sizeof(buf), 0);
if (ret <= 0)
break;
if (strcmp(path, buf) == 0) {
result = &(idx->ccb.cdm.matches[idx->i].result.periph_result);
*bus_no = result->path_id;
*host_no = result->path_id;
*channel_no = 0;
*target_no = result->target_id;
*lun_no = result->target_lun;
sg_destroy_enumerator(&idx);
return 1;
}
}
sg_destroy_enumerator(&idx);
return (0);
}
#endif /* HAVE_FREEBSD_CDROM */

View File

@@ -48,6 +48,17 @@ cdio_is_cdrom_freebsd_ioctl(char *drive, char *mnttype)
return(false);
}
/* ts 13 Jan 2009
The ioctl below seems to be of little significance if one does not insist
in readable media.
Various of its failures are not caused by not being a CD drive
but by unreadable media situations. A burn program must handle these
situations rather than refusing to see the drive.
*/
return(true);
#ifndef Libcdio_on_freeBSD_unsuitable_code
/* If it does exist, verify that it's an available CD-ROM */
cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
@@ -56,16 +67,41 @@ cdio_is_cdrom_freebsd_ioctl(char *drive, char *mnttype)
ENODEV means there's no drive present. */
if ( cdfd >= 0 ) {
if ( ioctl(cdfd, CDIOREADTOCHEADER, &tochdr) != -1 ) {
int ret;
ret = ioctl(cdfd, CDIOREADTOCHEADER, &tochdr);
/*
fprintf(stderr, "libcdio_DEBUG: ioctl(\"%s\", (O_RDONLY|O_EXCL|O_NONBLOCK) = %d (errno= %d)\n", drive, ret, errno);
*/
if ( ret != -1 ) {
is_cd = true;
} else if ( errno == ENXIO ) { /* Device not configured */
/* ts 9 Jan 2010 , FreeBSD 8.0
This error is issued with CAM device cd0 if no media is loaded.
*/
is_cd = true;
} else if ( errno == EIO ) { /* I/O error */
/* ts 9 Jan 2010 , FreeBSD 8.0
This error is issued with ATAPI device acd0 if no media is loaded.
*/
is_cd = true;
} else if ( errno == EINVAL ) { /* Invalid argument */
/* ts 13 Jan 2010 , FreeBSD 8.0
This error is issued with USB device cd1 if a blank CD-RW is loaded.
*/
is_cd = true;
}
close(cdfd);
}
/* Even if we can't read it, it might be mounted */
else if ( mnttype && (strcmp(mnttype, "iso9660") == 0) ) {
} else if ( mnttype && (strcmp(mnttype, "iso9660") == 0) ) {
/* Even if we can't read it, it might be mounted */
is_cd = true;
}
return(is_cd);
#endif /* Libcdio_on_freeBSD_unsuitable_codE */
}
/*!

View File

@@ -387,8 +387,6 @@ get_arg_linux (void *env, const char key[])
}
} else if (!strcmp (key, "scsi-tuple")) {
return _obj->gen.scsi_tuple;
} else if (!strcmp (key, "scsi-tuple-linux")) {
return _obj->gen.scsi_tuple;
}
return NULL;
}

View File

@@ -566,6 +566,10 @@ tmmc_test(char *drive_path, int flag)
insufficient access mode "IOCTL" */
static int emul_lack_of_wperm = 0;
/* If non-0 then demand that cdio_get_arg(,"scsi-tuple") return non-NULL */
static int demand_scsi_tuple = 0;
const char *scsi_tuple;
old_log_level = cdio_loglevel_default;
verbose = flag & 1;
@@ -589,6 +593,44 @@ tmmc_test(char *drive_path, int flag)
*/
cdio_loglevel_default = CDIO_LOG_WARN;
/* Test availability of info for cdrecord style adresses .
*/
scsi_tuple = cdio_get_arg(p_cdio, "scsi-tuple");
if (scsi_tuple == NULL) {
if (demand_scsi_tuple) {
fprintf(stderr, "Error: cdio_get_arg(\"scsi-tuple\") returns NULL.\n");
{ret = 6; goto ex;}
} else if (flag & 1)
fprintf(stderr, "cdio_get_arg(\"scsi-tuple\") returns NULL.\n");
} else if (flag & 1)
printf("Drive '%s' has cdio_get_arg(\"scsi-tuple\") = '%s'\n",
drive_path, scsi_tuple);
/* Test availability of sense reply in case of unready drive.
E.g. if the tray is already ejected.
*/
ret = tmmc_test_unit_ready(p_cdio, &sense_avail, sense, !!verbose);
if (ret != 0 && sense_avail < 18) {
fprintf(stderr,
"Error: Drive not ready. Only %d sense bytes. Expected >= 18.\n",
sense_avail);
{ret = 2; goto ex;}
}
/* Provoke sense reply by requesting inappropriate mode page 3Eh */
ret = tmmc_mode_sense(p_cdio, &sense_avail, sense,
0x3e, 0, alloc_len, buf, &buf_fill, !!verbose);
if (ret != 0 && sense_avail < 18) {
fprintf(stderr,
"Error: Deliberately illegal command yields only %d sense bytes. Expected >= 18.\n",
sense_avail);
{ret = 2; goto ex;}
} else if(ret == 0) {
fprintf(stderr,
"Warning: tmmc_mode_sense() cannot provoke failure by mode page 3Eh\n");
fprintf(stderr,
"Hint: Consider to set in tmmc_test(): with_tray_dance = 1\n");
}
/* Test availability of sense reply in case of unready drive.
E.g. if the tray is already ejected.
@@ -687,6 +729,7 @@ ex:;
int
main(int argc, const char *argv[])
{
const char *scsi_tuple;
CdIo_t *p_cdio;
char **ppsz_drives=NULL;
const char *psz_source = NULL;
@@ -705,6 +748,8 @@ main(int argc, const char *argv[])
p_cdio = cdio_open(ppsz_drives[0], DRIVER_DEVICE);
if (p_cdio) {
const char *psz_have_mmc = cdio_get_arg(p_cdio, "mmc-supported?");
psz_source = cdio_get_arg(p_cdio, "source");
if (0 != strncmp(psz_source, ppsz_drives[0],
strlen(ppsz_drives[0]))) {
@@ -713,19 +758,29 @@ main(int argc, const char *argv[])
psz_source, ppsz_drives[0]);
exit(1);
}
if (psz_have_mmc && 0 == strncmp("true", psz_have_mmc, sizeof("true"))) {
scsi_tuple = cdio_get_arg(p_cdio, "scsi-tuple");
if (scsi_tuple == NULL) {
fprintf(stderr, "cdio_get_arg(\"scsi-tuple\") returns NULL.\n");
exit(3);
} else if (cdio_loglevel_default == CDIO_LOG_DEBUG)
printf("Drive '%s' has cdio_get_arg(\"scsi-tuple\") = '%s'\n",
psz_source, scsi_tuple);
/* Test the MMC enhancements of version 0.83 in december 2009 */
ret = tmmc_test(ppsz_drives[0],
cdio_loglevel_default == CDIO_LOG_DEBUG);
if (ret != 0) exit(ret + 16);
}
cdio_destroy(p_cdio);
} else {
fprintf(stderr, "cdio_open_linux('%s') failed\n", ppsz_drives[0]);
fprintf(stderr, "cdio_open('%s') failed\n", ppsz_drives[0]);
exit(2);
}
cdio_destroy(p_cdio);
/* Test the MMC enhancements of version 0.83 in december 2009 */
ret = tmmc_test(ppsz_drives[0], cdio_loglevel_default == CDIO_LOG_DEBUG);
if (ret != 0)
exit(ret + 16);
cdio_free_device_list(ppsz_drives);
return 0;