summaryrefslogtreecommitdiffstats
path: root/drivers/cdrom/cdrom.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-01-27 01:05:20 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-01-27 01:05:20 +0000
commit546db14ee74118296f425f3b91634fb767d67290 (patch)
tree22b613a3da8d4bf663eec5e155af01b87fdf9094 /drivers/cdrom/cdrom.c
parent1e25e41c4f5474e14452094492dbc169b800e4c8 (diff)
Merge with Linux 2.3.23. The new bootmem stuff has broken various
platforms. At this time I've only verified that IP22 support compiles and IP27 actually works.
Diffstat (limited to 'drivers/cdrom/cdrom.c')
-rw-r--r--drivers/cdrom/cdrom.c210
1 files changed, 176 insertions, 34 deletions
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 7a46ed183..e57de2523 100644
--- a/drivers/cdrom/cdrom.c
+++ b/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 @@ int register_cdrom(struct cdrom_device_info *cdi)
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 @@ struct cdrom_device_info *cdrom_find_device (kdev_t dev)
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 @@ struct cdrom_device_info *cdrom_find_device (kdev_t dev)
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 @@ int open_for_data(struct cdrom_device_info * cdi)
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 @@ int cdrom_release(struct inode *ip, struct file *fp)
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 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
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 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
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 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
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 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
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 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
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 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
}
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 @@ int cdrom_get_last_written(kdev_t dev, long *last_written)
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 @@ int cdrom_get_next_writable(kdev_t dev, long *next_writable)
track_information ti;
__u16 last_track;
int ret = -1;
-
+
if (!CDROM_CAN(CDC_GENERIC_PACKET))
goto use_last_written;
@@ -2125,6 +2174,99 @@ use_last_written:
}
}
+/* 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 @@ static void cdrom_sysctl_register(void)
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 @@ void cleanup_module(void)
cdrom_sysctl_unregister();
#endif /* CONFIG_SYSCTL */
}
-
#endif /* endif MODULE */
+