2005-08-12 00:59:21 +04:00
/*
2008-04-10 01:17:39 +04:00
* cdrom_id - optical drive and media information prober
2005-08-12 00:59:21 +04:00
*
2010-04-14 19:00:57 +04:00
* Copyright ( C ) 2008 - 2010 Kay Sievers < kay . sievers @ vrfy . org >
2005-08-12 00:59:21 +04:00
*
2008-09-10 04:40:42 +04:00
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
2005-08-12 00:59:21 +04:00
*
2008-09-10 04:40:42 +04:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2008-04-10 01:17:39 +04:00
*
2008-09-10 04:40:42 +04:00
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-08-12 00:59:21 +04:00
*/
# ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
# endif
# include <stdio.h>
2008-04-10 01:17:39 +04:00
# include <stddef.h>
2005-08-12 00:59:21 +04:00
# include <stdlib.h>
# include <unistd.h>
2006-01-09 23:18:00 +03:00
# include <string.h>
2008-04-10 01:17:39 +04:00
# include <limits.h>
# include <fcntl.h>
2005-08-12 00:59:21 +04:00
# include <errno.h>
2008-04-10 01:17:39 +04:00
# include <getopt.h>
2010-04-07 11:24:25 +04:00
# include <time.h>
2008-04-10 01:17:39 +04:00
# include <scsi/sg.h>
2005-08-12 00:59:21 +04:00
# include <sys/types.h>
# include <sys/stat.h>
2008-04-10 01:17:39 +04:00
# include <sys/time.h>
# include <sys/ioctl.h>
# include <linux/cdrom.h>
2008-07-30 03:45:23 +04:00
2009-06-10 00:47:48 +04:00
# include "libudev.h"
# include "libudev-private.h"
2005-08-12 00:59:21 +04:00
2008-04-10 01:17:39 +04:00
static int debug ;
2005-08-12 22:37:56 +04:00
2008-09-06 17:45:31 +04:00
static void log_fn ( struct udev * udev , int priority ,
const char * file , int line , const char * fn ,
const char * format , va_list args )
2005-08-12 00:59:21 +04:00
{
2008-04-10 01:17:39 +04:00
if ( debug ) {
2008-09-06 17:45:31 +04:00
fprintf ( stderr , " %s: " , fn ) ;
2008-04-10 01:17:39 +04:00
vfprintf ( stderr , format , args ) ;
2008-09-06 17:45:31 +04:00
} else {
2008-04-10 01:17:39 +04:00
vsyslog ( priority , format , args ) ;
2008-09-06 17:45:31 +04:00
}
2005-08-12 00:59:21 +04:00
}
2008-04-10 01:17:39 +04:00
/* device info */
2010-04-13 12:49:24 +04:00
static unsigned int cd_cd_rom = 0 ;
static unsigned int cd_cd_r = 0 ;
static unsigned int cd_cd_rw = 0 ;
static unsigned int cd_dvd_rom = 0 ;
static unsigned int cd_dvd_r = 0 ;
static unsigned int cd_dvd_rw = 0 ;
static unsigned int cd_dvd_ram = 0 ;
static unsigned int cd_dvd_plus_r = 0 ;
static unsigned int cd_dvd_plus_rw = 0 ;
static unsigned int cd_dvd_plus_r_dl = 0 ;
static unsigned int cd_dvd_plus_rw_dl = 0 ;
static unsigned int cd_bd = 0 ;
static unsigned int cd_bd_r = 0 ;
static unsigned int cd_bd_re = 0 ;
static unsigned int cd_hddvd = 0 ;
static unsigned int cd_hddvd_r = 0 ;
static unsigned int cd_hddvd_rw = 0 ;
static unsigned int cd_mo = 0 ;
static unsigned int cd_mrw = 0 ;
static unsigned int cd_mrw_w = 0 ;
2008-04-10 01:17:39 +04:00
/* media info */
2010-04-13 12:49:24 +04:00
static unsigned int cd_media = 0 ;
static unsigned int cd_media_cd_rom = 0 ;
static unsigned int cd_media_cd_r = 0 ;
static unsigned int cd_media_cd_rw = 0 ;
static unsigned int cd_media_dvd_rom = 0 ;
static unsigned int cd_media_dvd_r = 0 ;
static unsigned int cd_media_dvd_rw = 0 ;
static unsigned int cd_media_dvd_ram = 0 ;
static unsigned int cd_media_dvd_plus_r = 0 ;
static unsigned int cd_media_dvd_plus_rw = 0 ;
static unsigned int cd_media_dvd_plus_r_dl = 0 ;
static unsigned int cd_media_dvd_plus_rw_dl = 0 ;
static unsigned int cd_media_bd = 0 ;
static unsigned int cd_media_bd_r = 0 ;
static unsigned int cd_media_bd_re = 0 ;
static unsigned int cd_media_hddvd = 0 ;
static unsigned int cd_media_hddvd_r = 0 ;
static unsigned int cd_media_hddvd_rw = 0 ;
static unsigned int cd_media_mo = 0 ;
static unsigned int cd_media_mrw = 0 ;
static unsigned int cd_media_mrw_w = 0 ;
2008-04-10 01:17:39 +04:00
2010-04-13 12:49:24 +04:00
static const char * cd_media_state = NULL ;
static unsigned int cd_media_session_next = 0 ;
static unsigned int cd_media_session_count = 0 ;
static unsigned int cd_media_track_count = 0 ;
static unsigned int cd_media_track_count_data = 0 ;
static unsigned int cd_media_track_count_audio = 0 ;
static unsigned long long int cd_media_session_last_offset = 0 ;
2008-04-10 01:17:39 +04:00
# define ERRCODE(s) ((((s)[2] & 0x0F) << 16) | ((s)[12] << 8) | ((s)[13]))
# define SK(errcode) (((errcode) >> 16) & 0xF)
# define ASC(errcode) (((errcode) >> 8) & 0xFF)
# define ASCQ(errcode) ((errcode) & 0xFF)
2010-03-18 13:14:32 +03:00
static int is_mounted ( const char * device )
{
struct stat statbuf ;
FILE * fp ;
int maj , min ;
int mounted = 0 ;
if ( stat ( device , & statbuf ) < 0 )
return - ENODEV ;
fp = fopen ( " /proc/self/mountinfo " , " r " ) ;
if ( fp = = NULL )
return - ENOSYS ;
while ( fscanf ( fp , " %*s %*s %i:%i %*[^ \n ] " , & maj , & min ) = = 2 ) {
if ( makedev ( maj , min ) = = statbuf . st_rdev ) {
mounted = 1 ;
break ;
}
}
fclose ( fp ) ;
return mounted ;
}
2008-09-06 17:45:31 +04:00
static void info_scsi_cmd_err ( struct udev * udev , char * cmd , int err )
2008-04-10 01:17:39 +04:00
{
if ( err = = - 1 ) {
2008-09-06 17:45:31 +04:00
info ( udev , " %s failed \n " , cmd ) ;
2008-04-10 01:17:39 +04:00
return ;
}
2008-09-06 17:45:31 +04:00
info ( udev , " %s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh \n " , cmd , SK ( err ) , ASC ( err ) , ASCQ ( err ) ) ;
2008-04-10 01:17:39 +04:00
}
struct scsi_cmd {
struct cdrom_generic_command cgc ;
union {
struct request_sense s ;
unsigned char u [ 18 ] ;
} _sense ;
struct sg_io_hdr sg_io ;
} ;
2010-04-15 22:07:07 +04:00
static void scsi_cmd_init ( struct udev * udev , struct scsi_cmd * cmd , unsigned char * buf , size_t bufsize )
{
memset ( cmd , 0x00 , sizeof ( struct scsi_cmd ) ) ;
memset ( buf , 0x00 , bufsize ) ;
cmd - > cgc . quiet = 1 ;
cmd - > cgc . sense = & cmd - > _sense . s ;
memset ( & cmd - > sg_io , 0 , sizeof ( cmd - > sg_io ) ) ;
cmd - > sg_io . interface_id = ' S ' ;
cmd - > sg_io . mx_sb_len = sizeof ( cmd - > _sense ) ;
cmd - > sg_io . cmdp = cmd - > cgc . cmd ;
cmd - > sg_io . sbp = cmd - > _sense . u ;
cmd - > sg_io . flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO ;
}
static void scsi_cmd_set ( struct udev * udev , struct scsi_cmd * cmd , size_t i , unsigned char arg )
2008-04-10 01:17:39 +04:00
{
cmd - > sg_io . cmd_len = i + 1 ;
cmd - > cgc . cmd [ i ] = arg ;
}
# define CHECK_CONDITION 0x01
2008-09-06 17:45:31 +04:00
static int scsi_cmd_run ( struct udev * udev , struct scsi_cmd * cmd , int fd , unsigned char * buf , size_t bufsize )
2008-04-10 01:17:39 +04:00
{
int ret = 0 ;
cmd - > sg_io . dxferp = buf ;
cmd - > sg_io . dxfer_len = bufsize ;
cmd - > sg_io . dxfer_direction = SG_DXFER_FROM_DEV ;
if ( ioctl ( fd , SG_IO , & cmd - > sg_io ) )
return - 1 ;
if ( ( cmd - > sg_io . info & SG_INFO_OK_MASK ) ! = SG_INFO_OK ) {
errno = EIO ;
ret = - 1 ;
if ( cmd - > sg_io . masked_status & CHECK_CONDITION ) {
ret = ERRCODE ( cmd - > _sense . u ) ;
if ( ret = = 0 )
ret = - 1 ;
}
}
return ret ;
}
2008-09-06 17:45:31 +04:00
static int cd_capability_compat ( struct udev * udev , int fd )
2008-04-10 01:17:39 +04:00
{
2009-08-08 17:29:38 +04:00
int capability ;
2008-04-10 01:17:39 +04:00
2009-08-08 17:29:38 +04:00
capability = ioctl ( fd , CDROM_GET_CAPABILITY , NULL ) ;
if ( capability < 0 ) {
2008-09-06 17:45:31 +04:00
info ( udev , " CDROM_GET_CAPABILITY failed \n " ) ;
2008-04-10 01:17:39 +04:00
return - 1 ;
}
2009-08-08 17:29:38 +04:00
if ( capability & CDC_CD_R )
2008-04-10 01:17:39 +04:00
cd_cd_r = 1 ;
2009-08-08 17:29:38 +04:00
if ( capability & CDC_CD_RW )
2008-04-10 01:17:39 +04:00
cd_cd_rw = 1 ;
2009-08-08 17:29:38 +04:00
if ( capability & CDC_DVD )
2008-04-10 01:17:39 +04:00
cd_dvd_rom = 1 ;
2009-08-08 17:29:38 +04:00
if ( capability & CDC_DVD_R )
2008-04-10 01:17:39 +04:00
cd_dvd_r = 1 ;
2009-08-08 17:29:38 +04:00
if ( capability & CDC_DVD_RAM )
2008-04-10 01:17:39 +04:00
cd_dvd_ram = 1 ;
2009-08-08 17:29:38 +04:00
if ( capability & CDC_MRW )
2008-04-10 01:17:39 +04:00
cd_mrw = 1 ;
2009-08-08 17:29:38 +04:00
if ( capability & CDC_MRW_W )
2008-04-10 01:17:39 +04:00
cd_mrw_w = 1 ;
return 0 ;
}
2009-04-23 17:04:16 +04:00
static int cd_media_compat ( struct udev * udev , int fd )
{
if ( ioctl ( fd , CDROM_DRIVE_STATUS , CDSL_CURRENT ) ! = CDS_DISC_OK ) {
info ( udev , " CDROM_DRIVE_STATUS != CDS_DISC_OK \n " ) ;
return - 1 ;
}
cd_media = 1 ;
return 0 ;
}
2008-09-06 17:45:31 +04:00
static int cd_inquiry ( struct udev * udev , int fd ) {
2008-04-10 01:17:39 +04:00
struct scsi_cmd sc ;
unsigned char inq [ 128 ] ;
int err ;
2010-04-15 22:07:07 +04:00
scsi_cmd_init ( udev , & sc , inq , sizeof ( inq ) ) ;
2008-09-06 17:45:31 +04:00
scsi_cmd_set ( udev , & sc , 0 , 0x12 ) ;
scsi_cmd_set ( udev , & sc , 4 , 36 ) ;
scsi_cmd_set ( udev , & sc , 5 , 0 ) ;
err = scsi_cmd_run ( udev , & sc , fd , inq , 36 ) ;
2010-04-15 10:56:48 +04:00
if ( ( err ! = 0 ) ) {
2008-09-06 17:45:31 +04:00
info_scsi_cmd_err ( udev , " INQUIRY " , err ) ;
2008-04-10 01:17:39 +04:00
return - 1 ;
}
if ( ( inq [ 0 ] & 0x1F ) ! = 5 ) {
2008-09-06 17:45:31 +04:00
info ( udev , " not an MMC unit \n " ) ;
2008-04-10 01:17:39 +04:00
return - 1 ;
}
2008-09-06 17:45:31 +04:00
info ( udev , " INQUIRY: [%.8s][%.16s][%.4s] \n " , inq + 8 , inq + 16 , inq + 32 ) ;
2008-04-10 01:17:39 +04:00
return 0 ;
}
2010-04-15 22:07:07 +04:00
static int feature_profiles ( struct udev * udev , const unsigned char * profiles , size_t size )
2008-04-10 01:17:39 +04:00
{
unsigned int i ;
2010-04-15 22:07:07 +04:00
for ( i = 0 ; i + 4 < = size ; i + = 4 ) {
int profile ;
2008-04-10 01:17:39 +04:00
2010-04-15 22:07:07 +04:00
profile = profiles [ i ] < < 8 | profiles [ i + 1 ] ;
2008-04-10 01:17:39 +04:00
switch ( profile ) {
case 0x03 :
case 0x04 :
case 0x05 :
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x mo \n " , profile ) ;
2008-04-10 01:17:39 +04:00
cd_mo = 1 ;
2010-04-15 22:07:07 +04:00
break ;
2008-04-10 01:17:39 +04:00
case 0x10 :
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x dvd_rom \n " , profile ) ;
2008-04-10 01:17:39 +04:00
cd_dvd_rom = 1 ;
break ;
case 0x12 :
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x dvd_ram \n " , profile ) ;
2008-04-10 01:17:39 +04:00
cd_dvd_ram = 1 ;
break ;
case 0x13 :
case 0x14 :
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x dvd_rw \n " , profile ) ;
2008-04-10 01:17:39 +04:00
cd_dvd_rw = 1 ;
break ;
case 0x1B :
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x dvd_plus_r \n " , profile ) ;
2008-04-10 01:17:39 +04:00
cd_dvd_plus_r = 1 ;
break ;
case 0x1A :
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x dvd_plus_rw \n " , profile ) ;
2008-04-10 01:17:39 +04:00
cd_dvd_plus_rw = 1 ;
break ;
case 0x2A :
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x dvd_plus_rw_dl \n " , profile ) ;
2008-04-10 01:17:39 +04:00
cd_dvd_plus_rw_dl = 1 ;
break ;
case 0x2B :
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x dvd_plus_r_dl \n " , profile ) ;
2008-04-10 01:17:39 +04:00
cd_dvd_plus_r_dl = 1 ;
break ;
case 0x40 :
cd_bd = 1 ;
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x bd \n " , profile ) ;
2008-04-10 01:17:39 +04:00
break ;
case 0x41 :
case 0x42 :
cd_bd_r = 1 ;
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x bd_r \n " , profile ) ;
2008-04-10 01:17:39 +04:00
break ;
case 0x43 :
cd_bd_re = 1 ;
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x bd_re \n " , profile ) ;
2008-04-10 01:17:39 +04:00
break ;
case 0x50 :
cd_hddvd = 1 ;
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x hddvd \n " , profile ) ;
2008-04-10 01:17:39 +04:00
break ;
case 0x51 :
cd_hddvd_r = 1 ;
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x hddvd_r \n " , profile ) ;
2008-04-10 01:17:39 +04:00
break ;
case 0x52 :
cd_hddvd_rw = 1 ;
2010-04-15 22:07:07 +04:00
info ( udev , " profile 0x%02x hddvd_rw \n " , profile ) ;
break ;
default :
info ( udev , " profile 0x%02x <ignored> \n " , profile ) ;
break ;
}
}
return 0 ;
}
static int cd_profiles ( struct udev * udev , int fd )
{
struct scsi_cmd sc ;
unsigned char features [ 65530 ] ;
unsigned int cur_profile = 0 ;
unsigned int len ;
unsigned int i ;
int err ;
scsi_cmd_init ( udev , & sc , features , sizeof ( features ) ) ;
scsi_cmd_set ( udev , & sc , 0 , 0x46 ) ;
scsi_cmd_set ( udev , & sc , 7 , ( sizeof ( features ) > > 8 ) & 0xff ) ;
scsi_cmd_set ( udev , & sc , 8 , sizeof ( features ) & 0xff ) ;
scsi_cmd_set ( udev , & sc , 9 , 0 ) ;
err = scsi_cmd_run ( udev , & sc , fd , features , sizeof ( features ) ) ;
2010-04-15 10:56:48 +04:00
if ( ( err ! = 0 ) ) {
2010-04-15 22:07:07 +04:00
info_scsi_cmd_err ( udev , " GET CONFIGURATION " , err ) ;
return - 1 ;
}
len = features [ 0 ] < < 24 | features [ 1 ] < < 16 | features [ 2 ] < < 8 | features [ 3 ] ;
2010-04-15 23:22:38 +04:00
info ( udev , " GET CONFIGURATION: size of features buffer 0x%04x \n " , len ) ;
2010-04-15 22:07:07 +04:00
if ( len > sizeof ( features ) ) {
info ( udev , " can not get features in a single query, truncating \n " ) ;
len = sizeof ( features ) ;
}
/* device features */
for ( i = 8 ; i + 4 < len ; i + = ( 4 + features [ i + 3 ] ) ) {
unsigned int feature ;
feature = features [ i ] < < 8 | features [ i + 1 ] ;
switch ( feature ) {
case 0x00 :
info ( udev , " GET CONFIGURATION: feature 'profiles', with %i entries \n " , features [ i + 3 ] / 4 ) ;
feature_profiles ( udev , & features [ i ] + 4 , features [ i + 3 ] ) ;
2008-04-10 01:17:39 +04:00
break ;
default :
2010-04-15 23:18:21 +04:00
info ( udev , " GET CONFIGURATION: feature 0x%04x <ignored>, with 0x%02x bytes \n " , feature , features [ i + 3 ] ) ;
2008-04-10 01:17:39 +04:00
break ;
}
}
2010-04-15 22:48:04 +04:00
cur_profile = features [ 6 ] < < 8 | features [ 7 ] ;
if ( cur_profile > 0 ) {
info ( udev , " current profile 0x%02x \n " , cur_profile ) ;
} else {
2008-09-06 17:45:31 +04:00
info ( udev , " no current profile, assuming no media \n " ) ;
2008-04-10 01:17:39 +04:00
return - 1 ;
}
2009-04-21 05:27:14 +04:00
cd_media = 1 ;
2008-04-10 01:17:39 +04:00
switch ( cur_profile ) {
case 0x03 :
case 0x04 :
case 0x05 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_mo = 1 ;
break ;
case 0x08 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_cd_rom \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_cd_rom = 1 ;
break ;
case 0x09 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_cd_r \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_cd_r = 1 ;
break ;
case 0x0a :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_cd_rw \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_cd_rw = 1 ;
break ;
case 0x10 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_dvd_ro \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_dvd_rom = 1 ;
break ;
case 0x11 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_dvd_r \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_dvd_r = 1 ;
break ;
case 0x12 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_dvd_ram \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_dvd_ram = 1 ;
break ;
case 0x13 :
case 0x14 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_dvd_rw \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_dvd_rw = 1 ;
break ;
case 0x1B :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_dvd_plus_r \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_dvd_plus_r = 1 ;
break ;
case 0x1A :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_dvd_plus_rw \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_dvd_plus_rw = 1 ;
break ;
case 0x2A :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_dvd_plus_rw_dl \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_dvd_plus_rw_dl = 1 ;
break ;
case 0x2B :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_dvd_plus_r_dl \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_dvd_plus_r_dl = 1 ;
break ;
case 0x40 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_bd \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_bd = 1 ;
break ;
case 0x41 :
case 0x42 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_bd_r \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_bd_r = 1 ;
break ;
case 0x43 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_bd_re \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_bd_re = 1 ;
break ;
case 0x50 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_hddvd \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_hddvd = 1 ;
break ;
case 0x51 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_hddvd_r \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_hddvd_r = 1 ;
break ;
case 0x52 :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x media_hddvd_rw \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
cd_media_hddvd_rw = 1 ;
break ;
default :
2010-04-15 22:48:04 +04:00
info ( udev , " profile 0x%02x <ignored> \n " , cur_profile ) ;
2008-04-10 01:17:39 +04:00
break ;
}
return 0 ;
}
2008-09-06 17:45:31 +04:00
static int cd_media_info ( struct udev * udev , int fd )
2008-04-10 01:17:39 +04:00
{
struct scsi_cmd sc ;
unsigned char header [ 32 ] ;
static const char * media_status [ ] = {
" blank " ,
" appendable " ,
" complete " ,
" other "
} ;
int err ;
2010-04-15 22:07:07 +04:00
scsi_cmd_init ( udev , & sc , header , sizeof ( header ) ) ;
2008-09-06 17:45:31 +04:00
scsi_cmd_set ( udev , & sc , 0 , 0x51 ) ;
scsi_cmd_set ( udev , & sc , 8 , sizeof ( header ) ) ;
scsi_cmd_set ( udev , & sc , 9 , 0 ) ;
err = scsi_cmd_run ( udev , & sc , fd , header , sizeof ( header ) ) ;
2010-04-15 10:56:48 +04:00
if ( ( err ! = 0 ) ) {
2008-09-06 17:45:31 +04:00
info_scsi_cmd_err ( udev , " READ DISC INFORMATION " , err ) ;
2008-04-10 01:17:39 +04:00
return - 1 ;
} ;
2008-09-06 17:45:31 +04:00
info ( udev , " disk type %02x \n " , header [ 8 ] ) ;
2008-04-10 01:17:39 +04:00
2009-05-26 02:50:45 +04:00
/* exclude plain CDROM, some fake cdroms return 0 for "blank" media here */
if ( ! cd_media_cd_rom & & ( header [ 2 ] & 3 ) < 4 )
2008-04-10 01:17:39 +04:00
cd_media_state = media_status [ header [ 2 ] & 3 ] ;
2009-05-26 02:50:45 +04:00
2008-04-10 01:17:39 +04:00
if ( ( header [ 2 ] & 3 ) ! = 2 )
cd_media_session_next = header [ 10 ] < < 8 | header [ 5 ] ;
cd_media_session_count = header [ 9 ] < < 8 | header [ 4 ] ;
cd_media_track_count = header [ 11 ] < < 8 | header [ 6 ] ;
return 0 ;
}
2008-09-06 17:45:31 +04:00
static int cd_media_toc ( struct udev * udev , int fd )
2008-04-10 01:17:39 +04:00
{
struct scsi_cmd sc ;
unsigned char header [ 12 ] ;
unsigned char toc [ 2048 ] ;
unsigned int len , i ;
unsigned char * p ;
int err ;
2010-04-15 22:07:07 +04:00
scsi_cmd_init ( udev , & sc , header , sizeof ( header ) ) ;
2008-09-06 17:45:31 +04:00
scsi_cmd_set ( udev , & sc , 0 , 0x43 ) ;
scsi_cmd_set ( udev , & sc , 6 , 1 ) ;
scsi_cmd_set ( udev , & sc , 8 , sizeof ( header ) ) ;
scsi_cmd_set ( udev , & sc , 9 , 0 ) ;
err = scsi_cmd_run ( udev , & sc , fd , header , sizeof ( header ) ) ;
2010-04-15 10:56:48 +04:00
if ( ( err ! = 0 ) ) {
2008-09-06 17:45:31 +04:00
info_scsi_cmd_err ( udev , " READ TOC " , err ) ;
2008-04-10 01:17:39 +04:00
return - 1 ;
}
len = ( header [ 0 ] < < 8 | header [ 1 ] ) + 2 ;
2008-09-06 17:45:31 +04:00
info ( udev , " READ TOC: len: %d \n " , len ) ;
2008-04-10 01:17:39 +04:00
if ( len > sizeof ( toc ) )
return - 1 ;
2008-06-12 09:20:14 +04:00
if ( len < 2 )
2008-05-14 18:03:49 +04:00
return - 1 ;
2008-04-10 01:17:39 +04:00
2008-06-12 09:20:14 +04:00
/* empty media has no tracks */
if ( len < 8 )
return 0 ;
2010-04-15 22:07:07 +04:00
scsi_cmd_init ( udev , & sc , toc , sizeof ( toc ) ) ;
2008-09-06 17:45:31 +04:00
scsi_cmd_set ( udev , & sc , 0 , 0x43 ) ;
scsi_cmd_set ( udev , & sc , 6 , header [ 2 ] ) ; /* First Track/Session Number */
scsi_cmd_set ( udev , & sc , 7 , len > > 8 ) ;
scsi_cmd_set ( udev , & sc , 8 , len ) ;
scsi_cmd_set ( udev , & sc , 9 , 0 ) ;
err = scsi_cmd_run ( udev , & sc , fd , toc , len ) ;
2010-04-15 10:56:48 +04:00
if ( ( err ! = 0 ) ) {
2008-09-06 17:45:31 +04:00
info_scsi_cmd_err ( udev , " READ TOC (tracks) " , err ) ;
2008-04-10 01:17:39 +04:00
return - 1 ;
}
for ( p = toc + 4 , i = 4 ; i < len - 8 ; i + = 8 , p + = 8 ) {
unsigned int block ;
2008-06-12 01:24:13 +04:00
unsigned int is_data_track ;
is_data_track = ( p [ 1 ] & 0x04 ) ! = 0 ;
2008-04-10 01:17:39 +04:00
block = p [ 4 ] < < 24 | p [ 5 ] < < 16 | p [ 6 ] < < 8 | p [ 7 ] ;
2008-09-06 17:45:31 +04:00
info ( udev , " track=%u info=0x%x(%s) start_block=%u \n " ,
2008-06-12 01:24:13 +04:00
p [ 2 ] , p [ 1 ] & 0x0f , is_data_track ? " data " : " audio " , block ) ;
if ( is_data_track )
cd_media_track_count_data + + ;
else
cd_media_track_count_audio + + ;
2008-04-10 01:17:39 +04:00
}
2010-04-15 22:07:07 +04:00
scsi_cmd_init ( udev , & sc , header , sizeof ( header ) ) ;
2008-09-06 17:45:31 +04:00
scsi_cmd_set ( udev , & sc , 0 , 0x43 ) ;
scsi_cmd_set ( udev , & sc , 2 , 1 ) ; /* Session Info */
scsi_cmd_set ( udev , & sc , 8 , 12 ) ;
scsi_cmd_set ( udev , & sc , 9 , 0 ) ;
err = scsi_cmd_run ( udev , & sc , fd , header , sizeof ( header ) ) ;
2010-04-15 10:56:48 +04:00
if ( ( err ! = 0 ) ) {
2008-09-06 17:45:31 +04:00
info_scsi_cmd_err ( udev , " READ TOC (multi session) " , err ) ;
2008-04-10 01:17:39 +04:00
return - 1 ;
}
len = header [ 4 + 4 ] < < 24 | header [ 4 + 5 ] < < 16 | header [ 4 + 6 ] < < 8 | header [ 4 + 7 ] ;
2008-09-06 17:45:31 +04:00
info ( udev , " last track %u starts at block %u \n " , header [ 4 + 2 ] , len ) ;
2008-04-10 01:17:39 +04:00
cd_media_session_last_offset = ( unsigned long long int ) len * 2048 ;
return 0 ;
}
2005-08-12 00:59:21 +04:00
int main ( int argc , char * argv [ ] )
{
2008-09-06 17:45:31 +04:00
struct udev * udev ;
2008-04-10 01:17:39 +04:00
static const struct option options [ ] = {
2008-10-02 18:49:05 +04:00
{ " export " , no_argument , NULL , ' x ' } ,
{ " debug " , no_argument , NULL , ' d ' } ,
{ " help " , no_argument , NULL , ' h ' } ,
2008-04-10 01:17:39 +04:00
{ }
} ;
2005-08-12 00:59:21 +04:00
const char * node = NULL ;
int export = 0 ;
2008-04-10 01:17:39 +04:00
int fd = - 1 ;
2010-04-07 13:32:22 +04:00
int cnt ;
2005-08-12 00:59:21 +04:00
int rc = 0 ;
2008-09-06 17:45:31 +04:00
udev = udev_new ( ) ;
if ( udev = = NULL )
goto exit ;
2009-06-10 00:47:48 +04:00
udev_log_init ( " cdrom_id " ) ;
2008-09-06 17:45:31 +04:00
udev_set_log_fn ( udev , log_fn ) ;
2005-08-12 00:59:21 +04:00
2008-04-10 01:17:39 +04:00
while ( 1 ) {
int option ;
2005-08-12 00:59:21 +04:00
2008-04-10 01:17:39 +04:00
option = getopt_long ( argc , argv , " dxh " , options , NULL ) ;
if ( option = = - 1 )
break ;
switch ( option ) {
case ' d ' :
debug = 1 ;
2008-09-06 17:45:31 +04:00
if ( udev_get_log_priority ( udev ) < LOG_INFO )
udev_set_log_priority ( udev , LOG_INFO ) ;
2008-04-10 01:17:39 +04:00
break ;
case ' x ' :
2005-08-12 00:59:21 +04:00
export = 1 ;
2008-04-10 01:17:39 +04:00
break ;
case ' h ' :
printf ( " Usage: cdrom_id [options] <device> \n "
2008-04-21 19:41:39 +04:00
" --export export key/value pairs \n "
" --debug debug to stderr \n "
" --help print this help text \n \n " ) ;
2008-04-10 01:17:39 +04:00
goto exit ;
default :
rc = 1 ;
goto exit ;
}
2005-08-12 00:59:21 +04:00
}
2008-04-10 01:17:39 +04:00
node = argv [ optind ] ;
2005-08-12 00:59:21 +04:00
if ( ! node ) {
2008-09-06 17:45:31 +04:00
err ( udev , " no device \n " ) ;
2008-04-10 01:17:39 +04:00
fprintf ( stderr , " no device \n " ) ;
2005-08-12 00:59:21 +04:00
rc = 1 ;
goto exit ;
}
2010-04-07 13:32:22 +04:00
srand ( ( unsigned int ) getpid ( ) ) ;
for ( cnt = 20 ; cnt > 0 ; cnt - - ) {
2010-04-07 11:24:25 +04:00
struct timespec duration ;
2010-04-07 13:32:22 +04:00
fd = open ( node , O_RDONLY | O_NONBLOCK | ( is_mounted ( node ) ? 0 : O_EXCL ) ) ;
if ( fd > = 0 | | errno ! = EBUSY )
break ;
duration . tv_sec = 0 ;
duration . tv_nsec = ( 100 * 1000 * 1000 ) + ( rand ( ) % 100 * 1000 * 1000 ) ;
nanosleep ( & duration , NULL ) ;
2010-04-07 11:24:25 +04:00
}
2005-08-12 00:59:21 +04:00
if ( fd < 0 ) {
2008-09-06 17:45:31 +04:00
info ( udev , " unable to open '%s' \n " , node ) ;
2009-05-29 15:21:38 +04:00
fprintf ( stderr , " unable to open '%s' \n " , node ) ;
2005-08-12 00:59:21 +04:00
rc = 1 ;
goto exit ;
}
2008-09-06 17:45:31 +04:00
info ( udev , " probing: '%s' \n " , node ) ;
2008-04-10 01:17:39 +04:00
/* same data as original cdrom_id */
2008-09-06 17:45:31 +04:00
if ( cd_capability_compat ( udev , fd ) < 0 ) {
2008-04-10 01:17:39 +04:00
rc = 1 ;
goto exit ;
}
2005-08-12 00:59:21 +04:00
2009-11-10 20:32:38 +03:00
/* check for media - don't bail if there's no media as we still need to
* to read profiles */
cd_media_compat ( udev , fd ) ;
2009-04-23 17:04:16 +04:00
/* check if drive talks MMC */
2009-04-17 02:29:56 +04:00
if ( cd_inquiry ( udev , fd ) < 0 )
goto print ;
2005-08-12 00:59:21 +04:00
2008-04-10 01:17:39 +04:00
/* read drive and possibly current profile */
2008-09-06 17:45:31 +04:00
if ( cd_profiles ( udev , fd ) < 0 )
2008-04-10 01:17:39 +04:00
goto print ;
/* get session/track info */
2008-09-06 17:45:31 +04:00
if ( cd_media_toc ( udev , fd ) < 0 )
2008-04-10 01:17:39 +04:00
goto print ;
2005-08-12 00:59:21 +04:00
2008-04-10 01:17:39 +04:00
/* get writable media state */
2008-09-06 17:45:31 +04:00
if ( cd_media_info ( udev , fd ) < 0 )
2008-04-10 01:17:39 +04:00
goto print ;
print :
printf ( " ID_CDROM=1 \n " ) ;
if ( cd_cd_rom )
printf ( " ID_CDROM_CD=1 \n " ) ;
if ( cd_cd_r )
2005-08-12 01:04:24 +04:00
printf ( " ID_CDROM_CD_R=1 \n " ) ;
2008-04-10 01:17:39 +04:00
if ( cd_cd_rw )
2005-08-12 01:04:24 +04:00
printf ( " ID_CDROM_CD_RW=1 \n " ) ;
2008-04-10 01:17:39 +04:00
if ( cd_dvd_rom )
2005-08-12 01:04:24 +04:00
printf ( " ID_CDROM_DVD=1 \n " ) ;
2008-04-10 01:17:39 +04:00
if ( cd_dvd_r )
2005-08-12 01:04:24 +04:00
printf ( " ID_CDROM_DVD_R=1 \n " ) ;
2008-04-10 01:17:39 +04:00
if ( cd_dvd_rw )
printf ( " ID_CDROM_DVD_RW=1 \n " ) ;
if ( cd_dvd_ram )
2005-08-12 01:04:24 +04:00
printf ( " ID_CDROM_DVD_RAM=1 \n " ) ;
2008-04-10 01:17:39 +04:00
if ( cd_dvd_plus_r )
printf ( " ID_CDROM_DVD_PLUS_R=1 \n " ) ;
if ( cd_dvd_plus_rw )
printf ( " ID_CDROM_DVD_PLUS_RW=1 \n " ) ;
if ( cd_dvd_plus_r_dl )
printf ( " ID_CDROM_DVD_PLUS_R_DL=1 \n " ) ;
if ( cd_dvd_plus_rw_dl )
printf ( " ID_CDROM_DVD_PLUS_RW_DL=1 \n " ) ;
if ( cd_bd )
printf ( " ID_CDROM_BD=1 \n " ) ;
if ( cd_bd_r )
printf ( " ID_CDROM_BD_R=1 \n " ) ;
if ( cd_bd_re )
printf ( " ID_CDROM_BD_RE=1 \n " ) ;
if ( cd_hddvd )
printf ( " ID_CDROM_HDDVD=1 \n " ) ;
if ( cd_hddvd_r )
printf ( " ID_CDROM_HDDVD_R=1 \n " ) ;
if ( cd_hddvd_rw )
printf ( " ID_CDROM_HDDVD_RW=1 \n " ) ;
if ( cd_mo )
printf ( " ID_CDROM_MO=1 \n " ) ;
if ( cd_mrw )
2005-08-12 01:04:24 +04:00
printf ( " ID_CDROM_MRW=1 \n " ) ;
2008-04-10 01:17:39 +04:00
if ( cd_mrw_w )
2005-08-12 01:04:24 +04:00
printf ( " ID_CDROM_MRW_W=1 \n " ) ;
2005-08-12 01:38:33 +04:00
2009-04-21 05:27:14 +04:00
if ( cd_media )
printf ( " ID_CDROM_MEDIA=1 \n " ) ;
2008-04-10 01:17:39 +04:00
if ( cd_media_mo )
printf ( " ID_CDROM_MEDIA_MO=1 \n " ) ;
if ( cd_media_mrw )
printf ( " ID_CDROM_MEDIA_MRW=1 \n " ) ;
if ( cd_media_mrw_w )
printf ( " ID_CDROM_MEDIA_MRW_W=1 \n " ) ;
if ( cd_media_cd_rom )
printf ( " ID_CDROM_MEDIA_CD=1 \n " ) ;
if ( cd_media_cd_r )
printf ( " ID_CDROM_MEDIA_CD_R=1 \n " ) ;
if ( cd_media_cd_rw )
printf ( " ID_CDROM_MEDIA_CD_RW=1 \n " ) ;
if ( cd_media_dvd_rom )
printf ( " ID_CDROM_MEDIA_DVD=1 \n " ) ;
if ( cd_media_dvd_r )
printf ( " ID_CDROM_MEDIA_DVD_R=1 \n " ) ;
if ( cd_media_dvd_ram )
printf ( " ID_CDROM_MEDIA_DVD_RAM=1 \n " ) ;
if ( cd_media_dvd_rw )
printf ( " ID_CDROM_MEDIA_DVD_RW=1 \n " ) ;
if ( cd_media_dvd_plus_r )
printf ( " ID_CDROM_MEDIA_DVD_PLUS_R=1 \n " ) ;
if ( cd_media_dvd_plus_rw )
printf ( " ID_CDROM_MEDIA_DVD_PLUS_RW=1 \n " ) ;
if ( cd_media_dvd_plus_rw_dl )
printf ( " ID_CDROM_MEDIA_DVD_PLUS_RW_DL=1 \n " ) ;
if ( cd_media_dvd_plus_r_dl )
printf ( " ID_CDROM_MEDIA_DVD_PLUS_R_DL=1 \n " ) ;
if ( cd_media_bd )
printf ( " ID_CDROM_MEDIA_BD=1 \n " ) ;
if ( cd_media_bd_r )
printf ( " ID_CDROM_MEDIA_BD_R=1 \n " ) ;
if ( cd_media_bd_re )
printf ( " ID_CDROM_MEDIA_BD_RE=1 \n " ) ;
if ( cd_media_hddvd )
printf ( " ID_CDROM_MEDIA_HDDVD=1 \n " ) ;
if ( cd_media_hddvd_r )
printf ( " ID_CDROM_MEDIA_HDDVD_R=1 \n " ) ;
if ( cd_media_hddvd_rw )
printf ( " ID_CDROM_MEDIA_HDDVD_RW=1 \n " ) ;
if ( cd_media_state ! = NULL )
printf ( " ID_CDROM_MEDIA_STATE=%s \n " , cd_media_state ) ;
if ( cd_media_session_next > 0 )
printf ( " ID_CDROM_MEDIA_SESSION_NEXT=%d \n " , cd_media_session_next ) ;
if ( cd_media_session_count > 0 )
printf ( " ID_CDROM_MEDIA_SESSION_COUNT=%d \n " , cd_media_session_count ) ;
if ( cd_media_track_count > 0 )
printf ( " ID_CDROM_MEDIA_TRACK_COUNT=%d \n " , cd_media_track_count ) ;
2008-08-22 12:04:55 +04:00
if ( cd_media_track_count_audio > 0 )
2008-06-12 01:24:13 +04:00
printf ( " ID_CDROM_MEDIA_TRACK_COUNT_AUDIO=%d \n " , cd_media_track_count_audio ) ;
2008-08-22 12:04:55 +04:00
if ( cd_media_track_count_data > 0 )
2008-06-12 01:24:13 +04:00
printf ( " ID_CDROM_MEDIA_TRACK_COUNT_DATA=%d \n " , cd_media_track_count_data ) ;
2008-08-22 12:04:55 +04:00
if ( cd_media_session_last_offset > 0 )
2008-04-10 01:17:39 +04:00
printf ( " ID_CDROM_MEDIA_SESSION_LAST_OFFSET=%llu \n " , cd_media_session_last_offset ) ;
2005-08-12 00:59:21 +04:00
exit :
2008-04-10 01:17:39 +04:00
if ( fd > = 0 )
close ( fd ) ;
2008-09-06 17:45:31 +04:00
udev_unref ( udev ) ;
2009-06-10 00:47:48 +04:00
udev_log_close ( ) ;
2005-08-12 00:59:21 +04:00
return rc ;
}
2008-04-10 01:17:39 +04:00