|
|
|
/*
|
|
|
|
*
|
|
|
|
* $Id: k3bscsicommand_bsd.cpp 679320 2007-06-23 15:57:22Z trueg $
|
|
|
|
* Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This file is part of the K3b project.
|
|
|
|
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.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
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
* See the file "COPYING" for the exact licensing terms.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "k3bscsicommand.h"
|
|
|
|
#include "k3bdevice.h"
|
|
|
|
|
|
|
|
#include <k3bdebug.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <camlib.h>
|
|
|
|
#include <cam/scsi/scsi_message.h>
|
|
|
|
#include <cam/scsi/scsi_pass.h>
|
|
|
|
|
|
|
|
#define ERRCODE(s) ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13]))
|
|
|
|
#define EMEDIUMTYPE EINVAL
|
|
|
|
#define ENOMEDIUM ENODEV
|
|
|
|
#define CREAM_ON_ERRNO(s) do { \
|
|
|
|
switch ((s)[12]) \
|
|
|
|
{ case 0x04: errno=EAGAIN; break; \
|
|
|
|
case 0x20: errno=ENODEV; break; \
|
|
|
|
case 0x21: if ((s)[13]==0) errno=ENOSPC; \
|
|
|
|
else errno=EINVAL; \
|
|
|
|
break; \
|
|
|
|
case 0x30: errno=EMEDIUMTYPE; break; \
|
|
|
|
case 0x3A: errno=ENOMEDIUM; break; \
|
|
|
|
} \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class K3bDevice::ScsiCommand::Private
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
union ccb ccb;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void K3bDevice::ScsiCommand::clear()
|
|
|
|
{
|
|
|
|
memset (&d->ccb,0,sizeof(ccb));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned char& K3bDevice::ScsiCommand::operator[]( size_t i )
|
|
|
|
{
|
|
|
|
if( d->ccb.csio.cdb_len < i+1 )
|
|
|
|
d->ccb.csio.cdb_len = i+1;
|
|
|
|
return d->ccb.csio.cdb_io.cdb_bytes[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
int K3bDevice::ScsiCommand::transport( TransportDirection dir,
|
|
|
|
void* data,
|
|
|
|
size_t len )
|
|
|
|
{
|
|
|
|
if( !m_device )
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
m_device->usageLock();
|
|
|
|
|
|
|
|
bool needToClose = false;
|
|
|
|
if( !m_device->isOpen() ) {
|
|
|
|
needToClose = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !m_device->open( true ) ) {
|
|
|
|
m_device->usageUnlock();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
d->ccb.ccb_h.path_id = m_device->handle()->path_id;
|
|
|
|
d->ccb.ccb_h.target_id = m_device->handle()->target_id;
|
|
|
|
d->ccb.ccb_h.target_lun = m_device->handle()->target_lun;
|
|
|
|
|
|
|
|
k3bDebug() << "(K3bDevice::ScsiCommand) transport command " << TQString::number((int)d->ccb.csio.cdb_io.cdb_bytes[0], 16) << ", length: " << (int)d->ccb.csio.cdb_len << endl;
|
|
|
|
int ret=0;
|
|
|
|
int direction = CAM_DEV_TQFRZDIS;
|
|
|
|
if (!len)
|
|
|
|
direction |= CAM_DIR_NONE;
|
|
|
|
else
|
|
|
|
direction |= (dir & TR_DIR_READ)?CAM_DIR_IN : CAM_DIR_OUT;
|
|
|
|
cam_fill_csio (&(d->ccb.csio), 1, 0 /* NULL */, direction, MSG_SIMPLE_TQ_TAG, (u_int8_t *)data, len, sizeof(d->ccb.csio.sense_data), d->ccb.csio.cdb_len, 30*1000);
|
|
|
|
unsigned char * sense = (unsigned char *)&d->ccb.csio.sense_data;
|
|
|
|
|
|
|
|
ret = cam_send_ccb(m_device->handle(), &d->ccb);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
k3bDebug() << "(K3bDevice::ScsiCommand) transport failed: " << ret << endl;
|
|
|
|
|
|
|
|
if( needToClose )
|
|
|
|
m_device->close();
|
|
|
|
|
|
|
|
m_device->usageUnlock();
|
|
|
|
|
|
|
|
struct scsi_sense_data* senset = (struct scsi_sense_data*)sense;
|
|
|
|
debugError( d->ccb.csio.cdb_io.cdb_bytes[0],
|
|
|
|
senset->error_code & SSD_ERRCODE,
|
|
|
|
senset->flags & SSD_KEY,
|
|
|
|
senset->add_sense_code,
|
|
|
|
senset->add_sense_code_qual );
|
|
|
|
|
|
|
|
int result = (((senset->error_code & SSD_ERRCODE)<<24) & 0xF000 |
|
|
|
|
((senset->flags & SSD_KEY)<<16) & 0x0F00 |
|
|
|
|
(senset->add_sense_code<<8) & 0x00F0 |
|
|
|
|
(senset->add_sense_code_qual) & 0x000F );
|
|
|
|
|
|
|
|
return result ? result : ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if ((d->ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_RETQ_CMP) {
|
|
|
|
if( needToClose )
|
|
|
|
m_device->close();
|
|
|
|
m_device->usageUnlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = EIO;
|
|
|
|
// FreeBSD 5-CURRENT since 2003-08-24, including 5.2 fails to
|
|
|
|
// pull sense data automatically, at least for ATAPI transport,
|
|
|
|
// so I reach for it myself...
|
|
|
|
if ((d->ccb.csio.scsi_status==SCSI_STATUS_CHECK_COND) &&
|
|
|
|
!(d->ccb.ccb_h.status&CAM_AUTOSNS_VALID))
|
|
|
|
{
|
|
|
|
u_int8_t _sense[18];
|
|
|
|
u_int32_t resid=d->ccb.csio.resid;
|
|
|
|
|
|
|
|
memset(_sense,0,sizeof(_sense));
|
|
|
|
|
|
|
|
operator[](0) = 0x03; // REQUEST SENSE
|
|
|
|
d->ccb.csio.cdb_io.cdb_bytes[4] = sizeof(_sense);
|
|
|
|
d->ccb.csio.cdb_len = 6;
|
|
|
|
d->ccb.csio.ccb_h.flags |= CAM_DIR_IN|CAM_DIS_AUTOSENSE;
|
|
|
|
d->ccb.csio.data_ptr = _sense;
|
|
|
|
d->ccb.csio.dxfer_len = sizeof(_sense);
|
|
|
|
d->ccb.csio.sense_len = 0;
|
|
|
|
|
|
|
|
ret = cam_send_ccb(m_device->handle(), &d->ccb);
|
|
|
|
|
|
|
|
d->ccb.csio.resid = resid;
|
|
|
|
if (ret<0)
|
|
|
|
{
|
|
|
|
k3bDebug() << "(K3bDevice::ScsiCommand) transport failed (2): " << ret << endl;
|
|
|
|
ret = -1;
|
|
|
|
struct scsi_sense_data* senset = (struct scsi_sense_data*)sense;
|
|
|
|
debugError( d->ccb.csio.cdb_io.cdb_bytes[0],
|
|
|
|
senset->error_code & SSD_ERRCODE,
|
|
|
|
senset->flags & SSD_KEY,
|
|
|
|
senset->add_sense_code,
|
|
|
|
senset->add_sense_code_qual );
|
|
|
|
|
|
|
|
if( needToClose )
|
|
|
|
m_device->close();
|
|
|
|
m_device->usageUnlock();
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((d->ccb.ccb_h.status&CAM_STATUS_MASK) != CAM_RETQ_CMP)
|
|
|
|
{
|
|
|
|
k3bDebug() << "(K3bDevice::ScsiCommand) transport failed (3): " << ret << endl;
|
|
|
|
errno=EIO,-1;
|
|
|
|
ret = -1;
|
|
|
|
struct scsi_sense_data* senset = (struct scsi_sense_data*)sense;
|
|
|
|
debugError( d->ccb.csio.cdb_io.cdb_bytes[0],
|
|
|
|
senset->error_code & SSD_ERRCODE,
|
|
|
|
senset->flags & SSD_KEY,
|
|
|
|
senset->add_sense_code,
|
|
|
|
senset->add_sense_code_qual );
|
|
|
|
|
|
|
|
if( needToClose )
|
|
|
|
m_device->close();
|
|
|
|
m_device->usageUnlock();
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(sense,_sense,sizeof(_sense));
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ERRCODE(sense);
|
|
|
|
k3bDebug() << "(K3bDevice::ScsiCommand) transport failed (4): " << ret << endl;
|
|
|
|
if (ret == 0)
|
|
|
|
ret = -1;
|
|
|
|
else
|
|
|
|
CREAM_ON_ERRNO(((unsigned char *)&d->ccb.csio.sense_data));
|
|
|
|
struct scsi_sense_data* senset = (struct scsi_sense_data*)sense;
|
|
|
|
debugError( d->ccb.csio.cdb_io.cdb_bytes[0],
|
|
|
|
senset->error_code & SSD_ERRCODE,
|
|
|
|
senset->flags & SSD_KEY,
|
|
|
|
senset->add_sense_code,
|
|
|
|
senset->add_sense_code_qual );
|
|
|
|
|
|
|
|
if( needToClose )
|
|
|
|
m_device->close();
|
|
|
|
m_device->usageUnlock();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|