-- Mt Rainier support
-- DVD-RAM write open fixes
+ Nov 5 2001, Aug 8 2002. Modified by Andy Polyakov
+ <appro@fy.chalmers.se> to support MMC-3 compliant DVD+RW units.
+
+ Modified by Nigel Kukard <nkukard@lbsd.net> - support DVD+RW
+ 2.4.x patch by Andy Polyakov <appro@fy.chalmers.se>
+
-------------------------------------------------------------------------*/
#define REVISION "Revision: 3.20"
static int check_media_type;
/* automatically restart mrw format */
static int mrw_format_restart = 1;
-MODULE_PARM(debug, "i");
-MODULE_PARM(autoclose, "i");
-MODULE_PARM(autoeject, "i");
-MODULE_PARM(lockdoor, "i");
-MODULE_PARM(check_media_type, "i");
-MODULE_PARM(mrw_format_restart, "i");
+module_param(debug, bool, 0);
+module_param(autoclose, bool, 0);
+module_param(autoeject, bool, 0);
+module_param(lockdoor, bool, 0);
+module_param(check_media_type, bool, 0);
+module_param(mrw_format_restart, bool, 0);
-static spinlock_t cdrom_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(cdrom_lock);
static const char *mrw_format_status[] = {
"not mrw",
#endif /* CONFIG_SYSCTL */
static struct cdrom_device_info *topCdromPtr;
+static int cdrom_dummy_generic_packet(struct cdrom_device_info *cdi,
+ struct packet_command *cgc)
+{
+ if (cgc->sense) {
+ cgc->sense->sense_key = 0x05;
+ cgc->sense->asc = 0x20;
+ cgc->sense->ascq = 0x00;
+ }
+
+ cgc->stat = -EIO;
+ return -EIO;
+}
+
/* This macro makes sure we don't have to check on cdrom_device_ops
* existence in the run-time routines below. Change_capability is a
* hack to have the capability flags defined const, while we can still
else
cdi->cdda_method = CDDA_OLD;
+ if (!cdo->generic_packet)
+ cdo->generic_packet = cdrom_dummy_generic_packet;
+
cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name);
spin_lock(&cdrom_lock);
cdi->next = topCdromPtr;
if (be16_to_cpu(eh->data_len) < sizeof(*med))
return 1;
+ if (eh->nea || eh->notification_class != 0x4)
+ return 1;
+
memcpy(med, &buffer[sizeof(*eh)], sizeof(*med));
return 0;
}
* the first prototypes used 0x2c as the page code for the mrw mode page,
* subsequently this was changed to 0x03. probe the one used by this drive
*/
-int cdrom_mrw_probe_pc(struct cdrom_device_info *cdi)
+static int cdrom_mrw_probe_pc(struct cdrom_device_info *cdi)
{
struct packet_command cgc;
char buffer[16];
return 1;
}
-int cdrom_is_mrw(struct cdrom_device_info *cdi, int *write)
+static int cdrom_is_mrw(struct cdrom_device_info *cdi, int *write)
{
struct packet_command cgc;
struct mrw_feature_desc *mfd;
return ret;
mfd = (struct mrw_feature_desc *)&buffer[sizeof(struct feature_header)];
+ if (be16_to_cpu(mfd->feature_code) != CDF_MRW)
+ return 1;
*write = mfd->write;
if ((ret = cdrom_mrw_probe_pc(cdi))) {
static int cdrom_mrw_exit(struct cdrom_device_info *cdi)
{
disc_information di;
- int ret = 0;
+ int ret;
- if (cdrom_get_disc_info(cdi, &di))
+ ret = cdrom_get_disc_info(cdi, &di);
+ if (ret < 0 || ret < (int)offsetof(typeof(di),disc_type))
return 1;
+ ret = 0;
if (di.mrw_status == CDM_MRW_BGFORMAT_ACTIVE) {
- printk(KERN_INFO "cdrom: issuing MRW back ground format suspend\n");
+ printk(KERN_INFO "cdrom: issuing MRW back ground "
+ "format suspend\n");
ret = cdrom_mrw_bgformat_susp(cdi, 0);
}
return 0;
}
-int cdrom_get_random_writable(struct cdrom_device_info *cdi,
+static int cdrom_get_random_writable(struct cdrom_device_info *cdi,
struct rwrt_feature_desc *rfd)
{
struct packet_command cgc;
return 0;
}
-int cdrom_has_defect_mgt(struct cdrom_device_info *cdi)
+static int cdrom_has_defect_mgt(struct cdrom_device_info *cdi)
{
struct packet_command cgc;
char buffer[16];
}
-int cdrom_is_random_writable(struct cdrom_device_info *cdi, int *write)
+static int cdrom_is_random_writable(struct cdrom_device_info *cdi, int *write)
{
struct rwrt_feature_desc rfd;
int ret;
static int cdrom_media_erasable(struct cdrom_device_info *cdi)
{
disc_information di;
+ int ret;
- if (cdrom_get_disc_info(cdi, &di))
+ ret = cdrom_get_disc_info(cdi, &di);
+ if (ret < 0 || ret < offsetof(typeof(di), n_first_track))
return -1;
return di.erasable;
return 1;
}
- if (cdrom_get_disc_info(cdi, &di))
+ ret = cdrom_get_disc_info(cdi, &di);
+ if (ret < 0 || ret < offsetof(typeof(di),disc_type))
return 1;
if (!di.erasable)
* 3 - MRW formatting complete
*/
ret = 0;
- printk(KERN_INFO "cdrom open: mrw_status '%s'\n", mrw_format_status[di.mrw_status]);
+ printk(KERN_INFO "cdrom open: mrw_status '%s'\n",
+ mrw_format_status[di.mrw_status]);
if (!di.mrw_status)
ret = 1;
- else if (di.mrw_status == CDM_MRW_BGFORMAT_INACTIVE && mrw_format_restart)
+ else if (di.mrw_status == CDM_MRW_BGFORMAT_INACTIVE &&
+ mrw_format_restart)
ret = cdrom_mrw_bgformat(cdi, 1);
return ret;
return ret;
}
+static void cdrom_mmc3_profile(struct cdrom_device_info *cdi)
+{
+ struct packet_command cgc;
+ char buffer[32];
+ int ret, mmc3_profile;
+
+ init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
+
+ cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
+ cgc.cmd[1] = 0;
+ cgc.cmd[2] = cgc.cmd[3] = 0; /* Starting Feature Number */
+ cgc.cmd[8] = sizeof(buffer); /* Allocation Length */
+ cgc.quiet = 1;
+
+ if ((ret = cdi->ops->generic_packet(cdi, &cgc)))
+ mmc3_profile = 0xffff;
+ else
+ mmc3_profile = (buffer[6] << 8) | buffer[7];
+
+ cdi->mmc3_profile = mmc3_profile;
+}
+
+static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi)
+{
+ switch (cdi->mmc3_profile) {
+ case 0x12: /* DVD-RAM */
+ case 0x1A: /* DVD+RW */
+ return 0;
+ default:
+ return 1;
+ }
+}
+
/*
* returns 0 for ok to open write, non-0 to disallow
*/
ret = cdrom_ram_open_write(cdi);
else if (CDROM_CAN(CDC_MO_DRIVE))
ret = mo_open_write(cdi);
+ else if (!cdrom_is_dvd_rw(cdi))
+ ret = 0;
return ret;
}
+static void cdrom_dvd_rw_close_write(struct cdrom_device_info *cdi)
+{
+ struct packet_command cgc;
+
+ if (cdi->mmc3_profile != 0x1a) {
+ cdinfo(CD_CLOSE, "%s: No DVD+RW\n", cdi->name);
+ return;
+ }
+
+ if (!cdi->media_written) {
+ cdinfo(CD_CLOSE, "%s: DVD+RW media clean\n", cdi->name);
+ return;
+ }
+
+ printk(KERN_INFO "cdrom: %s: dirty DVD+RW media, \"finalizing\"\n",
+ cdi->name);
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.cmd[0] = GPCMD_FLUSH_CACHE;
+ cgc.timeout = 30*HZ;
+ cdi->ops->generic_packet(cdi, &cgc);
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.cmd[0] = GPCMD_CLOSE_TRACK;
+ cgc.timeout = 3000*HZ;
+ cgc.quiet = 1;
+ cdi->ops->generic_packet(cdi, &cgc);
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.cmd[0] = GPCMD_CLOSE_TRACK;
+ cgc.cmd[2] = 2; /* Close session */
+ cgc.quiet = 1;
+ cgc.timeout = 3000*HZ;
+ cdi->ops->generic_packet(cdi, &cgc);
+
+ cdi->media_written = 0;
+}
+
static int cdrom_close_write(struct cdrom_device_info *cdi)
{
#if 0
ret = open_for_data(cdi);
if (ret)
goto err;
+ cdrom_mmc3_profile(cdi);
if (fp->f_mode & FMODE_WRITE) {
ret = -EROFS;
if (cdrom_open_write(cdi))
if (!CDROM_CAN(CDC_RAM))
goto err;
ret = 0;
+ cdi->media_written = 0;
}
}
}
cdinfo(CD_OPEN, "the tray is now closed.\n");
}
+ /* the door should be closed now, check for the disc */
+ ret = cdo->drive_status(cdi, CDSL_CURRENT);
if (ret!=CDS_DISC_OK) {
ret = -ENOMEDIUM;
goto clean_up_and_return;
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)
+ cdrom_dvd_rw_close_write(cdi);
if (cdi->use_count == 0 &&
(cdo->capability & CDC_LOCK) && !keeplocked) {
cdinfo(CD_CLOSE, "Unlocking door!\n");
if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
cdi->mc_flags = 0x3; /* set bit on both queues */
ret |= 1;
+ cdi->media_written = 0;
}
cdi->mc_flags &= ~mask; /* clear bit */
return ret;
s->manufact.len = buf[0] << 8 | buf[1];
if (s->manufact.len < 0 || s->manufact.len > 2048) {
cdinfo(CD_WARNING, "Received invalid manufacture info length"
- " (%d)\n", s->bca.len);
+ " (%d)\n", s->manufact.len);
ret = -EIO;
} else {
memcpy(s->manufact.value, &buf[4], s->manufact.len);
int lba, int nframes)
{
struct packet_command cgc;
- int nr, ret;
+ int ret = 0;
+ int nr;
cdi->last_sense = 0;
return -ENOMEM;
if (!access_ok(VERIFY_WRITE, ubuf, nframes * CD_FRAMESIZE_RAW)) {
- kfree(cgc.buffer);
- return -EFAULT;
+ ret = -EFAULT;
+ goto out;
}
cgc.data_direction = CGC_DATA_READ;
ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW);
if (ret)
break;
- __copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr);
+ if (__copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) {
+ ret = -EFAULT;
+ break;
+ }
ubuf += CD_FRAMESIZE_RAW * nr;
nframes -= nr;
lba += nr;
}
+out:
kfree(cgc.buffer);
- return 0;
+ return ret;
}
static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf,
struct cdrom_device_ops *cdo = cdi->ops;
struct packet_command cgc;
struct request_sense sense;
- char buffer[32];
+ unsigned char buffer[32];
int ret = 0;
memset(&cgc, 0, sizeof(cgc));
case CDROMVOLCTRL:
case CDROMVOLREAD: {
struct cdrom_volctrl volctrl;
- char mask[32];
+ char mask[sizeof(buffer)];
unsigned short offset;
+
cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n");
IOCTL_IN(arg, struct cdrom_volctrl, volctrl);
if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_AUDIO_CTL_PAGE, 0)))
return ret;
- /* some drives have longer pages, adjust and reread. */
- if (buffer[1] > cgc.buflen) {
- cgc.buflen = buffer[1] + 2;
- if ((ret = cdrom_mode_sense(cdi, &cgc,
- GPMODE_AUDIO_CTL_PAGE, 0)))
- return ret;
+ /* originally the code depended on buffer[1] to determine
+ how much data is available for transfer. buffer[1] is
+ unfortunately ambigious and the only reliable way seem
+ to be to simply skip over the block descriptor... */
+ offset = 8 + be16_to_cpu(*(unsigned short *)(buffer+6));
+
+ if (offset + 16 > sizeof(buffer))
+ return -E2BIG;
+
+ if (offset + 16 > cgc.buflen) {
+ cgc.buflen = offset+16;
+ ret = cdrom_mode_sense(cdi, &cgc,
+ GPMODE_AUDIO_CTL_PAGE, 0);
+ if (ret)
+ return ret;
}
-
- /* get the offset from the length of the page. length
- is measure from byte 2 an on, thus the 14. */
- offset = buffer[1] - 14;
+
+ /* sanity check */
+ if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE ||
+ buffer[offset+1] < 14)
+ return -EINVAL;
/* now we have the current volume settings. if it was only
a CDROMVOLREAD, return these values */
buffer[offset+15] = volctrl.channel3 & mask[offset+15];
/* set volume */
- cgc.buffer = buffer;
+ cgc.buffer = buffer + offset - 8;
+ memset(cgc.buffer, 0, 8);
return cdrom_mode_select(cdi, &cgc);
}
{
struct cdrom_device_ops *cdo = cdi->ops;
struct packet_command cgc;
- int ret;
+ int ret, buflen;
init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
- cgc.buflen = be16_to_cpu(ti->track_information_length) +
+ buflen = be16_to_cpu(ti->track_information_length) +
sizeof(ti->track_information_length);
- if (cgc.buflen > sizeof(track_information))
- cgc.buflen = sizeof(track_information);
+ if (buflen > sizeof(track_information))
+ buflen = sizeof(track_information);
- cgc.cmd[8] = cgc.buflen;
- return cdo->generic_packet(cdi, &cgc);
+ cgc.cmd[8] = cgc.buflen = buflen;
+ if ((ret = cdo->generic_packet(cdi, &cgc)))
+ return ret;
+
+ /* return actual fill size */
+ return buflen;
}
/* requires CD R/RW */
{
struct cdrom_device_ops *cdo = cdi->ops;
struct packet_command cgc;
- int ret;
+ int ret, buflen;
/* set up command and get the disc info */
init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);
/* not all drives have the same disc_info length, so requeue
* packet with the length the drive tells us it can supply
*/
- cgc.buflen = be16_to_cpu(di->disc_information_length) +
+ buflen = be16_to_cpu(di->disc_information_length) +
sizeof(di->disc_information_length);
- if (cgc.buflen > sizeof(disc_information))
- cgc.buflen = sizeof(disc_information);
+ if (buflen > sizeof(disc_information))
+ buflen = sizeof(disc_information);
- cgc.cmd[8] = cgc.buflen;
- return cdo->generic_packet(cdi, &cgc);
+ cgc.cmd[8] = cgc.buflen = buflen;
+ if ((ret = cdo->generic_packet(cdi, &cgc)))
+ return ret;
+
+ /* return actual fill size */
+ return buflen;
}
/* return the last written block on the CD-R media. this is for the udf
disc_information di;
track_information ti;
__u32 last_track;
- int ret = -1;
+ int ret = -1, ti_size;
if (!CDROM_CAN(CDC_GENERIC_PACKET))
goto use_toc;
- if ((ret = cdrom_get_disc_info(cdi, &di)))
+ ret = cdrom_get_disc_info(cdi, &di);
+ if (ret < (int)(offsetof(typeof(di), last_track_lsb)
+ + sizeof(di.last_track_lsb)))
goto use_toc;
+ /* if unit didn't return msb, it's zeroed by cdrom_get_disc_info */
last_track = (di.last_track_msb << 8) | di.last_track_lsb;
- if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti)))
+ ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
+ if (ti_size < (int)offsetof(typeof(ti), track_start))
goto use_toc;
/* if this track is blank, try the previous. */
if (ti.blank) {
- last_track--;
- if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti)))
+ if (last_track==1)
goto use_toc;
+ last_track--;
+ ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
}
+ if (ti_size < (int)(offsetof(typeof(ti), track_size)
+ + sizeof(ti.track_size)))
+ goto use_toc;
+
/* if last recorded field is valid, return it. */
- if (ti.lra_v) {
+ if (ti.lra_v && ti_size >= (int)(offsetof(typeof(ti), last_rec_address)
+ + sizeof(ti.last_rec_address))) {
*last_written = be32_to_cpu(ti.last_rec_address);
} else {
/* make it up instead */
/* this is where we end up if the drive either can't do a
GPCMD_READ_DISC_INFO or GPCMD_READ_TRACK_RZONE_INFO or if
- it fails. then we return the toc contents. */
+ it doesn't give enough information or fails. then we return
+ the toc contents. */
use_toc:
toc.cdte_format = CDROM_MSF;
toc.cdte_track = CDROM_LEADOUT;
- if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc))
+ if ((ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc)))
return ret;
sanitize_format(&toc.cdte_addr, &toc.cdte_format, CDROM_LBA);
*last_written = toc.cdte_addr.lba;
disc_information di;
track_information ti;
__u16 last_track;
- int ret = -1;
+ int ret, ti_size;
if (!CDROM_CAN(CDC_GENERIC_PACKET))
goto use_last_written;
- if ((ret = cdrom_get_disc_info(cdi, &di)))
+ ret = cdrom_get_disc_info(cdi, &di);
+ if (ret < 0 || ret < offsetof(typeof(di), last_track_lsb)
+ + sizeof(di.last_track_lsb))
goto use_last_written;
+ /* if unit didn't return msb, it's zeroed by cdrom_get_disc_info */
last_track = (di.last_track_msb << 8) | di.last_track_lsb;
- if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti)))
+ ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
+ if (ti_size < 0 || ti_size < offsetof(typeof(ti), track_start))
goto use_last_written;
/* if this track is blank, try the previous. */
if (ti.blank) {
+ if (last_track == 1)
+ goto use_last_written;
last_track--;
- if ((ret = cdrom_get_track_info(cdi, last_track, 1, &ti)))
+ ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
+ if (ti_size < 0)
goto use_last_written;
}
/* if next recordable address field is valid, use it. */
- if (ti.nwa_v)
+ if (ti.nwa_v && ti_size >= offsetof(typeof(ti), next_writable)
+ + sizeof(ti.next_writable)) {
*next_writable = be32_to_cpu(ti.next_writable);
- else
- goto use_last_written;
-
- return 0;
+ return 0;
+ }
use_last_written:
if ((ret = cdrom_get_last_written(cdi, next_writable))) {
EXPORT_SYMBOL(cdrom_mode_sense);
EXPORT_SYMBOL(init_cdrom_command);
EXPORT_SYMBOL(cdrom_get_media_event);
-EXPORT_SYMBOL(cdrom_is_mrw);
-EXPORT_SYMBOL(cdrom_is_random_writable);
#ifdef CONFIG_SYSCTL
#define CDROM_STR_SIZE 1000
-struct cdrom_sysctl_settings {
+static struct cdrom_sysctl_settings {
char info[CDROM_STR_SIZE]; /* general info */
int autoclose; /* close tray upon mount, etc */
int autoeject; /* eject on umount */
int check; /* check media type */
} cdrom_sysctl_settings;
-int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
+static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
int pos;
procfs/sysctl yet. When they are, this will naturally disappear. For now
just update all drives. Later this will become the template on which
new registered drives will be based. */
-void cdrom_update_settings(void)
+static void cdrom_update_settings(void)
{
struct cdrom_device_info *cdi;
}
/* Place files in /proc/sys/dev/cdrom */
-ctl_table cdrom_table[] = {
+static ctl_table cdrom_table[] = {
{
.ctl_name = DEV_CDROM_INFO,
.procname = "info",
{ .ctl_name = 0 }
};
-ctl_table cdrom_cdrom_table[] = {
+static ctl_table cdrom_cdrom_table[] = {
{
.ctl_name = DEV_CDROM,
.procname = "cdrom",
};
/* Make sure that /proc/sys/dev is there */
-ctl_table cdrom_root_table[] = {
-#ifdef CONFIG_PROC_FS
+static ctl_table cdrom_root_table[] = {
{
.ctl_name = CTL_DEV,
.procname = "dev",
.mode = 0555,
.child = cdrom_cdrom_table,
},
-#endif /* CONFIG_PROC_FS */
{ .ctl_name = 0 }
};
static struct ctl_table_header *cdrom_sysctl_header;