File indexing completed on 2024-04-21 04:49:44

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2011 Andriy Gapon <avg@FreeBSD.org>
0004     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "k3bscsicommand.h"
0010 #include "k3bdevice.h"
0011 
0012 #include <QDebug>
0013 
0014 #include <stdio.h>
0015 #include <errno.h>
0016 #include <camlib.h>
0017 #include <cam/scsi/scsi_message.h>
0018 #include <cam/scsi/scsi_pass.h>
0019 
0020 namespace /*anonymous*/
0021 {
0022     inline int sense_to_err( const struct scsi_sense_data& s )
0023     {
0024         int errorCode, senseKey, addSenseCode, addSenseCodeQual;
0025         scsi_extract_sense( (struct scsi_sense_data*) &s, &errorCode,
0026                             &senseKey, &addSenseCode, &addSenseCodeQual );
0027         return (errorCode << 24) | (senseKey << 16) |
0028            (addSenseCode << 8) | addSenseCodeQual;
0029     }
0030 }
0031 
0032 
0033 class K3b::Device::ScsiCommand::Private
0034 {
0035     typedef union ccb CCB;
0036 
0037 public:
0038     Private();
0039     int transport( const Device* device, TransportDirection dir, void* data, size_t len );
0040     unsigned char& operator[]( size_t i );
0041     void clear();
0042     const CCB& get_ccb() { return ccb; }
0043 
0044 private:
0045     CCB ccb;
0046 };
0047 
0048 
0049 void K3b::Device::ScsiCommand::clear()
0050 {
0051     d->clear();
0052 }
0053 
0054 unsigned char& K3b::Device::ScsiCommand::operator[]( size_t i )
0055 {
0056     return (*d)[i];
0057 }
0058 
0059 int K3b::Device::ScsiCommand::transport( TransportDirection dir,
0060                                          void* data,
0061                                          size_t len )
0062 {
0063     if( !m_device )
0064         return -1;
0065 
0066     m_device->usageLock();
0067 
0068     bool needToClose = false;
0069     if( !m_device->isOpen() ) {
0070         needToClose = true;
0071     }
0072 
0073     if( !m_device->open( true ) ) {
0074         m_device->usageUnlock();
0075         return -1;
0076     }
0077 
0078     int ret = d->transport( m_device, dir, data, len );
0079     if( ret != 0 ) {
0080         const struct scsi_sense_data& s = d->get_ccb().csio.sense_data;
0081         int errorCode, senseKey, addSenseCode, addSenseCodeQual;
0082         scsi_extract_sense( (struct scsi_sense_data*) &s, &errorCode, &senseKey,
0083                             &addSenseCode, &addSenseCodeQual );
0084         debugError( d->get_ccb().csio.cdb_io.cdb_bytes[0],
0085                     errorCode,
0086                     senseKey,
0087                     addSenseCode,
0088                     addSenseCodeQual );
0089     }
0090 
0091     if( needToClose )
0092         m_device->close();
0093     m_device->usageUnlock();
0094 
0095     return ret;
0096 }
0097 
0098 K3b::Device::ScsiCommand::Private::Private()
0099 {
0100     clear();
0101 }
0102 
0103 void K3b::Device::ScsiCommand::Private::clear()
0104 {
0105     memset( &ccb, 0, sizeof(ccb) );
0106 }
0107 
0108 unsigned char& K3b::Device::ScsiCommand::Private::operator[]( size_t i )
0109 {
0110     if( ccb.csio.cdb_len < i + 1 )
0111         ccb.csio.cdb_len = i + 1;
0112     return ccb.csio.cdb_io.cdb_bytes[i];
0113 }
0114 
0115 int K3b::Device::ScsiCommand::Private::transport( const Device* device, TransportDirection dir, void* data, size_t len )
0116 {
0117     ccb.ccb_h.path_id    = device->handle()->path_id;
0118     ccb.ccb_h.target_id  = device->handle()->target_id;
0119     ccb.ccb_h.target_lun = device->handle()->target_lun;
0120 
0121     qDebug() << "(K3b::Device::ScsiCommand) transport command " << commandString(ccb.csio.cdb_io.cdb_bytes[0])
0122              << " (" << QString::number((int)ccb.csio.cdb_io.cdb_bytes[0], 16) << "), length: " << (int)ccb.csio.cdb_len;
0123     int direction = CAM_DEV_QFRZDIS;
0124     if (!len)
0125         direction |= CAM_DIR_NONE;
0126     else
0127         direction |= (dir & TR_DIR_READ) ? CAM_DIR_IN : CAM_DIR_OUT;
0128 
0129     cam_fill_csio( &(ccb.csio), 1, NULL, direction, MSG_SIMPLE_Q_TAG, (uint8_t*)data, len, sizeof(ccb.csio.sense_data), ccb.csio.cdb_len, 30*1000 );
0130     int ret = cam_send_ccb( device->handle(), &ccb );
0131     if( ret < 0 ) {
0132         qCritical() << "(K3b::Device::ScsiCommand) transport cam_send_ccb failed: ret = " << ret
0133                  << ", errno = " << errno << ", cam_errbuf = " << cam_errbuf;
0134         return 1;
0135     }
0136     else if( (ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP ) {
0137         qDebug() << "(K3b::Device::ScsiCommand) transport succeeded";
0138         return 0;
0139     }
0140 
0141     qDebug() << "(K3b::Device::ScsiCommand) transport command failed: scsi_status = " << QString::number(ccb.csio.scsi_status, 16);
0142 
0143     if( ccb.csio.scsi_status == SCSI_STATUS_CHECK_COND &&
0144         !(ccb.ccb_h.status & CAM_AUTOSNS_VALID) &&
0145         ccb.csio.cdb_io.cdb_bytes[0] != MMC_REQUEST_SENSE )
0146     {
0147         qDebug() << "(K3b::Device::ScsiCommand) transport requesting sense data";
0148 
0149         struct scsi_sense_data sense;
0150         ScsiCommand::Private cmd;
0151         cmd[0] = MMC_REQUEST_SENSE;
0152         cmd[4] = SSD_MIN_SIZE;
0153         cmd[5] = 0; // Necessary to set the proper command length
0154 
0155         memset( &sense, 0, sizeof(sense) );
0156         ret = cmd.transport( device, TR_DIR_READ, &sense, SSD_MIN_SIZE );
0157         if( ret < 0 )
0158         {
0159             qWarning() << "(K3b::Device::ScsiCommand) transport getting sense data failed: " << ret;
0160             return 1;
0161         }
0162 
0163         ccb.csio.sense_data = sense;
0164         ccb.ccb_h.status |= CAM_AUTOSNS_VALID;
0165     }
0166 
0167     if( !(ccb.ccb_h.status & CAM_AUTOSNS_VALID) )
0168         qDebug() << "(K3b::Device::ScsiCommand) sense data is not available";
0169 
0170     ret = sense_to_err(ccb.csio.sense_data);
0171     if( ret == 0 )
0172         ret = 1;
0173     qDebug() << "(K3b::Device::ScsiCommand) transport failed: " << ret;
0174     return ret;
0175 }