RFC: New Features for CDROM-Drives

Christian A. Lademann (cal@zls.com)
Fri, 22 Nov 1996 10:42:19 +0100 (MEZ)


Hi.

Some time ago I posted this message to the list without any feedback
(except by Erik Andersen, the maintainer for the IDE-cdroms who promised
to take a look at it).

Now, let's try again.

I would like to standarize the capabilities of the different cdrom-
drives to 'eject-' and 'close the tray', 'disable the eject-button when the
drive is in use' and 'eject the tray on last close'.

This is very useful especialy when working with supermount.

There are already ioctls defined for eject, close and 'eject on last close',
namely CDROMEJECT, CDROMCLOSETRAY, and CDROMEJECT_SW. I would like to add
the ioctls CDROMLOCK (0x531b), CDROMUNLOCK (0x531c) and CDROMLOCK_SW (0x531d).

The should work as follows:

CDROMLOCK: disable the eject-button immediately, even if the drive is
not in use.

CDROMUNLOCK: enable the eject-button immediately, even if the drive
is in use.

CDROMLOCK_SW: depending on the value of the next parameter to ioctl
enable or disable the lock-on-use-feature.

If the parameter equals 0: disable the feature, if the drive
is currently in use, immediately enable the eject-button.

If the parameter does not equal 0: enable the feature, if the drive
is currently in use, immediately disable the eject-button.

I have patched the IDE- and SCSI-cdrom-drivers to support this proposed
functionality and I wrote a small program 'cdctrl' to use it. Both are
included, the patches work against linux-2.0.25.

Here is the usage-information for cdctrl:

usage: cdctrl [-f device] [-v] [-h] [-V] command [command...]

cdctrl version 1.0.000

valid options are:
-f device name of the cd-rom device (default: /dev/cd0)
-v verbose: show progress
-h show this message
-V show version
-- end of options

valid commands are:
eject eject cdrom
close close cdrom-tray
lock disable eject-button
unlock enable eject-button
eject_on_close eject cdrom on last close
-eject_on_close don't eject cdrom on last close
lock_on_use disable eject-button while cdrom in use
-lock_on_use enable eject-button while cdrom in use

Any feedback welcome.

--Christian
----------------------------------------------------------------------
Christian A. Lademann EMail: <cal@zls.com>
--------------------- speaking from, not for: ------------------------
ZLS Software GmbH Tel.: (+49) 6195 900500
D-65779 Kelkheim Fax: (+49) 6195 900600
----------------------------------------------------------------------
--( snip )--
diff -ruN linux-2.0.24p/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- linux-2.0.24p/drivers/block/ide-cd.c Fri Sep 20 16:00:34 1996
+++ linux/drivers/block/ide-cd.c Tue Nov 5 02:21:01 1996
@@ -250,7 +250,9 @@
__u8 door_locked : 1; /* We think that the drive door is locked. */
__u8 eject_on_close: 1; /* Drive should eject when device is closed. */
__u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */
- __u8 reserved : 2;
+ __u8 auto_lock : 1; /* Drive should locked when device is in use. */
+ __u8 auto_locked : 1;
+ /* __u8 reserved : 2; */
};
#define CDROM_STATE_FLAGS(drive) ((struct ide_cd_state_flags *)&((drive)->bios_head))

@@ -1421,7 +1423,10 @@
(pc->c[0] != REQUEST_SENSE &&
pc->c[0] != ALLOW_MEDIUM_REMOVAL &&
pc->c[0] != START_STOP)) {
- (void) cdrom_lockdoor (drive, 1, NULL);
+
+ CDROM_STATE_FLAGS (drive)->auto_locked = 1;
+ if(CDROM_STATE_FLAGS (drive)->auto_lock)
+ (void) cdrom_lockdoor (drive, 1, NULL);
}
return 0;
}
@@ -2055,6 +2060,22 @@
return 0;
}

