Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

linux_dvd_rw_utils.c

00001 /*
00002  * This is part of dvd+rw-tools by Andy Polyakov <appro@fy.chalmers.se>
00003  *
00004  * Use-it-on-your-own-risk, GPL bless...
00005  *
00006  * For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/
00007 */
00008 
00009 #define CREAM_ON_ERRNO(s)   do {                \
00010     switch ((s)[2]&0x0F)                    \
00011     {   case 2: if ((s)[12]==4) errno=EAGAIN;   break;      \
00012     case 5: errno=EINVAL;                   \
00013         if ((s)[13]==0)                 \
00014         {   if ((s)[12]==0x21)      errno=ENOSPC;   \
00015             else if ((s)[12]==0x20) errno=ENODEV;   \
00016         }                       \
00017         break;                      \
00018     }                               \
00019 } while(0)
00020 #define ERRCODE(s)  ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13]))
00021 #define SK(errcode) (((errcode)>>16)&0xF)
00022 #define ASC(errcode)    (((errcode)>>8)&0xFF)
00023 #define ASCQ(errcode)   ((errcode)&0xFF)
00024 
00025 #ifndef _LARGEFILE_SOURCE
00026 #define _LARGEFILE_SOURCE
00027 #endif
00028 #ifndef _LARGEFILE64_SOURCE
00029 #define _LARGEFILE64_SOURCE
00030 #endif
00031 #ifndef _FILE_OFFSET_BITS
00032 #define _FILE_OFFSET_BITS 64
00033 #endif
00034 #ifndef _GNU_SOURCE
00035 #define _GNU_SOURCE
00036 #endif
00037 
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <unistd.h>
00041 #include <sys/types.h>
00042 #include <sys/stat.h>
00043 #include <fcntl.h>
00044 #include <sys/ioctl.h>
00045 #include <linux/cdrom.h>
00046 #include <errno.h>
00047 #include <string.h>
00048 #include <mntent.h>
00049 #include <sys/wait.h>
00050 #include <sys/utsname.h>
00051 #include <scsi/scsi.h>
00052 #include <scsi/sg.h>
00053 #include <poll.h>
00054 #include <sys/time.h>
00055 
00056 #include "linux/linux_dvd_rw_utils.h"
00057 
00058 typedef enum {
00059     NONE = CGC_DATA_NONE,   // 3
00060     READ = CGC_DATA_READ,   // 2
00061     WRITE = CGC_DATA_WRITE  // 1
00062 } Direction;
00063 
00064 typedef struct ScsiCommand ScsiCommand;
00065 
00066 struct ScsiCommand {
00067     int fd;
00068     int autoclose;
00069     char *filename;
00070     struct cdrom_generic_command cgc;
00071     union {
00072         struct request_sense s;
00073         unsigned char u[18];
00074     } _sense;
00075     struct sg_io_hdr sg_io;
00076 };
00077 
00078 #define DIRECTION(i) (Dir_xlate[i]);
00079 
00080 /* 1,CGC_DATA_WRITE
00081  * 2,CGC_DATA_READ
00082  * 3,CGC_DATA_NONE
00083  */
00084 const int Dir_xlate[4] = {
00085     0,          // implementation-dependent...
00086     SG_DXFER_TO_DEV,    // 1,CGC_DATA_WRITE
00087     SG_DXFER_FROM_DEV,  // 2,CGC_DATA_READ
00088     SG_DXFER_NONE       // 3,CGC_DATA_NONE
00089 };
00090 
00091 static ScsiCommand *
00092 scsi_command_new (void)
00093 {
00094     ScsiCommand *cmd;
00095 
00096     cmd = (ScsiCommand *) malloc (sizeof (ScsiCommand));
00097     memset (cmd, 0, sizeof (ScsiCommand));
00098     cmd->fd = -1;
00099     cmd->filename = NULL;
00100     cmd->autoclose = 1;
00101 
00102     return cmd;
00103 }
00104 
00105 static ScsiCommand *
00106 scsi_command_new_from_fd (int f)
00107 {
00108     ScsiCommand *cmd;
00109 
00110     cmd = scsi_command_new ();
00111     cmd->fd = f;
00112     cmd->autoclose = 0;
00113 
00114     return cmd;
00115 }
00116 
00117 static void
00118 scsi_command_free (ScsiCommand * cmd)
00119 {
00120     if (cmd->fd >= 0 && cmd->autoclose) {
00121         close (cmd->fd);
00122         cmd->fd = -1;
00123     }
00124     if (cmd->filename) {
00125         free (cmd->filename);
00126         cmd->filename = NULL;
00127     }
00128 
00129     free (cmd);
00130 }
00131 
00132 static int
00133 scsi_command_transport (ScsiCommand * cmd, Direction dir, void *buf,
00134             size_t sz)
00135 {
00136     int ret = 0;
00137 
00138     cmd->sg_io.dxferp = buf;
00139     cmd->sg_io.dxfer_len = sz;
00140     cmd->sg_io.dxfer_direction = DIRECTION (dir);
00141 
00142     if (ioctl (cmd->fd, SG_IO, &cmd->sg_io))
00143         return -1;
00144 
00145     if ((cmd->sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
00146         errno = EIO;
00147         ret = -1;
00148         if (cmd->sg_io.masked_status & CHECK_CONDITION) {
00149             CREAM_ON_ERRNO (cmd->sg_io.sbp);
00150             ret = ERRCODE (cmd->sg_io.sbp);
00151             if (ret == 0)
00152                 ret = -1;
00153         }
00154     }
00155 
00156     return ret;
00157 }
00158 
00159 static void
00160 scsi_command_init (ScsiCommand * cmd, size_t i, int arg)
00161 {
00162     if (i == 0) {
00163         memset (&cmd->cgc, 0, sizeof (cmd->cgc));
00164         memset (&cmd->_sense, 0, sizeof (cmd->_sense));
00165         cmd->cgc.quiet = 1;
00166         cmd->cgc.sense = &cmd->_sense.s;
00167         memset (&cmd->sg_io, 0, sizeof (cmd->sg_io));
00168         cmd->sg_io.interface_id = 'S';
00169         cmd->sg_io.mx_sb_len = sizeof (cmd->_sense);
00170         cmd->sg_io.cmdp = cmd->cgc.cmd;
00171         cmd->sg_io.sbp = cmd->_sense.u;
00172         cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO;
00173     }
00174     cmd->sg_io.cmd_len = i + 1;
00175     cmd->cgc.cmd[i] = arg;
00176 }
00177 
00178 int
00179 get_dvd_r_rw_profile (int fd)
00180 {
00181     ScsiCommand *cmd;
00182     int retval = -1;
00183     unsigned char page[20];
00184     unsigned char *list;
00185     int i, len;
00186 
00187     cmd = scsi_command_new_from_fd (fd);
00188 
00189     scsi_command_init (cmd, 0, 0x46);
00190     scsi_command_init (cmd, 1, 2);
00191     scsi_command_init (cmd, 8, 8);
00192     scsi_command_init (cmd, 9, 0);
00193     if (scsi_command_transport (cmd, READ, page, 8)) {
00194         /* GET CONFIGURATION failed */
00195         scsi_command_free (cmd);
00196         return -1;
00197     }
00198 
00199     /* See if it's 2 gen drive by checking if DVD+R profile is an option */
00200     len = 4 + (page[0] << 24 | page[1] << 16 | page[2] << 8 | page[3]);
00201     if (len > 264) {
00202         scsi_command_free (cmd);
00203         /* insane profile list length */
00204         return -1;
00205     }
00206 
00207     list = (unsigned char *) malloc (len);
00208 
00209     scsi_command_init (cmd, 0, 0x46);
00210     scsi_command_init (cmd, 1, 2);
00211     scsi_command_init (cmd, 7, len >> 8);
00212     scsi_command_init (cmd, 8, len);
00213     scsi_command_init (cmd, 9, 0);
00214     if (scsi_command_transport (cmd, READ, list, len)) {
00215         /* GET CONFIGURATION failed */
00216         scsi_command_free (cmd);
00217         free (list);
00218         return -1;
00219     }
00220 
00221     for (i = 12; i < list[11]; i += 4) {
00222         int profile = (list[i] << 8 | list[i + 1]);
00223         /* 0x1B: DVD+R
00224          * 0x1A: DVD+RW */
00225         if (profile == 0x1B) {
00226             if (retval == 1)
00227                 retval = 2;
00228             else
00229                 retval = 0;
00230         } else if (profile == 0x1A) {
00231             if (retval == 0)
00232                 retval = 2;
00233             else
00234                 retval = 1;
00235         }
00236     }
00237 
00238     scsi_command_free (cmd);
00239     free (list);
00240 
00241     return retval;
00242 }
00243 
00244 static unsigned char *
00245 pull_page2a_from_fd (int fd)
00246 {
00247     ScsiCommand *cmd;
00248     unsigned char header[12], *page2A;
00249     unsigned int len, bdlen;
00250 
00251     cmd = scsi_command_new_from_fd (fd);
00252 
00253     scsi_command_init (cmd, 0, 0x5A);   /* MODE SENSE */
00254     scsi_command_init (cmd, 1, 0x08);   /* Disable Block Descriptors */
00255     scsi_command_init (cmd, 2, 0x2A);   /* Capabilities and Mechanical Status */
00256     scsi_command_init (cmd, 8, sizeof (header));    /* header only to start with */
00257     scsi_command_init (cmd, 9, 0);
00258 
00259     if (scsi_command_transport (cmd, READ, header, sizeof (header))) {
00260         /* MODE SENSE failed */
00261         scsi_command_free (cmd);
00262         return NULL;
00263     }
00264 
00265     len = (header[0] << 8 | header[1]) + 2;
00266     bdlen = header[6] << 8 | header[7];
00267 
00268     /* should never happen as we set "DBD" above */
00269     if (bdlen) {
00270         if (len < (8 + bdlen + 30)) {
00271             /* LUN impossible to bear with */
00272             scsi_command_free (cmd);
00273             return NULL;
00274         }
00275     } else if (len < (8 + 2 + (unsigned int) header[9])) {
00276         /* SANYO does this. */
00277         len = 8 + 2 + header[9];
00278     }
00279 
00280     page2A = (unsigned char *) malloc (len);
00281     if (page2A == NULL) {
00282         /* ENOMEM */
00283         scsi_command_free (cmd);
00284         return NULL;
00285     }
00286 
00287     scsi_command_init (cmd, 0, 0x5A);   /* MODE SENSE */
00288     scsi_command_init (cmd, 1, 0x08);   /* Disable Block Descriptors */
00289     scsi_command_init (cmd, 2, 0x2A);   /* Capabilities and Mechanical Status */
00290     scsi_command_init (cmd, 7, len >> 8);
00291     scsi_command_init (cmd, 8, len);    /* Real length */
00292     scsi_command_init (cmd, 9, 0);
00293     if (scsi_command_transport (cmd, READ, page2A, len)) {
00294         /* MODE SENSE failed */
00295         scsi_command_free (cmd);
00296         free (page2A);
00297         return NULL;
00298     }
00299 
00300     scsi_command_free (cmd);
00301 
00302     len -= 2;
00303     /* paranoia */
00304     if (len < ((unsigned int) page2A[0] << 8 | page2A[1])) {
00305         page2A[0] = len >> 8;
00306         page2A[1] = len;
00307     }
00308 
00309     return page2A;
00310 }
00311 
00312 int
00313 get_read_write_speed (int fd, int *read_speed, int *write_speed)
00314 {
00315     unsigned char *page2A;
00316     int len, hlen;
00317     unsigned char *p;
00318 
00319     *read_speed = 0;
00320     *write_speed = 0;
00321 
00322     page2A = pull_page2a_from_fd (fd);
00323     if (page2A == NULL) {
00324         printf ("Failed to get Page 2A\n");
00325         /* Failed to get Page 2A */
00326         return -1;
00327     }
00328 
00329     len = (page2A[0] << 8 | page2A[1]) + 2;
00330     hlen = 8 + (page2A[6] << 8 | page2A[7]);
00331     p = page2A + hlen;
00332 
00333     /* Values guessed from the cd_mode_page_2A struct
00334      * in cdrecord's libscg/scg/scsireg.h */
00335     if (len < (hlen + 30) || p[1] < (30 - 2)) {
00336         /* no MMC-3 "Current Write Speed" present,
00337          * try to use the MMC-2 one */
00338         if (len < (hlen + 20) || p[1] < (20 - 2))
00339             *write_speed = 0;
00340         else
00341             *write_speed = p[18] << 8 | p[19];
00342     } else {
00343         *write_speed = p[28] << 8 | p[29];
00344     }
00345 
00346     *read_speed = p[8] << 8 | p[9];
00347 
00348     free (page2A);
00349 
00350     return 0;
00351 }

Generated on Thu Mar 11 21:32:22 2004 for HAL by doxygen 1.3.6-20040222