patch-2.3.23 linux/drivers/cdrom/cdrom.c
Next file: linux/drivers/char/Config.in
Previous file: linux/drivers/block/via82cxxx.c
Back to the patch index
Back to the overall index
- Lines: 371
- Date:
Tue Oct 19 13:47:40 1999
- Orig file:
v2.3.22/linux/drivers/cdrom/cdrom.c
- Orig date:
Mon Oct 4 15:49:29 1999
diff -u --recursive --new-file v2.3.22/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
@@ -177,10 +177,15 @@
for ide-cd to handle multisession discs.
-- Export cdrom_mode_sense and cdrom_mode_select.
-- init_cdrom_command() for setting up a cgc command.
+
+ 3.05 Sep 23, 1999 - Jens Axboe <axboe@image.dk>
+ -- Changed the interface for CDROM_SEND_PACKET. Before it was virtually
+ impossible to send the drive data in a sensible way.
+
-------------------------------------------------------------------------*/
-#define REVISION "Revision: 3.04"
-#define VERSION "Id: cdrom.c 3.04 1999/09/14"
+#define REVISION "Revision: 3.05"
+#define VERSION "Id: cdrom.c 3.05 1999/09/23"
/* I use an error-log mask to give fine grain control over the type of
messages dumped to the system logs. The available masks include: */
@@ -213,6 +218,7 @@
#include <linux/cdrom.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
+#include <linux/init.h>
#include <asm/fcntl.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
@@ -316,7 +322,7 @@
if (cdo->open == NULL || cdo->release == NULL)
return -2;
if ( !banner_printed ) {
- printk(KERN_INFO "Uniform CDROM driver " REVISION "\n");
+ printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n");
banner_printed = 1;
#ifdef CONFIG_SYSCTL
cdrom_sysctl_register();
@@ -404,6 +410,8 @@
return cdi;
}
+static int cdrom_setup_writemode(struct cdrom_device_info *cdi);
+
/* We use the open-option O_NONBLOCK to indicate that the
* purpose of opening is only for subsequent ioctl() calls; no device
* integrity checks are performed.
@@ -415,22 +423,31 @@
static
int cdrom_open(struct inode *ip, struct file *fp)
{
+ struct cdrom_device_info *cdi;
kdev_t dev = ip->i_rdev;
- struct cdrom_device_info *cdi = cdrom_find_device(dev);
- int purpose = !!(fp->f_flags & O_NONBLOCK);
- int ret=0;
+ int ret;
cdinfo(CD_OPEN, "entering cdrom_open\n");
- if (cdi == NULL)
+ if ((cdi = cdrom_find_device(dev)) == NULL)
return -ENODEV;
- if (fp->f_mode & FMODE_WRITE)
- return -EROFS;
- purpose = purpose || !(cdi->options & CDO_USE_FFLAGS);
- if (purpose)
- ret = cdi->ops->open(cdi, purpose);
+
+ /* just CD-RW for now. DVD-RW will come soon, CD-R and DVD-R
+ * need to be handled differently. */
+ if ((fp->f_mode & FMODE_WRITE) && !CDROM_CAN(CDC_CD_RW))
+ return -EROFS;
+
+ /* if this was a O_NONBLOCK open and we should honor the flags,
+ * do a quick open without drive/disc integrity checks. */
+ if ((fp->f_flags & O_NONBLOCK) && (cdi->options & CDO_USE_FFLAGS))
+ ret = cdi->ops->open(cdi, 1);
else
ret = open_for_data(cdi);
+
if (!ret) cdi->use_count++;
+
+ if (fp->f_mode & FMODE_WRITE && !cdi->write.writeable)
+ cdi->write.writeable = !cdrom_setup_writemode(cdi);
+
cdinfo(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", cdi->name, cdi->use_count);
/* Do this on open. Don't wait for mount, because they might
not be mounting, but opening with O_NONBLOCK */
@@ -525,7 +542,7 @@
if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) {
cdo->lock_door(cdi, 1);
cdinfo(CD_OPEN, "door locked.\n");
- }
+ }
cdinfo(CD_OPEN, "device opened successfully.\n");
return ret;
@@ -616,7 +633,7 @@
if (cdi->use_count > 0) cdi->use_count--;
if (cdi->use_count == 0)
cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name);
- if (cdi->use_count == 0 && /* last process that closes dev*/
+ if (cdi->use_count == 0 &&
cdo->capability & CDC_LOCK && !keeplocked) {
cdinfo(CD_CLOSE, "Unlocking door!\n");
cdo->lock_door(cdi, 0);
@@ -1701,8 +1718,8 @@
unsigned long arg)
{
struct cdrom_device_ops *cdo = cdi->ops;
- kdev_t dev = cdi->dev;
struct cdrom_generic_command cgc;
+ kdev_t dev = cdi->dev;
char buffer[32];
int ret = 0;
@@ -1919,7 +1936,7 @@
case CDROMSTART:
case CDROMSTOP: {
- cdinfo(CD_DO_IOCTL, "entering audio ioctl (start/stop)\n");
+ cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n");
cgc.cmd[0] = GPCMD_START_STOP_UNIT;
cgc.cmd[1] = 1;
cgc.cmd[4] = (cmd == CDROMSTART) ? 1 : 0;
@@ -1928,7 +1945,7 @@
case CDROMPAUSE:
case CDROMRESUME: {
- cdinfo(CD_DO_IOCTL, "entering audio ioctl (pause/resume)\n");
+ cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n");
cgc.cmd[0] = GPCMD_PAUSE_RESUME;
cgc.cmd[8] = (cmd == CDROMRESUME) ? 1 : 0;
return cdo->generic_packet(cdi, &cgc);
@@ -1938,7 +1955,7 @@
dvd_struct s;
if (!CDROM_CAN(CDC_DVD))
return -ENOSYS;
- cdinfo(CD_DO_IOCTL, "entering dvd_read_struct\n");
+ cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n");
IOCTL_IN(arg, dvd_struct, s);
if ((ret = dvd_read_struct(cdi, &s)))
return ret;
@@ -1950,7 +1967,7 @@
dvd_authinfo ai;
if (!CDROM_CAN(CDC_DVD))
return -ENOSYS;
- cdinfo(CD_DO_IOCTL, "entering dvd_auth\n");
+ cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n");
IOCTL_IN(arg, dvd_authinfo, ai);
if ((ret = dvd_do_auth (cdi, &ai)))
return ret;
@@ -1959,26 +1976,58 @@
}
case CDROM_SEND_PACKET: {
+ __u8 *userbuf, copy = 0;
if (!CDROM_CAN(CDC_GENERIC_PACKET))
return -ENOSYS;
- cdinfo(CD_DO_IOCTL, "entering send_packet\n");
+ cdinfo(CD_DO_IOCTL, "entering CDROM_SEND_PACKET\n");
IOCTL_IN(arg, struct cdrom_generic_command, cgc);
- cgc.buffer = kmalloc(cgc.buflen, GFP_KERNEL);
+ copy = !!cgc.buflen;
+ userbuf = cgc.buffer;
+ cgc.buffer = NULL;
+ if (userbuf != NULL && copy) {
+ /* usually commands just copy data one way, i.e.
+ * we send a buffer to the drive and the command
+ * specifies whether the drive will read or
+ * write to that buffer. usually the buffers
+ * are very small, so we don't loose that much
+ * by doing a redundant copy each time. */
+ if (!access_ok(VERIFY_WRITE, userbuf, cgc.buflen)) {
+ printk("can't get write perms\n");
+ return -EFAULT;
+ }
+ if (!access_ok(VERIFY_READ, userbuf, cgc.buflen)) {
+ printk("can't get read perms\n");
+ return -EFAULT;
+ }
+ }
+ /* reasonable limits */
+ if (cgc.buflen < 0 || cgc.buflen > 131072) {
+ printk("invalid size given\n");
+ return -EINVAL;
+ }
+ if (copy) {
+ cgc.buffer = kmalloc(cgc.buflen, GFP_KERNEL);
+ if (cgc.buffer == NULL)
+ return -ENOMEM;
+ __copy_from_user(cgc.buffer, userbuf, cgc.buflen);
+ }
ret = cdo->generic_packet(cdi, &cgc);
- if (copy_to_user((void*)arg, cgc.buffer, cgc.buflen))
- ret = -EFAULT;
+ if (copy && !ret)
+ __copy_to_user(userbuf, cgc.buffer, cgc.buflen);
kfree(cgc.buffer);
return ret;
}
case CDROM_NEXT_WRITABLE: {
- long next;
+ long next = 0;
+ cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n");
if ((ret = cdrom_get_next_writable(dev, &next)))
return ret;
IOCTL_OUT(arg, long, next);
return 0;
}
case CDROM_LAST_WRITTEN: {
- long last;
+ long last = 0;
+ cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n");
if ((ret = cdrom_get_last_written(dev, &last)))
return ret;
IOCTL_OUT(arg, long, last);
@@ -2038,10 +2087,10 @@
track_information ti;
__u32 last_track;
int ret = -1;
-
+
if (!CDROM_CAN(CDC_GENERIC_PACKET))
goto use_toc;
-
+
if ((ret = cdrom_get_disc_info(dev, &di)))
goto use_toc;
@@ -2089,7 +2138,7 @@
track_information ti;
__u16 last_track;
int ret = -1;
-
+
if (!CDROM_CAN(CDC_GENERIC_PACKET))
goto use_last_written;
@@ -2125,6 +2174,99 @@
}
}
+/* return the buffer size of writeable drives */
+static int cdrom_read_buffer_capacity(struct cdrom_device_info *cdi)
+{
+ struct cdrom_device_ops *cdo = cdi->ops;
+ struct cdrom_generic_command cgc;
+ struct {
+ unsigned int pad;
+ unsigned int buffer_size;
+ unsigned int buffer_free;
+ } buf;
+ int ret;
+
+ init_cdrom_command(&cgc, &buf, 12);
+ cgc.cmd[0] = 0x5c;
+ cgc.cmd[8] = 12;
+
+ if ((ret = cdo->generic_packet(cdi, &cgc)))
+ return ret;
+
+ return be32_to_cpu(buf.buffer_size);
+}
+
+/* return 0 if succesful and the disc can be considered writeable. */
+static int cdrom_setup_writemode(struct cdrom_device_info *cdi)
+{
+ struct cdrom_generic_command cgc;
+ write_param_page wp;
+ disc_information di;
+ track_information ti;
+ int ret, last_track;
+
+ memset(&di, 0, sizeof(disc_information));
+ memset(&ti, 0, sizeof(track_information));
+ memset(&wp, 0, sizeof(write_param_page));
+ memset(&cdi->write, 0, sizeof(struct cdrom_write_settings));
+
+ if ((ret = cdrom_get_disc_info(cdi->dev, &di)))
+ return ret;
+
+ last_track = (di.last_track_msb << 8) | di.last_track_lsb;
+ if ((ret = cdrom_get_track_info(cdi->dev, last_track, 1, &ti)))
+ return ret;
+
+ /* if the media is erasable, then it is either CD-RW or
+ * DVD-RW - use fixed packets for those. non-erasable media
+ * indicated CD-R or DVD-R media, use varible sized packets for
+ * those (where the packet size is a bit less than the buffer
+ * capacity of the drive. */
+ if (di.erasable) {
+ cdi->write.fpacket = 1;
+ /* FIXME: DVD-RW is 16, should get the packet size instead */
+ cdi->write.packet_size = 32;
+ } else {
+ int buf_size;
+ cdi->write.fpacket = 0;
+ buf_size = cdrom_read_buffer_capacity(cdi);
+ buf_size -= 100*1024;
+ cdi->write.packet_size = buf_size / CD_FRAMESIZE;
+ }
+
+ init_cdrom_command(&cgc, &wp, 0x3c);
+ if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_WRITE_PARMS_PAGE, 0)))
+ return ret;
+
+ /* sanity checks */
+ if ((ti.damage && !ti.nwa_v) || ti.blank) {
+ cdinfo(CD_WARNING, "can't write to this disc\n");
+ return 1;
+ }
+
+ /* NWA is only for CD-R and DVD-R. -RW media is randomly
+ * writeable once it has been formatted. */
+ cdi->write.nwa = ti.nwa_v ? be32_to_cpu(ti.next_writable) : 0;
+
+ wp.fp = cdi->write.fpacket ? 1 : 0;
+ wp.track_mode = 0;
+ wp.write_type = 0;
+ wp.data_block_type = 8;
+ wp.session_format = 0;
+ wp.multi_session = 3;
+ wp.audio_pause = cpu_to_be16(0x96);
+ wp.packet_size = cdi->write.fpacket ? cpu_to_be32(cdi->write.packet_size) : 0;
+ wp.track_mode = 5; /* should be ok with both CD and DVD */
+
+ if ((ret = cdrom_mode_select(cdi, &cgc)))
+ return ret;
+
+ cdinfo(CD_WARNING, "%s: writeable with %lu block %s packets\n",
+ cdi->name, cdi->write.packet_size,
+ cdi->write.fpacket ? "fixed" : "variable");
+ return 0;
+}
+
EXPORT_SYMBOL(cdrom_get_next_writable);
EXPORT_SYMBOL(cdrom_get_last_written);
EXPORT_SYMBOL(cdrom_count_tracks);
@@ -2384,22 +2526,22 @@
initialized = 1;
}
+#endif /* endif CONFIG_SYSCTL */
+
#ifdef MODULE
static void cdrom_sysctl_unregister(void)
{
+#ifdef CONFIG_SYSCTL
unregister_sysctl_table(cdrom_sysctl_header);
+#endif
}
-#endif /* endif MODULE */
-#endif /* endif CONFIG_SYSCTL */
-
-#ifdef MODULE
int init_module(void)
{
#ifdef CONFIG_SYSCTL
cdrom_sysctl_register();
-#endif /* CONFIG_SYSCTL */
+#endif
return 0;
}
@@ -2410,5 +2552,5 @@
cdrom_sysctl_unregister();
#endif /* CONFIG_SYSCTL */
}
-
#endif /* endif MODULE */
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)