+ case CDROMLOCK:
+ return cdrom_lockdoor (drive, 1, NULL);
+
+ case CDROMUNLOCK:
+ return cdrom_lockdoor (drive, 0, NULL);
+
+ case CDROMLOCK_SW: {
+ if((CDROM_STATE_FLAGS (drive)->auto_lock = arg)) {
+ if(CDROM_STATE_FLAGS (drive)->auto_locked)
+ cdrom_lockdoor (drive, 1, NULL);
+ } else
+ cdrom_lockdoor (drive, 0, NULL);
+
+ return 0;
+ }
+
case CDROMPAUSE:
return cdrom_pause (drive, 1, NULL);

@@ -2469,7 +2490,10 @@

cdrom_saw_media_change (drive);
if (arg == -1) {
- (void) cdrom_lockdoor (drive, 0, NULL);
+ CDROM_STATE_FLAGS (drive)->auto_locked = 0;
+ if(CDROM_STATE_FLAGS (drive)->auto_lock)
+ (void) cdrom_lockdoor (drive, 0, NULL);
+
return 0;
}
(void) cdrom_load_unload (drive, (int)arg, NULL);
@@ -2587,7 +2611,9 @@
/* If things worked ok, lock the door and read the
TOC information. */
if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION) {
- (void) cdrom_lockdoor (drive, 1, &my_reqbuf);
+ CDROM_STATE_FLAGS (drive)->auto_locked = 1;
+ if(CDROM_STATE_FLAGS (drive)->auto_lock)
+ (void) cdrom_lockdoor (drive, 1, &my_reqbuf);
(void) cdrom_read_toc (drive, &my_reqbuf);
}
}
@@ -2607,7 +2633,9 @@
invalidate_buffers (inode->i_rdev);

/* Unlock the door. */
- (void) cdrom_lockdoor (drive, 0, NULL);
+ CDROM_STATE_FLAGS (drive)->auto_locked = 0;
+ if(CDROM_STATE_FLAGS (drive)->auto_lock)
+ (void) cdrom_lockdoor (drive, 0, NULL);

/* Do an eject if we were requested to do so. */
if (CDROM_STATE_FLAGS (drive)->eject_on_close)
@@ -2638,13 +2666,18 @@

#if NO_DOOR_LOCKING
CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+ CDROM_STATE_FLAGS (drive)->auto_lock = 0;
#else
CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
+ CDROM_STATE_FLAGS (drive)->auto_lock = 1;
#endif

+ CDROM_STATE_FLAGS (drive)->auto_locked = 0;
+
/* by default Sanyo 3 CD changer support is turned off and
ATAPI Rev 2.2+ standard support for CD changers is used */
CDROM_STATE_FLAGS (drive)->sanyo_slot = 0;
+

if (drive->id != NULL)
CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
diff -ruN linux-2.0.24p/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- linux-2.0.24p/drivers/scsi/sr.c Fri Sep 20 16:00:34 1996
+++ linux/drivers/scsi/sr.c Mon Nov 4 22:21:25 1996
@@ -72,7 +72,10 @@
sync_dev(inode->i_rdev);
if(! --scsi_CDs[MINOR(inode->i_rdev)].device->access_count)
{
- sr_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ scsi_CDs[MINOR(inode->i_rdev)].auto_locked = 0;
+ if(scsi_CDs[MINOR(inode->i_rdev)].auto_lock)
+ sr_ioctl(inode, NULL, CDROMUNLOCK, 0);
+
if (scsi_CDs[MINOR(inode->i_rdev)].auto_eject)
sr_ioctl(inode, NULL, CDROMEJECT, 0);
}
@@ -604,7 +607,11 @@
check_disk_change(inode->i_rdev);

if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++)
- sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+
+ scsi_CDs[MINOR(inode->i_rdev)].auto_locked = 1;
+ if(scsi_CDs[MINOR(inode->i_rdev)].auto_lock)
+ sr_ioctl(inode, NULL, CDROMLOCK, 0);
+
if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
(*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)++;
if(sr_template.usage_count) (*sr_template.usage_count)++;
@@ -662,9 +669,11 @@
* from user space, since we do not want to
* sleep from an interrupt.
*/
- if( SDev->removable && !intr_count )
+ if( SDev->lockable && !intr_count )
{
- scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
+ scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].auto_locked = 1;
+ if(scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].auto_lock)
+ scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
}
SDev->was_reset = 0;
}
@@ -1183,6 +1192,8 @@
scsi_CDs[i].remap = 1;
scsi_CDs[i].auto_eject = 0; /* Default is not to eject upon unmount. */
sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
+ scsi_CDs[i].auto_lock = 1; /* Default is to lock upon usage. */
+ scsi_CDs[i].auto_locked = 0;
}


