1 /*
2     SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org>
3     SPDX-FileCopyrightText: 2011 Andriy Gapon <avg@FreeBSD.org>
4     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
5 
6     SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 #include "k3bscsicommand.h"
10 #include "k3bdevice.h"
11 
12 #include <QDebug>
13 
14 #include <stdio.h>
15 #include <errno.h>
16 #include <camlib.h>
17 #include <bus/cam/scsi/scsi_message.h>
18 #include <bus/cam/scsi/scsi_pass.h>
19 
20 namespace /*anonymous*/
21 {
sense_to_err(const struct scsi_sense_data & s)22     inline int sense_to_err( const struct scsi_sense_data& s )
23     {
24         int errorCode, senseKey, addSenseCode, addSenseCodeQual;
25         scsi_extract_sense( (struct scsi_sense_data*) &s, &errorCode,
26                             &senseKey, &addSenseCode, &addSenseCodeQual );
27         return (errorCode << 24) | (senseKey << 16) |
28 	       (addSenseCode << 8) | addSenseCodeQual;
29     }
30 }
31 
32 
33 class K3b::Device::ScsiCommand::Private
34 {
35     typedef union ccb CCB;
36 
37 public:
38     Private();
39     int transport( const Device* device, TransportDirection dir, void* data, size_t len );
40     unsigned char& operator[]( size_t i );
41     void clear();
get_ccb()42     const CCB& get_ccb() { return ccb; }
43 
44 private:
45     CCB ccb;
46 };
47 
48 
clear()49 void K3b::Device::ScsiCommand::clear()
50 {
51     d->clear();
52 }
53 
operator [](size_t i)54 unsigned char& K3b::Device::ScsiCommand::operator[]( size_t i )
55 {
56     return (*d)[i];
57 }
58 
transport(TransportDirection dir,void * data,size_t len)59 int K3b::Device::ScsiCommand::transport( TransportDirection dir,
60                                          void* data,
61                                          size_t len )
62 {
63     if( !m_device )
64         return -1;
65 
66     m_device->usageLock();
67 
68     bool needToClose = false;
69     if( !m_device->isOpen() ) {
70         needToClose = true;
71     }
72 
73     if( !m_device->open( true ) ) {
74         m_device->usageUnlock();
75         return -1;
76     }
77 
78     int ret = d->transport( m_device, dir, data, len );
79     if( ret != 0 ) {
80         const struct scsi_sense_data& s = d->get_ccb().csio.sense_data;
81         int errorCode, senseKey, addSenseCode, addSenseCodeQual;
82         scsi_extract_sense( (struct scsi_sense_data*) &s, &errorCode, &senseKey,
83                             &addSenseCode, &addSenseCodeQual );
84         debugError( d->get_ccb().csio.cdb_io.cdb_bytes[0],
85                     errorCode,
86                     senseKey,
87                     addSenseCode,
88                     addSenseCodeQual );
89     }
90 
91     if( needToClose )
92         m_device->close();
93     m_device->usageUnlock();
94 
95     return ret;
96 }
97 
Private()98 K3b::Device::ScsiCommand::Private::Private()
99 {
100     clear();
101 }
102 
clear()103 void K3b::Device::ScsiCommand::Private::clear()
104 {
105     memset( &ccb, 0, sizeof(ccb) );
106 }
107 
operator [](size_t i)108 unsigned char& K3b::Device::ScsiCommand::Private::operator[]( size_t i )
109 {
110     if( ccb.csio.cdb_len < i + 1 )
111         ccb.csio.cdb_len = i + 1;
112     return ccb.csio.cdb_io.cdb_bytes[i];
113 }
114 
transport(const Device * device,TransportDirection dir,void * data,size_t len)115 int K3b::Device::ScsiCommand::Private::transport( const Device* device, TransportDirection dir, void* data, size_t len )
116 {
117     ccb.ccb_h.path_id    = device->handle()->path_id;
118     ccb.ccb_h.target_id  = device->handle()->target_id;
119     ccb.ccb_h.target_lun = device->handle()->target_lun;
120 
121     qDebug() << "(K3b::Device::ScsiCommand) transport command " << commandString(ccb.csio.cdb_io.cdb_bytes[0])
122              << " (" << QString::number((int)ccb.csio.cdb_io.cdb_bytes[0], 16) << "), length: " << (int)ccb.csio.cdb_len;
123     int direction = CAM_DEV_QFRZDIS;
124     if (!len)
125         direction |= CAM_DIR_NONE;
126     else
127         direction |= (dir & TR_DIR_READ) ? CAM_DIR_IN : CAM_DIR_OUT;
128 
129     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 );
130     int ret = cam_send_ccb( device->handle(), &ccb );
131     if( ret < 0 ) {
132         qCritical() << "(K3b::Device::ScsiCommand) transport cam_send_ccb failed: ret = " << ret
133                  << ", errno = " << errno << ", cam_errbuf = " << cam_errbuf;
134         return 1;
135     }
136     else if( (ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP ) {
137         qDebug() << "(K3b::Device::ScsiCommand) transport succeeded";
138         return 0;
139     }
140 
141     qDebug() << "(K3b::Device::ScsiCommand) transport command failed: scsi_status = " << QString::number(ccb.csio.scsi_status, 16);
142 
143     if( ccb.csio.scsi_status == SCSI_STATUS_CHECK_COND &&
144         !(ccb.ccb_h.status & CAM_AUTOSNS_VALID) &&
145         ccb.csio.cdb_io.cdb_bytes[0] != MMC_REQUEST_SENSE )
146     {
147         qDebug() << "(K3b::Device::ScsiCommand) transport requesting sense data";
148 
149         struct scsi_sense_data sense;
150         ScsiCommand::Private cmd;
151         cmd[0] = MMC_REQUEST_SENSE;
152         cmd[4] = SSD_MIN_SIZE;
153         cmd[5] = 0; // Necessary to set the proper command length
154 
155         memset( &sense, 0, sizeof(sense) );
156         ret = cmd.transport( device, TR_DIR_READ, &sense, SSD_MIN_SIZE );
157         if( ret < 0 )
158         {
159             qWarning() << "(K3b::Device::ScsiCommand) transport getting sense data failed: " << ret;
160             return 1;
161         }
162 
163         ccb.csio.sense_data = sense;
164         ccb.ccb_h.status |= CAM_AUTOSNS_VALID;
165     }
166 
167     if( !(ccb.ccb_h.status & CAM_AUTOSNS_VALID) )
168         qDebug() << "(K3b::Device::ScsiCommand) sense data is not available";
169 
170     ret = sense_to_err(ccb.csio.sense_data);
171     if( ret == 0 )
172         ret = 1;
173     qDebug() << "(K3b::Device::ScsiCommand) transport failed: " << ret;
174     return ret;
175 }
176