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