diff -ruN linux-2.0.24p/drivers/scsi/sr.h linux/drivers/scsi/sr.h
--- linux-2.0.24p/drivers/scsi/sr.h Thu Oct 31 15:58:29 1996
+++ linux/drivers/scsi/sr.h Mon Nov 4 23:43:38 1996
@@ -33,6 +33,8 @@
unsigned remap:1; /* support remapping */
unsigned use:1; /* is this device still supportable */
unsigned auto_eject:1; /* auto-eject medium on last release. */
+ unsigned auto_lock:1; /* auto-lock on usage. */
+ unsigned auto_locked:1; /* auto_lock status. */
} Scsi_CD;

extern Scsi_CD * scsi_CDs;
diff -ruN linux-2.0.24p/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- linux-2.0.24p/drivers/scsi/sr_ioctl.c Sat May 18 21:19:19 1996
+++ linux/drivers/scsi/sr_ioctl.c Mon Nov 4 22:43:29 1996
@@ -307,7 +307,9 @@

/* Gather information about newly inserted disc */
check_disk_change (inode->i_rdev);
- sr_ioctl (inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+ scsi_CDs[MINOR(inode->i_rdev)].auto_locked = 1;
+ if(scsi_CDs[MINOR(inode->i_rdev)].auto_lock)
+ sr_ioctl (inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
sr_photocd (inode);

if (scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size)
@@ -322,7 +324,9 @@
if (scsi_CDs[target].device -> access_count > 1)
return -EBUSY;

- sr_ioctl (inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ scsi_CDs[target].auto_locked = 0;
+ if(scsi_CDs[target].auto_lock)
+ sr_ioctl (inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
sr_cmd[0] = START_STOP;
sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 1;
sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
@@ -335,6 +339,21 @@

case CDROMEJECT_SW:
scsi_CDs[target].auto_eject = !!arg;
+ return 0;
+
+ case CDROMLOCK:
+ return(sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0));
+
+ case CDROMUNLOCK:
+ return(sr_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0));
+
+ case CDROMLOCK_SW:
+ if((scsi_CDs[target].auto_lock = !!arg)) {
+ if(scsi_CDs[target].auto_locked)
+ sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+ } else
+ sr_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+
return 0;

case CDROMVOLCTRL:
diff -ruN linux-2.0.24p/include/linux/cdrom.h linux/include/linux/cdrom.h
--- linux-2.0.24p/include/linux/cdrom.h Fri Sep 20 16:00:36 1996
+++ linux/include/linux/cdrom.h Mon Nov 4 17:02:21 1996
@@ -285,6 +285,13 @@
*/
#define CDROMLOADFROMSLOT 0x531a /* LOAD disk from slot*/

+#define CDROMLOCK 0x531b /* lock tray */
+#define CDROMUNLOCK 0x531c /* unlock tray */
+
+/*
+ * enable (1) / disable (0) auto-locking
+ */
+#define CDROMLOCK_SW 0x531d /* arg: 0 or 1 */

/*
* CD-ROM-specific SCSI command opcodes
--( snip )--
/* cdctrl.c */
/*
control behaviour of cdrom-drives

written by Christian Lademann <cal@zls.de>
*/
/*
07.11.1996:cal:version 1.0.000
*/

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <linux/cdrom.h>

#define VERSION "1.0.000"
#define DEF_DEVICE "/dev/cd0"

#define CMD_EJECT 1
#define CMD_CLOSE_TRAY 2
#define CMD_LOCK 3
#define CMD_UNLOCK 4
#define CMD_EJECT_ON_CLOSE 5
#define CMD_LOCK_ON_USE 6

int usage() {
fprintf(stderr, "usage: cdctrl [-f device] [-v] [-h] [-V] command [command...]\n");
fprintf(stderr, "\ncdctrl version %s\n", VERSION);
fprintf(stderr, "\nvalid options are:\n");
fprintf(stderr, " -f device name of the cd-rom device (default: /dev/cd0)\n");
fprintf(stderr, " -v verbose: show progress\n");
fprintf(stderr, " -h show this message\n");
fprintf(stderr, " -V show version\n");
fprintf(stderr, " -- end of options\n");
fprintf(stderr, "\nvalid commands are:\n");
fprintf(stderr, " eject eject cdrom\n");
fprintf(stderr, " close close cdrom-tray\n");
fprintf(stderr, " lock disable eject-button\n");
fprintf(stderr, " unlock enable eject-button\n");
fprintf(stderr, " eject_on_close eject cdrom on last close\n");
fprintf(stderr, " -eject_on_close don't eject cdrom on last close\n");
fprintf(stderr, " lock_on_use disable eject-button while cdrom in use\n");
fprintf(stderr, " -lock_on_use enable eject-button while cdrom in use\n");
exit(1);
}

int version() {
printf("cdctrl version %s\n", VERSION);
exit(0);
}

main(int argc, char *argv []) {
extern int optind, opterr;
extern char *optarg;

char *device = NULL,
msg [80];

int c,
cmd = 0,
dev = -1,
toggle = 1,
ioctl_cmd = 0,
ioctl_arg = 0,
verbose = 0;

while((c = getopt(argc, argv, "f:hvV")) != EOF)
switch(c) {
case 'f': device = strdup(optarg); break;
case 'v': verbose++; break;
case 'V': version(); break;
case 'h':
case '?': usage(); break;
case '-': break;
}

if(device == NULL)
device = strdup(DEF_DEVICE);

if(optind >= argc)
usage();

if((dev = open(device, O_RDONLY)) < 0) {
sprintf(msg, "cannot open device \"%s\"", device);
perror(msg);
exit(1);
}

while(optind < argc) {
cmd = 0;
toggle = 1;

if(strcmp(argv [optind], "eject") == 0)
cmd = CMD_EJECT;
else if(strcmp(argv [optind], "close") == 0)
cmd = CMD_CLOSE_TRAY;
else if(strcmp(argv [optind], "lock") == 0)
cmd = CMD_LOCK;
else if(strcmp(argv [optind], "unlock") == 0)
cmd = CMD_UNLOCK;
else {
if(argv [optind] [0] == '-' || argv [optind] [0] == '+') {
toggle = (argv [optind] [0] == '+');
argv [optind]++;
}

if(strcmp(argv [optind], "eject_on_close") == 0)
cmd = CMD_EJECT_ON_CLOSE;
else if(strcmp(argv [optind], "lock_on_use") == 0)
cmd = CMD_LOCK_ON_USE;
}

ioctl_cmd = ioctl_arg = 0;

switch(cmd) {
case CMD_EJECT:
if(verbose)
sprintf(msg, "eject cdrom");

ioctl_cmd = CDROMEJECT;
break;

case CMD_CLOSE_TRAY:
if(verbose)
sprintf(msg, "close tray");

ioctl_cmd = CDROMCLOSETRAY;
break;

case CMD_LOCK:
if(verbose)
sprintf(msg, "lock cdrom");

ioctl_cmd = CDROMLOCK;
break;

case CMD_UNLOCK:
if(verbose)
sprintf(msg, "unlock cdrom");

ioctl_cmd = CDROMUNLOCK;
break;

case CMD_EJECT_ON_CLOSE:
if(verbose)
sprintf(msg, "%s eject-on-close flag", (toggle ? "set" : "clear"));

ioctl_cmd = CDROMEJECT_SW;
ioctl_arg = toggle;
break;

case CMD_LOCK_ON_USE:
if(verbose)
sprintf(msg, "%s lock-on-use flag", (toggle ? "set" : "clear"));

ioctl_cmd = CDROMLOCK_SW;
ioctl_arg = toggle;
break;

default:
fprintf(stderr, "unrecognized command \"%s\"\n", argv [optind]);
exit(1);
break;
}

if(ioctl_cmd) {
if(verbose)
printf("%s ... ", msg);

if(ioctl(dev, ioctl_cmd, ioctl_arg) < 0) {
perror("ioctl failed");
exit(1);
}

if(verbose)
printf("ok.\n");
}

optind++;
}

close(dev);

exit(0);
}
--( snip )--