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     {
00122         close (cmd->fd);
00123         cmd->fd = -1;
00124     }
00125     if (cmd->filename)
00126     {
00127         free (cmd->filename);
00128         cmd->filename = NULL;
00129     }
00130 
00131     free (cmd);
00132 }
00133 
00134 static int
00135 scsi_command_transport (ScsiCommand *cmd, Direction dir, void *buf, size_t sz)
00136 {
00137     int ret = 0;
00138 
00139     cmd->sg_io.dxferp                = buf;
00140     cmd->sg_io.dxfer_len             = sz;
00141     cmd->sg_io.dxfer_direction       = DIRECTION(dir);
00142 
00143     if (ioctl (cmd->fd, SG_IO, &cmd->sg_io))
00144         return -1;
00145 
00146     if ((cmd->sg_io.info&SG_INFO_OK_MASK) != SG_INFO_OK)
00147     {
00148         errno=EIO;
00149         ret=-1;
00150         if (cmd->sg_io.masked_status & CHECK_CONDITION)
00151         {
00152             CREAM_ON_ERRNO(cmd->sg_io.sbp);
00153             ret=ERRCODE(cmd->sg_io.sbp);
00154             if (ret==0)
00155                 ret=-1;
00156         }
00157     }
00158 
00159     return ret;
00160 }
00161 
00162 static void
00163 scsi_command_init (ScsiCommand *cmd, size_t i, int arg)
00164 {
00165     if (i == 0)
00166     {
00167         memset(&cmd->cgc,0,sizeof(cmd->cgc));
00168         memset(&cmd->_sense,0,sizeof(cmd->_sense));
00169         cmd->cgc.quiet = 1;
00170         cmd->cgc.sense = &cmd->_sense.s;
00171         memset(&cmd->sg_io,0,sizeof(cmd->sg_io));
00172         cmd->sg_io.interface_id= 'S';
00173         cmd->sg_io.mx_sb_len = sizeof(cmd->_sense);
00174         cmd->sg_io.cmdp      = cmd->cgc.cmd;
00175         cmd->sg_io.sbp       = cmd->_sense.u;
00176         cmd->sg_io.flags     = SG_FLAG_LUN_INHIBIT|SG_FLAG_DIRECT_IO;
00177     }
00178     cmd->sg_io.cmd_len = i+1;
00179     cmd->cgc.cmd[i] = arg;
00180 }
00181 
00182 int
00183 get_dvd_r_rw_profile (int fd)
00184 {
00185     ScsiCommand *cmd;
00186     int retval = -1;
00187     unsigned char page[20];
00188     unsigned char *list;
00189     int i, len;
00190 
00191     cmd = scsi_command_new_from_fd (fd);
00192 
00193     scsi_command_init (cmd, 0, 0x46);
00194     scsi_command_init (cmd, 1, 2);
00195     scsi_command_init (cmd, 8, 8);
00196     scsi_command_init (cmd, 9, 0);
00197     if (scsi_command_transport (cmd, READ, page, 8))
00198     {
00199         /* GET CONFIGURATION failed */
00200         scsi_command_free (cmd);
00201         return -1;
00202     }
00203 
00204     /* See if it's 2 gen drive by checking if DVD+R profile is an option */
00205     len = 4 + (page[0] << 24 | page[1] << 16 | page[2] << 8 | page[3]);
00206     if (len > 264)
00207     {
00208         scsi_command_free (cmd);
00209         /* insane profile list length */
00210         return -1;
00211     }
00212 
00213     list = (unsigned char *) malloc (len);
00214 
00215     scsi_command_init (cmd, 0, 0x46);
00216     scsi_command_init (cmd, 1, 2);
00217     scsi_command_init (cmd, 7, len >> 8);
00218     scsi_command_init (cmd, 8, len);
00219     scsi_command_init (cmd, 9, 0);
00220     if (scsi_command_transport (cmd, READ, list, len))
00221     {
00222         /* GET CONFIGURATION failed */
00223         scsi_command_free (cmd);
00224         free (list);
00225         return -1;
00226     }
00227 
00228     for (i = 12 ; i < list[11] ; i += 4)
00229     {
00230         int profile = (list[i] <<8 | list[i+1]);
00231         /* 0x1B: DVD+R
00232          * 0x1A: DVD+RW */
00233         if (profile == 0x1B)
00234         {
00235             if (retval == 1)
00236                 retval = 2;
00237             else
00238                 retval = 0;
00239         } else if (profile == 0x1A) {
00240             if (retval == 0)
00241                 retval = 2;
00242             else
00243                 retval = 1;
00244         }
00245     }
00246 
00247     scsi_command_free (cmd);
00248     free (list);
00249 
00250     return retval;
00251 }
00252 
00253 static unsigned char *
00254 pull_page2a_from_fd (int fd)
00255 {
00256     ScsiCommand *cmd;
00257     unsigned char header[12], *page2A;
00258     unsigned int len, bdlen;
00259 
00260     cmd = scsi_command_new_from_fd (fd);
00261 
00262     scsi_command_init (cmd, 0, 0x5A); /* MODE SENSE */
00263     scsi_command_init (cmd, 1, 0x08); /* Disable Block Descriptors */
00264     scsi_command_init (cmd, 2, 0x2A); /* Capabilities and Mechanical Status */
00265     scsi_command_init (cmd, 8, sizeof (header)); /* header only to start with */
00266     scsi_command_init (cmd, 9, 0);
00267 
00268     if (scsi_command_transport (cmd, READ, header, sizeof (header)))
00269     {
00270         /* MODE SENSE failed */
00271         scsi_command_free (cmd);
00272         return NULL;
00273     }
00274 
00275     len = (header[0] << 8 | header[1]) + 2;
00276     bdlen = header[6] << 8 | header[7];
00277 
00278     /* should never happen as we set "DBD" above */
00279     if (bdlen)
00280     {
00281         if (len < (8 + bdlen + 30))
00282         {
00283             /* LUN impossible to bear with */
00284             scsi_command_free (cmd);
00285             return NULL;
00286         }
00287     } else if (len < (8 + 2 + (unsigned int) header[9])) {
00288         /* SANYO does this. */
00289         len = 8+2+header[9];
00290     }
00291 
00292     page2A = (unsigned char *)malloc(len);
00293     if (page2A == NULL)
00294     {
00295         /* ENOMEM */
00296         scsi_command_free (cmd);
00297         return NULL;
00298     }
00299 
00300     scsi_command_init (cmd, 0, 0x5A); /* MODE SENSE */
00301     scsi_command_init (cmd, 1, 0x08); /* Disable Block Descriptors */
00302     scsi_command_init (cmd, 2, 0x2A); /* Capabilities and Mechanical Status */
00303     scsi_command_init (cmd, 7, len >> 8);
00304     scsi_command_init (cmd, 8, len); /* Real length */
00305     scsi_command_init (cmd, 9, 0);
00306     if (scsi_command_transport (cmd, READ, page2A, len))
00307     {
00308         /* MODE SENSE failed */
00309         scsi_command_free (cmd);
00310         free (page2A);
00311         return NULL;
00312     }
00313 
00314     scsi_command_free (cmd);
00315 
00316     len -= 2;
00317     /* paranoia */
00318     if (len < ((unsigned int) page2A[0] << 8 | page2A[1]))
00319     {
00320         page2A[0] = len >> 8;
00321         page2A[1] = len;
00322     }
00323 
00324     return page2A;
00325 }
00326 
00327 int
00328 get_read_write_speed (int fd, int *read_speed, int *write_speed)
00329 {
00330     unsigned char *page2A;
00331     int len, hlen;
00332     unsigned char *p;
00333 
00334     *read_speed = 0;
00335     *write_speed =0;
00336 
00337     page2A = pull_page2a_from_fd (fd);
00338     if (page2A == NULL)
00339     {
00340         printf ("Failed to get Page 2A\n");
00341         /* Failed to get Page 2A */
00342         return -1;
00343     }
00344 
00345     len  = (page2A[0] << 8 | page2A[1]) + 2;
00346     hlen = 8 + (page2A[6] << 8 | page2A[7]);
00347     p = page2A + hlen;
00348 
00349     /* Values guessed from the cd_mode_page_2A struct
00350      * in cdrecord's libscg/scg/scsireg.h */
00351     if (len < (hlen + 30) || p[1] < (30 - 2))
00352     {
00353         /* no MMC-3 "Current Write Speed" present,
00354          * try to use the MMC-2 one */
00355         if (len < (hlen + 20) || p[1] < (20 -2))
00356             *write_speed = 0;
00357         else
00358             *write_speed = p[18] << 8 | p[19];
00359     } else {
00360         *write_speed = p[28] << 8 | p[29];
00361     }
00362 
00363     *read_speed = p[8] << 8 | p[9];
00364 
00365     free (page2A);
00366 
00367     return 0;
00368 }
00369 

Generated on Sat Feb 7 22:11:47 2004 for HAL by doxygen 1.3.5