1 /* cdrdao - write audio CD-Rs in disc-at-once mode
2 *
3 * Copyright (C) 2007 Denis Leroy <denis@poolshark.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <config.h>
21
22 #include <sys/types.h>
23 #include <sys/stat.h>
24
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <sys/ioctl.h>
32 #include <glob.h>
33 #include <asm/param.h>
34 #include <scsi/scsi.h>
35 #include <scsi/sg.h>
36
37 #include "ScsiIf.h"
38 #include "sg_err.h"
39 #include "log.h"
40 #include "util.h"
41
42 //
43 // SG_IO Linux SCSI interface
44 ///
45
46 #ifndef SG_GET_RESERVED_SIZE
47 #define SG_GET_RESERVED_SIZE 0x2272
48 #endif
49
50 #ifndef SG_SET_RESERVED_SIZE
51 #define SG_SET_RESERVED_SIZE 0x2275
52 #endif
53
54 #ifndef SG_GET_VERSION_NUM
55 #define SG_GET_VERSION_NUM 0x2282
56 #endif
57
58 #ifndef SG_MAX_SENSE
59 #define SG_MAX_SENSE 16
60 #endif
61
62 #define CDRDAO_DEFAULT_TIMEOUT 30000
63
64 #define SYSFS_SCSI_DEVICES "/sys/bus/scsi/devices"
65
66 typedef unsigned char uchar;
67
68 class ScsiIfImpl
69 {
70 public:
71 char* filename_; // user provided device name
72 int fd_;
73 bool readOnlyMode;
74
75 int openScsiDevAsSg(const char* devname);
76 int adjustReservedBuffer(int requestedSize);
77
78 uchar sense_buffer[SG_MAX_SENSE];
79 uchar sense_buffer_length;
80
81 uchar last_sense_buffer_length;
82 uchar last_command_status;
83
84 int timeout_ms;
85 };
86
87
ScsiIf(const char * dev)88 ScsiIf::ScsiIf(const char *dev)
89 {
90 impl_ = new ScsiIfImpl;
91 memset(impl_, 0, sizeof(ScsiIfImpl));
92
93 impl_->filename_ = strdupCC(dev);
94 impl_->fd_ = -1;
95 impl_->sense_buffer_length = SG_MAX_SENSE;
96 impl_->timeout_ms = CDRDAO_DEFAULT_TIMEOUT;
97 }
98
~ScsiIf()99 ScsiIf::~ScsiIf()
100 {
101 if (impl_->fd_ >= 0)
102 close(impl_->fd_);
103
104 delete[] impl_->filename_;
105 delete impl_;
106 }
107
108 // Opens and flushes scsi device.
109
init()110 int ScsiIf::init()
111 {
112 int flags;
113 int sg_version = 0;
114
115 impl_->fd_ = open(impl_->filename_, O_RDWR | O_NONBLOCK | O_EXCL);
116
117 if (impl_->fd_ < 0) {
118
119 if (errno == EACCES || errno == EBUSY) {
120 impl_->fd_ = open(impl_->filename_, O_RDONLY | O_NONBLOCK);
121
122 if (impl_->fd_ < 0) {
123 goto failed;
124 }
125 impl_->readOnlyMode = true;
126 if (errno == EACCES)
127 log_message(-1, "No permission to write to SCSI device. "
128 "Only read commands are supported.");
129 if (errno == EBUSY)
130 log_message(-1, "SCSI device is currently in use. "
131 "Only read commands are supported.");
132 } else {
133 goto failed;
134 }
135 }
136
137 if (ioctl(impl_->fd_, SG_GET_VERSION_NUM, &sg_version) == 0) {
138 log_message(3, "Detected SG driver version: %d.%d.%d",
139 sg_version / 10000,
140 (sg_version / 100) % 100, sg_version % 100);
141 if (sg_version < 30000) {
142 log_message(-2, "SG interface under 3.0 not supported.");
143 return 1;
144 }
145 }
146
147 maxDataLen_ = impl_->adjustReservedBuffer(64 * 1024);
148
149 if (inquiry() != 0) {
150 return 2;
151 }
152
153 return 0;
154
155 failed:
156 log_message(-2, "Unable to open SCSI device %s: %s.",
157 impl_->filename_, strerror(errno));
158 return 1;
159 }
160
161 // Sets given timeout value in seconds and returns old timeout. Return
162 // the previous timeout value.
163
timeout(int t)164 int ScsiIf::timeout(int t)
165 {
166 int old = impl_->timeout_ms / 1000;
167 impl_->timeout_ms = t * 1000;
168
169 return old;
170 }
171
172 // Sens a scsi command and send/receive data.
173
sendCmd(const uchar * cmd,int cmdLen,const uchar * dataOut,int dataOutLen,uchar * dataIn,int dataInLen,int showMsg)174 int ScsiIf::sendCmd(const uchar *cmd, int cmdLen, const uchar *dataOut,
175 int dataOutLen, uchar *dataIn, int dataInLen, int showMsg)
176 {
177 int status;
178
179 sg_io_hdr_t io_hdr;
180 memset(&io_hdr, 0, sizeof(io_hdr));
181
182 // Check SCSI cdb length.
183 assert(cmdLen >= 0 && cmdLen <= 16);
184 // Can't both input and output data.
185 assert(!(dataOut && dataIn));
186
187 io_hdr.interface_id = 'S';
188 io_hdr.cmd_len = cmdLen;
189 io_hdr.cmdp = (unsigned char*)cmd;
190 io_hdr.timeout = impl_->timeout_ms;
191 io_hdr.sbp = impl_->sense_buffer;
192 io_hdr.mx_sb_len = impl_->sense_buffer_length;
193 io_hdr.flags = 1;
194
195 if (dataOut) {
196 io_hdr.dxferp = (void*)dataOut;
197 io_hdr.dxfer_len = dataOutLen;
198 io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
199 } else if (dataIn) {
200 io_hdr.dxferp = dataIn;
201 io_hdr.dxfer_len = dataInLen;
202 io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
203 }
204
205 log_message(4, "%s: Initiating SCSI command %s%s",
206 impl_->filename_, sg_strcommand(cmd[0]),
207 sg_strcmdopts(cmd));
208
209 if (ioctl(impl_->fd_, SG_IO, &io_hdr) < 0) {
210 int errnosave = errno;
211 log_message((showMsg ? -2 : 3), "%s: SCSI command %s (0x%02x) "
212 "failed: %s.", impl_->filename_,
213 sg_strcommand(cmd[0]), cmd[0],
214 strerror(errnosave));
215 return 1;
216 }
217
218 log_message(4, "%s: SCSI command %s (0x%02x) executed in %u ms, status=%d",
219 impl_->filename_, sg_strcommand(cmd[0]),
220 cmd[0], io_hdr.duration, io_hdr.status);
221
222 impl_->last_sense_buffer_length = io_hdr.sb_len_wr;
223 impl_->last_command_status = io_hdr.status;
224
225 if (io_hdr.status) {
226 if (io_hdr.sb_len_wr > 0)
227 return 2;
228 else
229 return 1;
230 }
231
232 return 0;
233 }
234
getSense(int & len) const235 const uchar *ScsiIf::getSense(int &len) const
236 {
237 len = impl_->last_sense_buffer_length;
238 return impl_->sense_buffer;
239 }
240
printError()241 void ScsiIf::printError()
242 {
243 sg_print_sense("\nSCSI command failed", impl_->sense_buffer);
244 }
245
246
inquiry()247 int ScsiIf::inquiry()
248 {
249 unsigned char cmd[6] = { INQUIRY, 0, 0, 0, 0x2c, 0 };
250 unsigned char result[0x2c];
251 int i;
252
253 memset(result, 0, sizeof(result));
254
255 if (sendCmd(cmd, 6, NULL, 0, result, 0x2c, 1) != 0) {
256 log_message(-2, "Inquiry command failed on \"%s\"", impl_->filename_);
257 return 1;
258 }
259
260 strncpy(vendor_, (char *)(result + 0x08), 8);
261 vendor_[8] = 0;
262
263 strncpy(product_, (char *)(result + 0x10), 16);
264 product_[16] = 0;
265
266 strncpy(revision_, (char *)(result + 0x20), 4);
267 revision_[4] = 0;
268
269 // Remove all trailing spaces.
270 for (i = 7; i >= 0 && vendor_[i] == ' '; i--) {
271 vendor_[i] = 0;
272 }
273 for (i = 15; i >= 0 && product_[i] == ' '; i--) {
274 product_[i] = 0;
275 }
276 for (i = 3; i >= 0 && revision_[i] == ' '; i--) {
277 revision_[i] = 0;
278 }
279
280 return 0;
281 }
282
283 // Scan implementation uses sysfs to
284
scan(int * len,char * scsi_dev_path)285 ScsiIf::ScanData *ScsiIf::scan(int *len, char* scsi_dev_path)
286 {
287 struct stat st;
288 int matches = 0;
289 unsigned i;
290 ScanData* sdata = NULL;
291 char* path = NULL;
292 glob_t pglob;
293
294 if (stat(SYSFS_SCSI_DEVICES, &st) != 0) {
295 log_message(-2, "Unable to access sysfs filesystem at %s",
296 SYSFS_SCSI_DEVICES);
297 goto fail;
298 }
299
300 path = (char*)alloca(strlen(SYSFS_SCSI_DEVICES) + 64);
301 sprintf(path, "%s/*", SYSFS_SCSI_DEVICES);
302 if (glob(path, 0, NULL, &pglob) != 0) {
303 log_message(-2, "Unable to glob through sysfs filesystem (%d).",
304 errno);
305 goto fail;
306 }
307
308 sdata = new ScanData[pglob.gl_pathc];
309
310 for (i = 0; i < pglob.gl_pathc; i++) {
311 int type;
312 char rbuf[16];
313 FILE* f;
314
315 sprintf(path, "%s/type", pglob.gl_pathv[i]);
316 f = fopen(path, "r");
317 if (!f)
318 continue;
319 int ret = fscanf(f, "%d", &type);
320 fclose(f);
321
322 if (ret != 1 || type != TYPE_ROM)
323 continue;
324
325 // Now we have a CD-ROM device.
326 memset(&sdata[matches].vendor, 0, sizeof(sdata[matches].vendor));
327 memset(&sdata[matches].product, 0, sizeof(sdata[matches].product));
328 memset(&sdata[matches].revision, 0, sizeof(sdata[matches].revision));
329
330 // Copy vendor data
331 sprintf(path, "%s/vendor", pglob.gl_pathv[i]);
332 f = fopen(path, "r");
333 if (!f)
334 continue;
335 if (fread(sdata[matches].vendor, 8, 1, f) != 1) {
336 fclose(f);
337 continue;
338 }
339 fclose(f);
340
341 // Copy product data
342 sprintf(path, "%s/model", pglob.gl_pathv[i]);
343 f = fopen(path, "r");
344 if (!f)
345 continue;
346 if (fread(sdata[matches].product, 16, 1, f) != 1) {
347 fclose(f);
348 continue;
349 }
350 fclose(f);
351
352 // Copy revision data
353 sprintf(path, "%s/rev", pglob.gl_pathv[i]);
354 f = fopen(path, "r");
355 if (!f)
356 continue;
357 if (fread(sdata[matches].revision, 4, 1, f) != 1) {
358 fclose(f);
359 continue;
360 }
361 fclose(f);
362
363 // figure out the block device
364 glob_t bglob;
365 char* devname = NULL;
366 sprintf(path, "%s/block:*", pglob.gl_pathv[i]);
367 if (glob(path, 0, NULL, &bglob) == 0) {
368
369 if (bglob.gl_pathc != 1) {
370 globfree(&bglob);
371 continue;
372 }
373
374 char* match = strrchr(bglob.gl_pathv[0], ':');
375 if (!match) {
376 globfree(&bglob);
377 continue;
378 }
379 devname = (char*)alloca(strlen(match));
380 strcpy(devname, match+1);
381 } else {
382 sprintf(path, "%s/block/*", pglob.gl_pathv[i]);
383 if (glob(path, 0, NULL, &bglob) == 0) {
384
385 if (bglob.gl_pathc != 1) {
386 globfree(&bglob);
387 continue;
388 }
389 char* match = strrchr(bglob.gl_pathv[0], '/');
390 devname = (char*)alloca(strlen(match));
391 strcpy(devname, match + 1);
392 }
393 }
394
395 if (devname) {
396 sdata[matches].dev = "/dev/";
397 sdata[matches].dev += devname;
398 globfree(&bglob);
399 } else {
400 continue;
401 }
402
403 matches++;
404
405 }
406 globfree(&pglob);
407
408 if (matches) {
409 *len = matches;
410 return sdata;
411 }
412
413 delete[] sdata;
414 fail:
415 *len = 0;
416 return NULL;
417 }
418
419 #include "ScsiIf-common.cc"
420
adjustReservedBuffer(int requestedSize)421 int ScsiIfImpl::adjustReservedBuffer(int requestedSize)
422 {
423 int maxTransferLength;
424
425 if (ioctl(fd_, SG_SET_RESERVED_SIZE, &requestedSize) < 0) {
426 log_message(-2, "SG_SET_RESERVED_SIZE ioctl failed: %s",
427 strerror(errno));
428 return 0;
429 }
430 if (ioctl(fd_, SG_GET_RESERVED_SIZE, &maxTransferLength) < 0) {
431 log_message(-2, "SG_GET_RESERVED_SIZE ioctl failed: %s",
432 strerror(errno));
433 return 0;
434 }
435
436 log_message(4, "SG: Maximum transfer length: %ld", maxTransferLength);
437
438 return maxTransferLength;
439 }
440