cdrom_id: add tray lock and eject handling

This commit is contained in:
Kay Sievers 2011-06-18 20:54:47 +02:00
parent 693b6344e1
commit b20fd3cb34
2 changed files with 105 additions and 31 deletions

View file

@ -5,10 +5,14 @@ SUBSYSTEM!="block", GOTO="cdrom_end"
KERNEL!="sr[0-9]*|xvd*", GOTO="cdrom_end" KERNEL!="sr[0-9]*|xvd*", GOTO="cdrom_end"
ENV{DEVTYPE}!="disk", GOTO="cdrom_end" ENV{DEVTYPE}!="disk", GOTO="cdrom_end"
# this is only a button press event # unconditionally tag device as CDROM
ENV{DISK_EJECT_REQUEST}=="?*", GOTO="cdrom_end"
KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1" KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1"
IMPORT{program}="cdrom_id $tempnode"
# media eject button pressed
ENV{DISK_EJECT_REQUEST}=="?*", RUN+="cdrom_id --eject-media $tempnode", GOTO="cdrom_end"
# import device and media properties and lock tray to
# enable the receiving of media eject button events
IMPORT{program}="cdrom_id --lock-media $tempnode"
LABEL="cdrom_end" LABEL="cdrom_end"

View file

@ -41,7 +41,7 @@
#include "libudev.h" #include "libudev.h"
#include "libudev-private.h" #include "libudev-private.h"
static int debug; static bool debug;
static void log_fn(struct udev *udev, int priority, static void log_fn(struct udev *udev, int priority,
const char *file, int line, const char *fn, const char *file, int line, const char *fn,
@ -156,13 +156,11 @@ struct scsi_cmd {
struct sg_io_hdr sg_io; struct sg_io_hdr sg_io;
}; };
static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd, unsigned char *buf, size_t bufsize) static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd)
{ {
memset(cmd, 0x00, sizeof(struct scsi_cmd)); memset(cmd, 0x00, sizeof(struct scsi_cmd));
memset(buf, 0x00, bufsize);
cmd->cgc.quiet = 1; cmd->cgc.quiet = 1;
cmd->cgc.sense = &cmd->_sense.s; cmd->cgc.sense = &cmd->_sense.s;
memset(&cmd->sg_io, 0, sizeof(cmd->sg_io));
cmd->sg_io.interface_id = 'S'; cmd->sg_io.interface_id = 'S';
cmd->sg_io.mx_sb_len = sizeof(cmd->_sense); cmd->sg_io.mx_sb_len = sizeof(cmd->_sense);
cmd->sg_io.cmdp = cmd->cgc.cmd; cmd->sg_io.cmdp = cmd->cgc.cmd;
@ -182,9 +180,13 @@ static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigne
{ {
int ret = 0; int ret = 0;
cmd->sg_io.dxferp = buf; if (bufsize > 0) {
cmd->sg_io.dxfer_len = bufsize; cmd->sg_io.dxferp = buf;
cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV; cmd->sg_io.dxfer_len = bufsize;
cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV;
} else {
cmd->sg_io.dxfer_direction = SG_DXFER_NONE;
}
if (ioctl(fd, SG_IO, &cmd->sg_io)) if (ioctl(fd, SG_IO, &cmd->sg_io))
return -1; return -1;
@ -200,6 +202,39 @@ static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigne
return ret; return ret;
} }
static int media_lock(struct udev *udev, int fd, bool lock)
{
int err;
/* disable the kernel's lock logic */
err = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_LOCK);
if (err < 0)
info(udev, "CDROM_CLEAR_OPTIONS, CDO_LOCK failed\n");
err = ioctl(fd, CDROM_LOCKDOOR, lock ? 1 : 0);
if (err < 0)
info(udev, "CDROM_LOCKDOOR failed\n");
return err;
}
static int media_eject(struct udev *udev, int fd)
{
struct scsi_cmd sc;
int err;
scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x1b);
scsi_cmd_set(udev, &sc, 4, 0x02);
scsi_cmd_set(udev, &sc, 5, 0);
err = scsi_cmd_run(udev, &sc, fd, NULL, 0);
if ((err != 0)) {
info_scsi_cmd_err(udev, "START_STOP_UNIT", err);
return -1;
}
return 0;
}
static int cd_capability_compat(struct udev *udev, int fd) static int cd_capability_compat(struct udev *udev, int fd)
{ {
int capability; int capability;
@ -237,12 +272,13 @@ static int cd_media_compat(struct udev *udev, int fd)
return 0; return 0;
} }
static int cd_inquiry(struct udev *udev, int fd) { static int cd_inquiry(struct udev *udev, int fd)
{
struct scsi_cmd sc; struct scsi_cmd sc;
unsigned char inq[128]; unsigned char inq[128];
int err; int err;
scsi_cmd_init(udev, &sc, inq, sizeof(inq)); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x12); scsi_cmd_set(udev, &sc, 0, 0x12);
scsi_cmd_set(udev, &sc, 4, 36); scsi_cmd_set(udev, &sc, 4, 36);
scsi_cmd_set(udev, &sc, 5, 0); scsi_cmd_set(udev, &sc, 5, 0);
@ -467,7 +503,7 @@ static int cd_profiles_old_mmc(struct udev *udev, int fd)
unsigned char header[32]; unsigned char header[32];
scsi_cmd_init(udev, &sc, header, sizeof(header)); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x51); scsi_cmd_set(udev, &sc, 0, 0x51);
scsi_cmd_set(udev, &sc, 8, sizeof(header)); scsi_cmd_set(udev, &sc, 8, sizeof(header));
scsi_cmd_set(udev, &sc, 9, 0); scsi_cmd_set(udev, &sc, 9, 0);
@ -513,7 +549,7 @@ static int cd_profiles(struct udev *udev, int fd)
ret = -1; ret = -1;
/* First query the current profile */ /* First query the current profile */
scsi_cmd_init(udev, &sc, features, sizeof(features)); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x46); scsi_cmd_set(udev, &sc, 0, 0x46);
scsi_cmd_set(udev, &sc, 8, 8); scsi_cmd_set(udev, &sc, 8, 8);
scsi_cmd_set(udev, &sc, 9, 0); scsi_cmd_set(udev, &sc, 9, 0);
@ -549,7 +585,7 @@ static int cd_profiles(struct udev *udev, int fd)
} }
/* Now get the full feature buffer */ /* Now get the full feature buffer */
scsi_cmd_init(udev, &sc, features, len); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x46); scsi_cmd_set(udev, &sc, 0, 0x46);
scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff); scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff);
scsi_cmd_set(udev, &sc, 8, len & 0xff); scsi_cmd_set(udev, &sc, 8, len & 0xff);
@ -601,7 +637,7 @@ static int cd_media_info(struct udev *udev, int fd)
}; };
int err; int err;
scsi_cmd_init(udev, &sc, header, sizeof(header)); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x51); scsi_cmd_set(udev, &sc, 0, 0x51);
scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff);
scsi_cmd_set(udev, &sc, 9, 0); scsi_cmd_set(udev, &sc, 9, 0);
@ -639,7 +675,7 @@ static int cd_media_info(struct udev *udev, int fd)
unsigned char dvdstruct[8]; unsigned char dvdstruct[8];
unsigned char format[12]; unsigned char format[12];
scsi_cmd_init(udev, &sc, dvdstruct, sizeof(dvdstruct)); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0xAD); scsi_cmd_set(udev, &sc, 0, 0xAD);
scsi_cmd_set(udev, &sc, 7, 0xC0); scsi_cmd_set(udev, &sc, 7, 0xC0);
scsi_cmd_set(udev, &sc, 9, sizeof(dvdstruct)); scsi_cmd_set(udev, &sc, 9, sizeof(dvdstruct));
@ -656,7 +692,7 @@ static int cd_media_info(struct udev *udev, int fd)
} }
/* let's make sure we don't try to read unformatted media */ /* let's make sure we don't try to read unformatted media */
scsi_cmd_init(udev, &sc, format, sizeof(format)); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x23); scsi_cmd_set(udev, &sc, 0, 0x23);
scsi_cmd_set(udev, &sc, 8, sizeof(format)); scsi_cmd_set(udev, &sc, 8, sizeof(format));
scsi_cmd_set(udev, &sc, 9, 0); scsi_cmd_set(udev, &sc, 9, 0);
@ -697,7 +733,7 @@ static int cd_media_info(struct udev *udev, int fd)
* has "blank" status", DVD-RAM was examined earlier) and check * has "blank" status", DVD-RAM was examined earlier) and check
* for ISO and UDF PVDs or a fs superblock presence and do it * for ISO and UDF PVDs or a fs superblock presence and do it
* in one ioctl (we need just sectors 0 and 16) */ * in one ioctl (we need just sectors 0 and 16) */
scsi_cmd_init(udev, &sc, buffer, sizeof(buffer)); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x28); scsi_cmd_set(udev, &sc, 0, 0x28);
scsi_cmd_set(udev, &sc, 5, 0); scsi_cmd_set(udev, &sc, 5, 0);
scsi_cmd_set(udev, &sc, 8, 32); scsi_cmd_set(udev, &sc, 8, 32);
@ -750,7 +786,7 @@ static int cd_media_toc(struct udev *udev, int fd)
unsigned char *p; unsigned char *p;
int err; int err;
scsi_cmd_init(udev, &sc, header, sizeof(header)); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x43); scsi_cmd_set(udev, &sc, 0, 0x43);
scsi_cmd_set(udev, &sc, 6, 1); scsi_cmd_set(udev, &sc, 6, 1);
scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff);
@ -774,7 +810,7 @@ static int cd_media_toc(struct udev *udev, int fd)
if (len < 8) if (len < 8)
return 0; return 0;
scsi_cmd_init(udev, &sc, toc, sizeof(toc)); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x43); scsi_cmd_set(udev, &sc, 0, 0x43);
scsi_cmd_set(udev, &sc, 6, header[2]); /* First Track/Session Number */ scsi_cmd_set(udev, &sc, 6, header[2]); /* First Track/Session Number */
scsi_cmd_set(udev, &sc, 7, (len >> 8) & 0xff); scsi_cmd_set(udev, &sc, 7, (len >> 8) & 0xff);
@ -805,7 +841,7 @@ static int cd_media_toc(struct udev *udev, int fd)
cd_media_track_count_audio++; cd_media_track_count_audio++;
} }
scsi_cmd_init(udev, &sc, header, sizeof(header)); scsi_cmd_init(udev, &sc);
scsi_cmd_set(udev, &sc, 0, 0x43); scsi_cmd_set(udev, &sc, 0, 0x43);
scsi_cmd_set(udev, &sc, 2, 1); /* Session Info */ scsi_cmd_set(udev, &sc, 2, 1); /* Session Info */
scsi_cmd_set(udev, &sc, 8, sizeof(header)); scsi_cmd_set(udev, &sc, 8, sizeof(header));
@ -825,10 +861,16 @@ int main(int argc, char *argv[])
{ {
struct udev *udev; struct udev *udev;
static const struct option options[] = { static const struct option options[] = {
{ "lock-media", no_argument, NULL, 'l' },
{ "unlock-media", no_argument, NULL, 'u' },
{ "eject-media", no_argument, NULL, 'e' },
{ "debug", no_argument, NULL, 'd' }, { "debug", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{} {}
}; };
bool eject = false;
bool lock = false;
bool unlock = false;
const char *node = NULL; const char *node = NULL;
int fd = -1; int fd = -1;
int cnt; int cnt;
@ -844,18 +886,30 @@ int main(int argc, char *argv[])
while (1) { while (1) {
int option; int option;
option = getopt_long(argc, argv, "dh", options, NULL); option = getopt_long(argc, argv, "deluh", options, NULL);
if (option == -1) if (option == -1)
break; break;
switch (option) { switch (option) {
case 'l':
lock = true;
break;
case 'u':
unlock = true;
break;
case 'e':
eject = true;
break;
case 'd': case 'd':
debug = 1; debug = true;
if (udev_get_log_priority(udev) < LOG_INFO) if (udev_get_log_priority(udev) < LOG_INFO)
udev_set_log_priority(udev, LOG_INFO); udev_set_log_priority(udev, LOG_INFO);
break; break;
case 'h': case 'h':
printf("Usage: cdrom_id [options] <device>\n" printf("Usage: cdrom_id [options] <device>\n"
" --lock-media lock the media (to enable eject request events)\n"
" --unlock-media unlock the media\n"
" --eject-media eject the media\n"
" --debug debug to stderr\n" " --debug debug to stderr\n"
" --help print this help text\n\n"); " --help print this help text\n\n");
goto exit; goto exit;
@ -904,14 +958,13 @@ int main(int argc, char *argv[])
/* check if drive talks MMC */ /* check if drive talks MMC */
if (cd_inquiry(udev, fd) < 0) if (cd_inquiry(udev, fd) < 0)
goto print; goto work;
/* read drive and possibly current profile */ /* read drive and possibly current profile */
if (cd_profiles(udev, fd) != 0) if (cd_profiles(udev, fd) != 0)
goto print; goto work;
/* at this point we are guaranteed to have media in the /* at this point we are guaranteed to have media in the drive - find out more about it */
* drive - find out more about it */
/* get session/track info */ /* get session/track info */
cd_media_toc(udev, fd); cd_media_toc(udev, fd);
@ -919,7 +972,25 @@ int main(int argc, char *argv[])
/* get writable media state */ /* get writable media state */
cd_media_info(udev, fd); cd_media_info(udev, fd);
print: work:
/* lock the media, so we enable eject button events */
if (lock && cd_media) {
info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (lock)\n");
media_lock(udev, fd, true);
}
if (unlock && cd_media) {
info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)\n");
media_lock(udev, fd, false);
}
if (eject) {
info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)\n");
media_lock(udev, fd, false);
info(udev, "START_STOP_UNIT (eject)\n");
media_eject(udev, fd);
}
printf("ID_CDROM=1\n"); printf("ID_CDROM=1\n");
if (cd_cd_rom) if (cd_cd_rom)
printf("ID_CDROM_CD=1\n"); printf("ID_CDROM_CD=1\n");
@ -1026,4 +1097,3 @@ exit:
udev_log_close(); udev_log_close();
return rc; return rc;
} }