1 /* OGMDvd - A wrapper library around libdvdread
2 * Copyright (C) 2009-2012 Olivier Rolland <billl@users.sourceforge.net>
3 *
4 * Code from dvd+rw-tools
5 * Copyright (C) Andy Polyakov <appro@fy.chalmers.se>
6 *
7 * dvd+rw-tools is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * dvd+rw-tools is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "ogmdvd-transport.h"
27
28 #include <poll.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <sys/stat.h>
38
39 #ifndef CHECK_CONDITION
40 #define CHECK_CONDITION 0x01
41 #endif
42
43 #ifndef EMEDIUMTYPE
44 #define EMEDIUMTYPE EINVAL
45 #endif
46
47 #ifndef ENOMEDIUM
48 #define ENOMEDIUM ENODEV
49 #endif
50
51 #define ERRCODE(s) ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13]))
52 #define SK(errcode) (((errcode)>>16)&0xF)
53 #define ASC(errcode) (((errcode)>>8)&0xFF)
54 #define ASCQ(errcode) ((errcode)&0xFF)
55
56 #define CREAM_ON_ERRNO_NAKED(s) \
57 switch ((s)[12]) \
58 { case 0x04: errno=EAGAIN; break; \
59 case 0x20: errno=ENODEV; break; \
60 case 0x21: if ((s)[13]==0) errno=ENOSPC; \
61 else errno=EINVAL; \
62 break; \
63 case 0x30: errno=EMEDIUMTYPE; break; \
64 case 0x3A: errno=ENOMEDIUM; break; \
65 }
66 #define CREAM_ON_ERRNO(s) do { CREAM_ON_ERRNO_NAKED(s) } while(0)
67
68 inline long
getmsecs()69 getmsecs ()
70 {
71 struct timeval tv;
72 gettimeofday (&tv, NULL);
73 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
74 }
75
76 void
sperror(const char * cmd,int err)77 sperror (const char *cmd, int err)
78 {
79 int saved_errno = errno;
80
81 if (err == -1)
82 fprintf (stderr, ":-( unable to %s: ", cmd);
83 else
84 fprintf (stderr, ":-[ %s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh]: ",
85 cmd, SK (err), ASC (err), ASCQ (err));
86 errno = saved_errno, perror (NULL);
87 }
88
89 #if defined(HAVE_SG_IO_HDR_T)
90
91 #include <limits.h>
92 #include <mntent.h>
93
94 #include <linux/cdrom.h>
95
96 #include <sys/wait.h>
97 #include <sys/ioctl.h>
98 #include <sys/utsname.h>
99
100 #include <scsi/sg.h>
101
102 static const int Dir_xlate[] =
103 {
104 SG_DXFER_NONE,
105 SG_DXFER_FROM_DEV,
106 SG_DXFER_TO_DEV
107 };
108
109 struct _Scsi_Command
110 {
111 int fd, autoclose;
112 char *filename;
113 struct cdrom_generic_command cgc;
114 union
115 {
116 struct request_sense s;
117 unsigned char u[18];
118 } _sense;
119 struct sg_io_hdr sg_io;
120 };
121
122 Scsi_Command *
scsi_command_new_from_fd(int fd)123 scsi_command_new_from_fd (int fd)
124 {
125 Scsi_Command *cmd;
126
127 cmd = (Scsi_Command *) malloc (sizeof (Scsi_Command));
128 cmd->fd = fd;
129 cmd->autoclose = 0;
130 cmd->filename = NULL;
131
132 return cmd;
133 }
134
135 void
scsi_command_free(Scsi_Command * cmd)136 scsi_command_free (Scsi_Command *cmd)
137 {
138 if (cmd->fd >= 0 && cmd->autoclose)
139 {
140 close (cmd->fd);
141 cmd->fd = -1;
142 }
143
144 if (cmd->filename)
145 {
146 free (cmd->filename);
147 cmd->filename = NULL;
148 }
149
150 free (cmd);
151 }
152
153 void
scsi_command_set(Scsi_Command * cmd,size_t index,unsigned char value)154 scsi_command_set (Scsi_Command *cmd, size_t index, unsigned char value)
155 {
156 if (index == 0)
157 {
158 memset (&cmd->cgc, 0, sizeof (cmd->cgc)), memset (&cmd->_sense, 0, sizeof (cmd->_sense));
159 cmd->cgc.quiet = 1;
160 cmd->cgc.sense = &cmd->_sense.s;
161 memset (&cmd->sg_io, 0, sizeof (cmd->sg_io));
162 cmd->sg_io.interface_id = 'S';
163 cmd->sg_io.mx_sb_len = sizeof (cmd->_sense);
164 cmd->sg_io.cmdp = cmd->cgc.cmd;
165 cmd->sg_io.sbp = cmd->_sense.u;
166 cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO;
167 }
168 cmd->sg_io.cmd_len = index + 1;
169 cmd->cgc.cmd[index] = value;
170 }
171
172 static const int real_dir[] =
173 {
174 CGC_DATA_NONE,
175 CGC_DATA_READ,
176 CGC_DATA_WRITE
177 };
178
179 int
scsi_command_transport(Scsi_Command * cmd,Direction dir,void * buf,size_t sz)180 scsi_command_transport (Scsi_Command *cmd, Direction dir, void *buf, size_t sz)
181 {
182 int ret = 0;
183
184 cmd->sg_io.dxferp = buf;
185 cmd->sg_io.dxfer_len = sz;
186 cmd->sg_io.dxfer_direction = Dir_xlate[dir];
187 if (ioctl (cmd->fd, SG_IO, &cmd->sg_io))
188 return -1;
189
190 if ((cmd->sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK)
191 {
192 errno = EIO;
193 ret = -1;
194 if (cmd->sg_io.masked_status & CHECK_CONDITION)
195 {
196 ret = ERRCODE ((unsigned char *) cmd->sg_io.sbp);
197 if (ret == 0)
198 ret = -1;
199 else
200 CREAM_ON_ERRNO ((unsigned char *) cmd->sg_io.sbp);
201 }
202 }
203
204 return ret;
205 }
206
207 #elif defined(HAVE_SCSIREQ_T)
208
209 #include <sys/ioctl.h>
210 #include <sys/scsiio.h>
211 #include <sys/wait.h>
212 #include <sys/param.h>
213 #include <sys/mount.h>
214
215 typedef off_t off64_t;
216 #define stat64 stat
217 #define fstat64 fstat
218 #define open64 open
219 #define pread64 pread
220 #define pwrite64 pwrite
221 #define lseek64 lseek
222
223 typedef enum
224 {
225 NONE = 0,
226 READ = SCCMD_READ,
227 WRITE = SCCMD_WRITE
228 } Direction;
229
230 struct _Scsi_Command
231 {
232 int fd;
233 int autoclose;
234 char *filename;
235 scsireq_t req;
236 };
237
238 Scsi_Command *
scsi_command_new_from_fd(int f)239 scsi_command_new_from_fd (int f)
240 {
241 Scsi_Command *cmd;
242
243 cmd = (Scsi_Command *) malloc (sizeof (Scsi_Command));
244 cmd->fd = f;
245 cmd->autoclose = 0;
246 cmd->filename = NULL;
247
248 return cmd;
249 }
250
251 void
scsi_command_free(Scsi_Command * cmd)252 scsi_command_free (Scsi_Command *cmd)
253 {
254 if (cmd->fd >= 0 && cmd->autoclose)
255 {
256 close (cmd->fd);
257 cmd->fd = -1;
258 }
259
260 if (cmd->filename)
261 {
262 free (cmd->filename);
263 cmd->filename = NULL;
264 }
265
266 free (cmd);
267 }
268
269 void
scsi_command_set(Scsi_Command * cmd,size_t index,unsigned char value)270 scsi_command_set (Scsi_Command *cmd, size_t index, unsigned char value)
271 {
272 if (index == 0)
273 {
274 memset (&req, 0, sizeof (req));
275 req.flags = SCCMD_ESCAPE;
276 req.timeout = 30000;
277 req.senselen = 18; //sizeof(req.sense);
278 }
279 req.cmdlen = index + 1;
280 req.cmd[index] = value;
281 }
282
283 int
scsi_command_transport(Scsi_Command * cmd,Direction dir,void * buf,size_t sz)284 scsi_command_transport (Scsi_Command *cmd, Direction dir, void *buf, size_t sz)
285 {
286 int ret = 0;
287
288 req.databuf = (caddr_t) buf;
289 req.datalen = sz;
290 req.flags |= dir;
291
292 if (ioctl (fd, SCIOCCOMMAND, &req) < 0)
293 return -1;
294 if (req.retsts == SCCMD_OK)
295 return 0;
296
297 errno = EIO;
298 ret = -1;
299 if (req.retsts == SCCMD_SENSE)
300 {
301 ret = ERRCODE (req.sense);
302 if (ret == 0)
303 ret = -1;
304 else
305 CREAM_ON_ERRNO (req.sense);
306 }
307
308 return ret;
309 }
310
311 #elif defined(HAVE_STRUCT_USCSI_CMD)
312
313 /* Licensing terms for commercial distribution for Solaris are to be
314 settled with Inserve Technology, Åvägen 6, 412 50 GÖTEBORG, Sweden,
315 tel. +46-(0)31-86 87 88, see http://www.inserve.se/ for further
316 details. */
317
318 #include <sys/scsi/impl/uscsi.h>
319
320 struct _Scsi_Command
321 {
322 int fd;
323 int autoclose;
324 char *filename;
325 struct uscsi_cmd ucmd;
326 unsigned char cdb[16];
327 unsigned char _sense[18];
328 };
329
330 Scsi_Command *
scsi_command_new_from_fd(int f)331 scsi_command_new_from_fd (int f)
332 {
333 Scsi_Command *cmd;
334
335 cmd = (Scsi_Command *) malloc (sizeof (Scsi_Command));
336 cmd->fd = f;
337 cmd->autoclose = 0;
338 cmd->filename = NULL;
339
340 return cmd;
341 }
342
343 void
scsi_command_free(Scsi_Command * cmd)344 scsi_command_free (Scsi_Command *cmd)
345 {
346 if (cmd->fd >= 0 && cmd->autoclose)
347 {
348 close (cmd->fd);
349 cmd->fd = -1;
350 }
351
352 if (cmd->filename)
353 {
354 free (cmd->filename);
355 cmd->filename = NULL;
356 }
357
358 free (cmd);
359 }
360
361 void
scsi_command_set(Scsi_Command * cmd,size_t index,unsigned char value)362 scsi_command_set (Scsi_Command *cmd, size_t index, unsigned char value)
363 {
364 if (index == 0)
365 {
366 memset (&cmd->ucmd, 0, sizeof (cmd->ucmd));
367 memset (cmd->cdb, 0, sizeof (cmd->cdb));
368 memset (cmd->_sense, 0, sizeof (cmd->_sense));
369 cmd->ucmd.uscsi_cdb = (caddr_t) cmd->cdb;
370 cmd->ucmd.uscsi_rqbuf = (caddr_t) cmd->_sense;
371 cmd->ucmd.uscsi_rqlen = sizeof (cmd->_sense);
372 cmd->ucmd.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE |
373 USCSI_ISOLATE | USCSI_RQENABLE;
374 cmd->ucmd.uscsi_timeout = 60;
375 }
376 cmd->ucmd.uscsi_cdblen = index + 1;
377 cmd->cdb[index] = value;
378 }
379
380 static const int real_dir[] =
381 {
382 0,
383 USCSI_READ,
384 USCSI_WRITE
385 };
386
387 int
scsi_command_transport(Scsi_Command * cmd,Direction dir,void * buf,size_t sz)388 scsi_command_transport (Scsi_Command *cmd, Direction dir, void *buf, size_t sz)
389 {
390 int ret = 0;
391
392 cmd->ucmd.uscsi_bufaddr = (caddr_t) buf;
393 cmd->ucmd.uscsi_buflen = sz;
394 cmd->ucmd.uscsi_flags |= dir;
395 if (ioctl (cmd->fd, USCSICMD, &cmd->ucmd))
396 {
397 if (errno == EIO && cmd->_sense[0] == 0) /* USB seems to be broken... */
398 {
399 size_t residue = cmd->ucmd.uscsi_resid;
400 memset (cmd->cdb, 0, sizeof (cmd->cdb));
401 cmd->cdb[0] = 0x03; /* REQUEST SENSE */
402 cmd->cdb[4] = sizeof (cmd->_sense);
403 cmd->ucmd.uscsi_cdblen = 6;
404 cmd->ucmd.uscsi_bufaddr = (caddr_t) cmd->_sense;
405 cmd->ucmd.uscsi_buflen = sizeof (cmd->_sense);
406 cmd->ucmd.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE |
407 USCSI_ISOLATE | USCSI_READ;
408 cmd->ucmd.uscsi_timeout = 1;
409 ret = ioctl (cmd->fd, USCSICMD, &cmd->ucmd);
410 cmd->ucmd.uscsi_resid = residue;
411 if (ret)
412 return -1;
413 }
414 ret = ERRCODE (cmd->_sense);
415 if (ret == 0)
416 ret = -1;
417 /*else CREAM_ON_ERRNO(_sense); */
418 }
419 return ret;
420 }
421
422 #elif defined(HAVE_STRUCT_CAM_DEVICE)
423
424 #include <camlib.h>
425 #include <dirent.h>
426
427 #include <sys/wait.h>
428 #include <sys/ioctl.h>
429 #include <sys/mount.h>
430 #include <sys/param.h>
431
432 #include <bus/cam/scsi/scsi_message.h>
433 #include <bus/cam/scsi/scsi_pass.h>
434
435 typedef off_t off64_t;
436 #define stat64 stat
437 #define fstat64 fstat
438 #define open64 open
439 #define pread64 pread
440 #define pwrite64 pwrite
441 #define lseek64 lseek
442
443 #define ioctl_fd (((struct cam_device *)ioctl_handle)->fd)
444
445 struct _Scsi_Command
446 {
447 int fd;
448 int autoclose;
449 char *filename;
450 struct cam_device *cam;
451 union ccb ccb;
452 };
453
454 Scsi_Command *
scsi_command_new_from_fd(int f)455 scsi_command_new_from_fd (int f)
456 {
457 Scsi_Command *cmd;
458
459 char pass[32]; /* periph_name is 16 chars long */
460
461 cmd = (Scsi_Command *) malloc (sizeof (Scsi_Command));
462 cmd->fd = -1;
463 cmd->autoclose = 1;
464 cmd->filename = NULL;
465
466 memset (&cmd->ccb, 0, sizeof (cmd->ccb));
467 cmd->ccb.ccb_h.func_code = XPT_GDEVLIST;
468 if (ioctl (f, CAMGETPASSTHRU, &cmd->ccb) < 0)
469 {
470 free (cmd);
471 return NULL;
472 }
473
474 sprintf (pass, "/dev/%.15s%u", cmd->ccb.cgdl.periph_name,
475 cmd->ccb.cgdl.unit_number);
476 cmd->cam = cam_open_pass (pass, O_RDWR, NULL);
477
478 return cmd;
479 }
480
481 void
scsi_command_free(Scsi_Command * cmd)482 scsi_command_free (Scsi_Command *cmd)
483 {
484 if (cmd->cam && cmd->autoclose)
485 {
486 cam_close_device (cmd->cam);
487 cmd->cam = NULL;
488 }
489
490 if (cmd->fd >= 0)
491 close (cmd->fd);
492
493 if (cmd->filename)
494 {
495 free (cmd->filename);
496 cmd->filename = NULL;
497 }
498
499 free (cmd);
500 }
501
502 void
scsi_command_set(Scsi_Command * cmd,size_t index,unsigned char value)503 scsi_command_set (Scsi_Command *cmd, size_t index, unsigned char value)
504 {
505 if (index == 0)
506 {
507 memset (&cmd->ccb, 0, sizeof (cmd->ccb));
508 cmd->ccb.ccb_h.path_id = cmd->cam->path_id;
509 cmd->ccb.ccb_h.target_id = cmd->cam->target_id;
510 cmd->ccb.ccb_h.target_lun = cmd->cam->target_lun;
511 cam_fill_csio (&(cmd->ccb.csio), 1, /* retries */
512 NULL, /* cbfncp */
513 CAM_DEV_QFRZDIS, /* flags */
514 MSG_SIMPLE_Q_TAG, /* tag_action */
515 NULL, /* data_ptr */
516 0, /* dxfer_len */
517 sizeof (cmd->ccb.csio.sense_data), /* sense_len */
518 0, /* cdb_len */
519 30 * 1000); /* timeout */
520 }
521 cmd->ccb.csio.cdb_len = index + 1;
522 cmd->ccb.csio.cdb_io.cdb_bytes[index] = value;
523 }
524
525 static const int real_dir[] =
526 {
527 CAM_DIR_NONE,
528 CAM_DIR_IN,
529 CAM_DIR_OUT
530 };
531
532 int
scsi_command_transport(Scsi_Command * cmd,Direction dir,void * buf,size_t sz)533 scsi_command_transport (Scsi_Command *cmd, Direction dir, void *buf, size_t sz)
534 {
535 int ret = 0;
536
537 cmd->ccb.csio.ccb_h.flags |= real_dir[dir];
538 cmd->ccb.csio.data_ptr = (u_int8_t *) buf;
539 cmd->ccb.csio.dxfer_len = sz;
540
541 if ((ret = cam_send_ccb (cmd->cam, &cmd->ccb)) < 0)
542 return -1;
543
544 if ((cmd->ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
545 return 0;
546
547 unsigned char *sense = (unsigned char *) &cmd->ccb.csio.sense_data;
548
549 errno = EIO;
550 /* FreeBSD 5-CURRENT since 2003-08-24, including 5.2 fails to
551 pull sense data automatically, at least for ATAPI transport,
552 so I reach for it myself... */
553 if ((cmd->ccb.csio.scsi_status == SCSI_STATUS_CHECK_COND) &&
554 !(cmd->ccb.ccb_h.status & CAM_AUTOSNS_VALID))
555 {
556 u_int8_t _sense[18];
557 u_int32_t resid = cmd->ccb.csio.resid;
558
559 memset (_sense, 0, sizeof (_sense));
560
561 scsi_command_set (cmd, 0, 0x03); /* REQUEST SENSE */
562 cmd->ccb.csio.cdb_io.cdb_bytes[4] = sizeof (_sense);
563 cmd->ccb.csio.cdb_len = 6;
564 cmd->ccb.csio.ccb_h.flags |= CAM_DIR_IN | CAM_DIS_AUTOSENSE;
565 cmd->ccb.csio.data_ptr = _sense;
566 cmd->ccb.csio.dxfer_len = sizeof (_sense);
567 cmd->ccb.csio.sense_len = 0;
568 ret = cam_send_ccb (cmd->cam, &cmd->ccb);
569
570 cmd->ccb.csio.resid = resid;
571 if (ret < 0)
572 return -1;
573 if ((cmd->ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
574 return errno = EIO, -1;
575
576 memcpy (sense, _sense, sizeof (_sense));
577 }
578
579 ret = ERRCODE (sense);
580 if (ret == 0)
581 ret = -1;
582 else
583 CREAM_ON_ERRNO (sense);
584
585 return ret;
586 }
587
588 #endif
589
590