-- 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",
/* These are used to simplify getting data in from and back to user land */
#define IOCTL_IN(arg, type, in) \
- if (copy_from_user(&(in), (type *) (arg), sizeof (in))) \
+ if (copy_from_user(&(in), (type __user *) (arg), sizeof (in))) \
return -EFAULT;
#define IOCTL_OUT(arg, type, out) \
- if (copy_to_user((type *) (arg), &(out), sizeof (out))) \
+ if (copy_to_user((type __user *) (arg), &(out), sizeof (out))) \
return -EFAULT;
/* The (cdo->capability & ~cdi->mask & CDC_XXX) construct was used in
#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;
int cdrom_get_media_event(struct cdrom_device_info *cdi,
struct media_event_desc *med)
{
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
unsigned char buffer[8];
struct event_header *eh = (struct event_header *) buffer;
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 cdrom_generic_command cgc;
+ struct packet_command cgc;
char buffer[16];
init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
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 cdrom_generic_command cgc;
+ struct packet_command cgc;
struct mrw_feature_desc *mfd;
unsigned char buffer[16];
int ret;
+ *write = 0;
+
init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
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)))
+ if ((ret = cdrom_mrw_probe_pc(cdi))) {
+ *write = 0;
return ret;
+ }
return 0;
}
static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont)
{
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
unsigned char buffer[12];
int ret;
static int cdrom_mrw_bgformat_susp(struct cdrom_device_info *cdi, int immed)
{
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
cgc.cmd[0] = GPCMD_CLOSE_TRACK;
static int cdrom_flush_cache(struct cdrom_device_info *cdi)
{
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
cgc.cmd[0] = GPCMD_FLUSH_CACHE;
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);
}
static int cdrom_mrw_set_lba_space(struct cdrom_device_info *cdi, int space)
{
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
struct mode_page_header *mph;
char buffer[16];
int ret, offset, size;
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 cdrom_generic_command cgc;
+ struct packet_command cgc;
char buffer[24];
- struct feature_header *fh;
int ret;
init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
if ((ret = cdi->ops->generic_packet(cdi, &cgc)))
return ret;
- fh = (struct feature_header *)&buffer[0];
- if (be32_to_cpu(fh->data_len) >= (sizeof(struct feature_header)+
- sizeof(struct rwrt_feature_desc)))
- memcpy(rfd, &buffer[sizeof(struct feature_header)],
- sizeof (*rfd));
- else
- memset(rfd, 0, sizeof(*rfd));
-
+ memcpy(rfd, &buffer[sizeof(struct feature_header)], sizeof (*rfd));
return 0;
}
-int cdrom_has_defect_mgt(struct cdrom_device_info *cdi)
+static int cdrom_has_defect_mgt(struct cdrom_device_info *cdi)
{
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
char buffer[16];
- struct feature_header *fh;
__u16 *feature_code;
int ret;
init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_GET_CONFIGURATION; /* often 0x46 */
- cgc.cmd[3] = CDF_HWDM; /* often 0x0024 */
- cgc.cmd[8] = sizeof(buffer); /* often 0x10 */
+ cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
+ cgc.cmd[3] = CDF_HWDM;
+ cgc.cmd[8] = sizeof(buffer);
cgc.quiet = 1;
if ((ret = cdi->ops->generic_packet(cdi, &cgc)))
return ret;
- fh = (struct feature_header *)&buffer[0];
- ret = 1;
- if (be32_to_cpu(fh->data_len) >= (sizeof(struct feature_header)+8)) {
- feature_code = (__u16 *)&buffer[sizeof(struct feature_header)];
- if (CDF_HWDM == be16_to_cpu(*feature_code))
- ret = 0;
- }
- return ret;
+ feature_code = (__u16 *) &buffer[sizeof(struct feature_header)];
+ if (be16_to_cpu(*feature_code) == CDF_HWDM)
+ return 0;
+
+ return 1;
}
-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;
static int mo_open_write(struct cdrom_device_info *cdi)
{
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
char buffer[255];
int 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
*/
static int cdrom_open_write(struct cdrom_device_info *cdi)
{
+ int mrw, mrw_write, ram_write;
int ret = 1;
+ mrw = 0;
+ if (!cdrom_is_mrw(cdi, &mrw_write))
+ mrw = 1;
+
+ if (CDROM_CAN(CDC_MO_DRIVE))
+ ram_write = 1;
+ else
+ (void) cdrom_is_random_writable(cdi, &ram_write);
+
+ if (mrw)
+ cdi->mask &= ~CDC_MRW;
+ else
+ cdi->mask |= CDC_MRW;
+
+ if (mrw_write)
+ cdi->mask &= ~CDC_MRW_W;
+ else
+ cdi->mask |= CDC_MRW_W;
+
+ if (ram_write)
+ cdi->mask &= ~CDC_RAM;
+ else
+ cdi->mask |= CDC_RAM;
+
if (CDROM_CAN(CDC_MRW_W))
ret = cdrom_mrw_open_write(cdi);
else if (CDROM_CAN(CDC_DVD_RAM))
ret = cdrom_dvdram_open_write(cdi);
else if (CDROM_CAN(CDC_RAM) &&
- !CDROM_CAN(CDC_CD_R|CDC_CD_RW|CDC_DVD|CDC_DVD_R|CDC_MRW))
+ !CDROM_CAN(CDC_CD_R|CDC_CD_RW|CDC_DVD|CDC_DVD_R|CDC_MRW|CDC_MO_DRIVE))
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
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)
+ goto err;
+ cdrom_mmc3_profile(cdi);
if (fp->f_mode & FMODE_WRITE) {
ret = -EROFS;
- if (!CDROM_CAN(CDC_RAM))
- goto err;
if (cdrom_open_write(cdi))
goto err;
+ if (!CDROM_CAN(CDC_RAM))
+ goto err;
+ ret = 0;
+ cdi->media_written = 0;
}
- ret = open_for_data(cdi);
}
if (ret)
}
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");
static int cdrom_read_mech_status(struct cdrom_device_info *cdi,
struct cdrom_changer_info *buf)
{
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
int length;
/* If SLOT < 0, unload the current slot. Otherwise, try to load SLOT. */
static int cdrom_load_unload(struct cdrom_device_info *cdi, int slot)
{
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
cdinfo(CD_CHANGER, "entering cdrom_load_unload()\n");
if (cdi->sanyo_slot && slot < 0)
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;
*curr = requested;
}
-void init_cdrom_command(struct cdrom_generic_command *cgc, void *buf, int len,
+void init_cdrom_command(struct packet_command *cgc, void *buf, int len,
int type)
{
- memset(cgc, 0, sizeof(struct cdrom_generic_command));
+ memset(cgc, 0, sizeof(struct packet_command));
if (buf)
memset(buf, 0, len);
cgc->buffer = (char *) buf;
#define copy_key(dest,src) memcpy((dest), (src), sizeof(dvd_key))
#define copy_chal(dest,src) memcpy((dest), (src), sizeof(dvd_challenge))
-static void setup_report_key(struct cdrom_generic_command *cgc, unsigned agid, unsigned type)
+static void setup_report_key(struct packet_command *cgc, unsigned agid, unsigned type)
{
cgc->cmd[0] = GPCMD_REPORT_KEY;
cgc->cmd[10] = type | (agid << 6);
cgc->data_direction = CGC_DATA_READ;
}
-static void setup_send_key(struct cdrom_generic_command *cgc, unsigned agid, unsigned type)
+static void setup_send_key(struct packet_command *cgc, unsigned agid, unsigned type)
{
cgc->cmd[0] = GPCMD_SEND_KEY;
cgc->cmd[10] = type | (agid << 6);
{
int ret;
u_char buf[20];
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
rpc_state_t rpc_state;
/* LU data send */
case DVD_LU_SEND_AGID:
cdinfo(CD_DVD, "entering DVD_LU_SEND_AGID\n");
+ cgc.quiet = 1;
setup_report_key(&cgc, ai->lsa.agid, 0);
if ((ret = cdo->generic_packet(cdi, &cgc)))
/* Post-auth key */
case DVD_LU_SEND_TITLE_KEY:
cdinfo(CD_DVD, "entering DVD_LU_SEND_TITLE_KEY\n");
+ cgc.quiet = 1;
setup_report_key(&cgc, ai->lstk.agid, 4);
cgc.cmd[5] = ai->lstk.lba;
cgc.cmd[4] = ai->lstk.lba >> 8;
{
unsigned char buf[21], *base;
struct dvd_layer *layer;
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
int ret, layer_num = s->physical.layer_num;
{
int ret;
u_char buf[8];
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
{
int ret, size;
u_char *buf;
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
size = sizeof(s->disckey.value) + 4;
{
int ret;
u_char buf[4 + 188];
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
{
int ret = 0, size;
u_char *buf;
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
size = sizeof(s->manufact.value) + 4;
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 cdrom_mode_sense(struct cdrom_device_info *cdi,
- struct cdrom_generic_command *cgc,
+ struct packet_command *cgc,
int page_code, int page_control)
{
struct cdrom_device_ops *cdo = cdi->ops;
}
int cdrom_mode_select(struct cdrom_device_info *cdi,
- struct cdrom_generic_command *cgc)
+ struct packet_command *cgc)
{
struct cdrom_device_ops *cdo = cdi->ops;
struct cdrom_subchnl *subchnl, int mcn)
{
struct cdrom_device_ops *cdo = cdi->ops;
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
char buffer[32];
int ret;
* Specific READ_10 interface
*/
static int cdrom_read_cd(struct cdrom_device_info *cdi,
- struct cdrom_generic_command *cgc, int lba,
+ struct packet_command *cgc, int lba,
int blocksize, int nblocks)
{
struct cdrom_device_ops *cdo = cdi->ops;
/* very generic interface for reading the various types of blocks */
static int cdrom_read_block(struct cdrom_device_info *cdi,
- struct cdrom_generic_command *cgc,
+ struct packet_command *cgc,
int lba, int nblocks, int format, int blksize)
{
struct cdrom_device_ops *cdo = cdi->ops;
static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf,
int lba, int nframes)
{
- struct cdrom_generic_command cgc;
- int nr, ret;
+ struct packet_command cgc;
+ int ret = 0;
+ int nr;
+
+ cdi->last_sense = 0;
memset(&cgc, 0, sizeof(cgc));
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,
if (!q)
return -ENXIO;
+ cdi->last_sense = 0;
+
while (nframes) {
nr = nframes;
if (cdi->cdda_method == CDDA_BPC_SINGLE)
rq->timeout = 60 * HZ;
bio = rq->bio;
+ if (rq->bio)
+ blk_queue_bounce(q, &rq->bio);
+
if (blk_execute_rq(q, cdi->disk, rq)) {
struct request_sense *s = rq->sense;
ret = -EIO;
cdi->last_sense = s->sense_key;
}
- if (blk_rq_unmap_user(rq, ubuf, bio, len))
+ if (blk_rq_unmap_user(rq, bio, len))
ret = -EFAULT;
if (ret)
nframes -= nr;
lba += nr;
+ ubuf += len;
}
return ret;
* these days. ATAPI / SCSI specific code now mainly resides in
* mmc_ioct().
*/
-int cdrom_ioctl(struct cdrom_device_info *cdi, struct inode *ip,
- unsigned int cmd, unsigned long arg)
+int cdrom_ioctl(struct file * file, struct cdrom_device_info *cdi,
+ struct inode *ip, unsigned int cmd, unsigned long arg)
{
struct cdrom_device_ops *cdo = cdi->ops;
int ret;
/* Try the generic SCSI command ioctl's first.. */
- ret = scsi_cmd_ioctl(ip->i_bdev->bd_disk, cmd, arg);
+ ret = scsi_cmd_ioctl(file, ip->i_bdev->bd_disk, cmd, (void __user *)arg);
if (ret != -ENOTTY)
return ret;
static int cdrom_switch_blocksize(struct cdrom_device_info *cdi, int size)
{
struct cdrom_device_ops *cdo = cdi->ops;
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
struct modesel_head mh;
memset(&mh, 0, sizeof(mh));
unsigned long arg)
{
struct cdrom_device_ops *cdo = cdi->ops;
- struct cdrom_generic_command cgc;
+ struct packet_command cgc;
struct request_sense sense;
- char buffer[32];
+ unsigned char buffer[32];
int ret = 0;
memset(&cgc, 0, sizeof(cgc));
ret = cdrom_read_cd(cdi, &cgc, lba, blocksize, 1);
ret |= cdrom_switch_blocksize(cdi, blocksize);
}
- if (!ret && copy_to_user((char *)arg, cgc.buffer, blocksize))
+ if (!ret && copy_to_user((char __user *)arg, cgc.buffer, blocksize))
ret = -EFAULT;
kfree(cgc.buffer);
return ret;
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);
}
if ((s = (dvd_struct *) kmalloc(size, GFP_KERNEL)) == NULL)
return -ENOMEM;
cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n");
- if (copy_from_user(s, (dvd_struct *)arg, size)) {
+ if (copy_from_user(s, (dvd_struct __user *)arg, size)) {
kfree(s);
return -EFAULT;
}
kfree(s);
return ret;
}
- if (copy_to_user((dvd_struct *)arg, s, size))
+ if (copy_to_user((dvd_struct __user *)arg, s, size))
ret = -EFAULT;
kfree(s);
return ret;
track_information *ti)
{
struct cdrom_device_ops *cdo = cdi->ops;
- struct cdrom_generic_command cgc;
- int ret;
+ struct packet_command cgc;
+ 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 */
static int cdrom_get_disc_info(struct cdrom_device_info *cdi, disc_information *di)
{
struct cdrom_device_ops *cdo = cdi->ops;
- struct cdrom_generic_command cgc;
- int ret;
+ struct packet_command cgc;
+ 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,
- void *buffer, size_t *lenp)
+static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
int pos;
struct cdrom_device_info *cdi;
char *info = cdrom_sysctl_settings.info;
- if (!*lenp || (filp->f_pos && !write)) {
+ if (!*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
strcpy(info+pos,"\n\n");
- return proc_dostring(ctl, write, filp, buffer, lenp);
+ return proc_dostring(ctl, write, filp, buffer, lenp, ppos);
}
/* Unfortunately, per device settings are not implemented through
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;
}
static int cdrom_sysctl_handler(ctl_table *ctl, int write, struct file * filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
int *valp = ctl->data;
int val = *valp;
int ret;
- ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+ ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
if (write && *valp != val) {
}
/* 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;