/* * Taken in part from dvd+rw-tools by Andy Polyakov * * Use-it-on-your-own-risk, GPL bless... * * For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/ */ #define CREAM_ON_ERRNO(s) do { \ switch ((s)[2]&0x0F) \ { case 2: if ((s)[12]==4) errno=EAGAIN; break; \ case 5: errno=EINVAL; \ if ((s)[13]==0) \ { if ((s)[12]==0x21) errno=ENOSPC; \ else if ((s)[12]==0x20) errno=ENODEV; \ } \ break; \ } \ } while(0) #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) #ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 #endif #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef enum { NONE = CGC_DATA_NONE, // 3 READ = CGC_DATA_READ, // 2 WRITE = CGC_DATA_WRITE // 1 } Direction; typedef struct ScsiCommand ScsiCommand; struct ScsiCommand { int fd; int autoclose; char *filename; struct cdrom_generic_command cgc; union { struct request_sense s; unsigned char u[18]; } _sense; struct sg_io_hdr sg_io; }; #define DIRECTION(i) (Dir_xlate[i]); /* 1,CGC_DATA_WRITE * 2,CGC_DATA_READ * 3,CGC_DATA_NONE */ const int Dir_xlate[4] = { 0, // implementation-dependent... SG_DXFER_TO_DEV, // 1,CGC_DATA_WRITE SG_DXFER_FROM_DEV, // 2,CGC_DATA_READ SG_DXFER_NONE // 3,CGC_DATA_NONE }; static ScsiCommand * scsi_command_new (void) { ScsiCommand *cmd; cmd = (ScsiCommand *) malloc (sizeof (ScsiCommand)); memset (cmd, 0, sizeof (ScsiCommand)); cmd->fd = -1; cmd->filename = NULL; cmd->autoclose = 1; return cmd; } static ScsiCommand * scsi_command_new_from_fd (int f) { ScsiCommand *cmd; cmd = scsi_command_new (); cmd->fd = f; cmd->autoclose = 0; return cmd; } static void scsi_command_free (ScsiCommand * cmd) { if (cmd->fd >= 0 && cmd->autoclose) { close (cmd->fd); cmd->fd = -1; } if (cmd->filename) { free (cmd->filename); cmd->filename = NULL; } free (cmd); } static int scsi_command_transport (ScsiCommand * cmd, Direction dir, void *buf, size_t sz) { int ret = 0; cmd->sg_io.dxferp = buf; cmd->sg_io.dxfer_len = sz; cmd->sg_io.dxfer_direction = DIRECTION (dir); if (ioctl (cmd->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) { CREAM_ON_ERRNO (cmd->sg_io.sbp); ret = ERRCODE (cmd->sg_io.sbp); if (ret == 0) ret = -1; } } return ret; } static void scsi_command_init (ScsiCommand * cmd, size_t i, int arg) { if (i == 0) { memset (&cmd->cgc, 0, sizeof (cmd->cgc)); memset (&cmd->_sense, 0, sizeof (cmd->_sense)); 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; } cmd->sg_io.cmd_len = i + 1; cmd->cgc.cmd[i] = arg; } static void usage (const char *prog) { printf ("Usage: %s [poll|notif]\n", prog); } int main (int argc, char *argv[]) { int fd; char *device_file; ScsiCommand *cmd; unsigned char reply[32]; unsigned int i; unsigned int len; int do_poll; if (argc != 3) { usage (argv[0]); goto out; } device_file = argv[1]; if (strcmp (argv[2], "poll") == 0) do_poll = 1; else if (strcmp (argv[2], "notif") == 0) do_poll = 0; else { usage (argv[0]); goto out; } printf ("Using device %s and %s\n", device_file, do_poll ? "polling" : "notification"); fd = open (device_file, O_RDONLY|O_NONBLOCK); if (fd < 0) { printf ("Cannot open %s: %s\n", device_file, strerror (errno)); goto out; } /* see MMC-3 draft revision 10g, section 5.6 */ cmd = scsi_command_new_from_fd (fd); scsi_command_init (cmd, 0, 0x4a); /* GET_EVENT/STATUS NOTIFICATION */ scsi_command_init (cmd, 1, do_poll ? 0x01 : 0x00); /* IMMED */ scsi_command_init (cmd, 4, 0x10); /* Notification: Media */ scsi_command_init (cmd, 8, 32); scsi_command_init (cmd, 9, 0); printf ("Sending command...\n"); if (scsi_command_transport (cmd, READ, reply, sizeof (reply))) { printf ("Sending command failed: %s\n", strerror (errno)); scsi_command_free (cmd); return 0; } len = (reply[0]<<8) | reply[1]; printf ("Got reply; size %d bytes\ndata: ", len); for (i = 0; i < len; i++) { printf ("0x%02x ", reply[i]); } printf ("\n"); printf ("Media Present: %d\n", reply[5] & 0x02); printf ("Door or Tray Open : %d\n", reply[5] & 0x01); scsi_command_free (cmd); close (fd); out: return 0; }