/* * * $Id: k3bscsicommand_bsd.cpp 679320 2007-06-23 15:57:22Z trueg $ * Copyright (C) 2003-2007 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2007 Sebastian Trueg * * 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 #include #include #include #include #include #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_REQ_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_REQ_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; }