FreeBSD read/write and most-recent scsi sense access courtesy of Thomas Schmitt.
This commit is contained in:
6
NEWS
6
NEWS
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user