Back to index

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