X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fcdrom%2Fcdrom.c;h=3105dddf59f19a9409807370ebfce41b7a9167f1;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=1f65c1e72ec1d0fed874c50fac5da137994ee727;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 1f65c1e72..3105dddf5 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -1,4 +1,4 @@ -/* linux/drivers/cdrom/cdrom.c. +/* linux/drivers/cdrom/cdrom.c Copyright (c) 1996, 1997 David A. van Leeuwen. Copyright (c) 1997, 1998 Erik Andersen Copyright (c) 1998, 1999 Jens Axboe @@ -234,6 +234,12 @@ -- Mt Rainier support -- DVD-RAM write open fixes + Nov 5 2001, Aug 8 2002. Modified by Andy Polyakov + to support MMC-3 compliant DVD+RW units. + + Modified by Nigel Kukard - support DVD+RW + 2.4.x patch by Andy Polyakov + -------------------------------------------------------------------------*/ #define REVISION "Revision: 3.20" @@ -257,7 +263,6 @@ /* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */ /* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE|CD_COUNT_TRACKS) */ -#include #include #include #include @@ -290,14 +295,14 @@ static int lockdoor = 1; 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", @@ -318,11 +323,11 @@ static const char *mrw_address_space[] = { "DMA", "GAA" }; /* 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 @@ -332,6 +337,12 @@ static const char *mrw_address_space[] = { "DMA", "GAA" }; /* used in the audio ioctls */ #define CHECKAUDIO if ((ret=check_for_audio_disc(cdi, cdo))) return ret +/* + * Another popular OS uses 7 seconds as the hard timeout for default + * commands, so it is a good choice for us as well. + */ +#define CDROM_DEF_TIMEOUT (7 * HZ) + /* Not-exported routines. */ static int open_for_data(struct cdrom_device_info * cdi); static int check_for_audio_disc(struct cdrom_device_info * cdi, @@ -354,6 +365,19 @@ static void cdrom_sysctl_register(void); #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 @@ -388,7 +412,6 @@ int register_cdrom(struct cdrom_device_info *cdi) ENSURE(get_mcn, CDC_MCN); ENSURE(reset, CDC_RESET); ENSURE(audio_ioctl, CDC_PLAY_AUDIO); - ENSURE(dev_ioctl, CDC_IOCTLS); ENSURE(generic_packet, CDC_GENERIC_PACKET); cdi->mc_flags = 0; cdo->n_minors = 0; @@ -411,6 +434,9 @@ int register_cdrom(struct cdrom_device_info *cdi) 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; @@ -455,7 +481,7 @@ int unregister_cdrom(struct cdrom_device_info *unreg) 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; @@ -472,6 +498,9 @@ int cdrom_get_media_event(struct cdrom_device_info *cdi, 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; } @@ -480,9 +509,9 @@ int cdrom_get_media_event(struct cdrom_device_info *cdi, * 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); @@ -501,13 +530,15 @@ int cdrom_mrw_probe_pc(struct cdrom_device_info *cdi) 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; @@ -519,17 +550,21 @@ int cdrom_is_mrw(struct cdrom_device_info *cdi, int *write) 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; @@ -570,7 +605,7 @@ static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont) 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; @@ -588,7 +623,7 @@ static int cdrom_mrw_bgformat_susp(struct cdrom_device_info *cdi, int immed) 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; @@ -601,17 +636,20 @@ static int cdrom_flush_cache(struct cdrom_device_info *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); } - if (!ret) + if (!ret && cdi->media_written) ret = cdrom_flush_cache(cdi); return ret; @@ -619,7 +657,7 @@ static int cdrom_mrw_exit(struct cdrom_device_info *cdi) 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; @@ -646,12 +684,11 @@ static int cdrom_mrw_set_lba_space(struct cdrom_device_info *cdi, int space) 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); @@ -664,47 +701,36 @@ int cdrom_get_random_writable(struct cdrom_device_info *cdi, 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; + __be16 *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 = (__be16 *) &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; @@ -723,8 +749,10 @@ int cdrom_is_random_writable(struct cdrom_device_info *cdi, int *write) 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; @@ -760,7 +788,8 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi) 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) @@ -774,10 +803,12 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi) * 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; @@ -785,7 +816,7 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi) static int mo_open_write(struct cdrom_device_info *cdi) { - struct cdrom_generic_command cgc; + struct packet_command cgc; char buffer[255]; int ret; @@ -829,26 +860,124 @@ static int cdrom_ram_open_write(struct cdrom_device_info *cdi) 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 @@ -878,14 +1007,19 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp) 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; + goto err_release; + if (!CDROM_CAN(CDC_RAM)) + goto err_release; + ret = 0; + cdi->media_written = 0; } - ret = open_for_data(cdi); } if (ret) @@ -897,6 +1031,8 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp) not be mounting, but opening with O_NONBLOCK */ check_disk_change(ip->i_bdev); return 0; +err_release: + cdi->ops->release(cdi); err: cdi->use_count--; return ret; @@ -946,6 +1082,8 @@ int open_for_data(struct cdrom_device_info * cdi) } 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; @@ -999,7 +1137,7 @@ int open_for_data(struct cdrom_device_info * cdi) This ensures that the drive gets unlocked after a mount fails. This is a goto to avoid bloating the driver with redundant code. */ clean_up_and_return: - cdinfo(CD_WARNING, "open failed.\n"); + cdinfo(CD_OPEN, "open failed.\n"); if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) { cdo->lock_door(cdi, 0); cdinfo(CD_OPEN, "door unlocked.\n"); @@ -1076,6 +1214,8 @@ int cdrom_release(struct cdrom_device_info *cdi, struct file *fp) 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"); @@ -1102,7 +1242,7 @@ int cdrom_release(struct cdrom_device_info *cdi, struct file *fp) 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; @@ -1185,7 +1325,7 @@ int cdrom_number_of_slots(struct cdrom_device_info *cdi) /* 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) @@ -1282,6 +1422,7 @@ int media_changed(struct cdrom_device_info *cdi, int queue) 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; @@ -1384,16 +1525,16 @@ void sanitize_format(union cdrom_addr *addr, *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; cgc->buflen = len; cgc->data_direction = type; - cgc->timeout = 5*HZ; + cgc->timeout = CDROM_DEF_TIMEOUT; } /* DVD handling */ @@ -1401,7 +1542,7 @@ void init_cdrom_command(struct cdrom_generic_command *cgc, void *buf, int len, #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); @@ -1423,7 +1564,7 @@ static void setup_report_key(struct cdrom_generic_command *cgc, unsigned agid, u 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); @@ -1449,7 +1590,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) { 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; @@ -1460,6 +1601,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) /* 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))) @@ -1494,6 +1636,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) /* 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; @@ -1595,7 +1738,7 @@ static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s) { 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; @@ -1645,7 +1788,7 @@ static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s) { 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); @@ -1668,12 +1811,12 @@ static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s) { 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; - if ((buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL) + if ((buf = kmalloc(size, GFP_KERNEL)) == NULL) return -ENOMEM; init_cdrom_command(&cgc, buf, size, CGC_DATA_READ); @@ -1694,13 +1837,13 @@ static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s) { 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); cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; cgc.cmd[7] = s->type; - cgc.cmd[9] = cgc.buflen = 0xff; + cgc.cmd[9] = cgc.buflen & 0xff; if ((ret = cdo->generic_packet(cdi, &cgc))) return ret; @@ -1719,12 +1862,12 @@ static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s) { 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; - if ((buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL) + if ((buf = kmalloc(size, GFP_KERNEL)) == NULL) return -ENOMEM; init_cdrom_command(&cgc, buf, size, CGC_DATA_READ); @@ -1741,7 +1884,7 @@ static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s) 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); @@ -1777,7 +1920,7 @@ static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s) } 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; @@ -1793,7 +1936,7 @@ int cdrom_mode_sense(struct cdrom_device_info *cdi, } 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; @@ -1811,7 +1954,7 @@ static int cdrom_read_subchannel(struct cdrom_device_info *cdi, 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; @@ -1845,7 +1988,7 @@ static int cdrom_read_subchannel(struct cdrom_device_info *cdi, * 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; @@ -1865,7 +2008,7 @@ static int cdrom_read_cd(struct cdrom_device_info *cdi, /* 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; @@ -1899,8 +2042,11 @@ static int cdrom_read_block(struct cdrom_device_info *cdi, 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)); @@ -1920,8 +2066,8 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, 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; @@ -1932,13 +2078,17 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, 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, @@ -1953,6 +2103,12 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, if (!q) return -ENXIO; + rq = blk_get_request(q, READ, GFP_KERNEL); + if (!rq) + return -ENOMEM; + + cdi->last_sense = 0; + while (nframes) { nr = nframes; if (cdi->cdda_method == CDDA_BPC_SINGLE) @@ -1962,9 +2118,9 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, len = nr * CD_FRAMESIZE_RAW; - rq = blk_rq_map_user(q, READ, ubuf, len); - if (IS_ERR(rq)) - return PTR_ERR(rq); + ret = blk_rq_map_user(q, rq, ubuf, len); + if (ret) + break; memset(rq->cmd, 0, sizeof(rq->cmd)); rq->cmd[0] = GPCMD_READ_CD; @@ -1979,17 +2135,17 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, rq->cmd[9] = 0xf8; rq->cmd_len = 12; - rq->flags |= REQ_BLOCK_PC; + rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->timeout = 60 * HZ; bio = rq->bio; - if (blk_execute_rq(q, cdi->disk, rq)) { + if (blk_execute_rq(q, cdi->disk, rq, 0)) { 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(bio)) ret = -EFAULT; if (ret) @@ -1997,8 +2153,10 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, nframes -= nr; lba += nr; + ubuf += len; } + blk_put_request(rq); return ret; } @@ -2041,395 +2199,586 @@ retry: return cdrom_read_cdda_old(cdi, ubuf, lba, nframes); } -/* Just about every imaginable ioctl is supported in the Uniform layer - * 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) +static int cdrom_ioctl_multisession(struct cdrom_device_info *cdi, + void __user *argp) { - struct cdrom_device_ops *cdo = cdi->ops; + struct cdrom_multisession ms_info; + u8 requested_format; int ret; - /* Try the generic SCSI command ioctl's first.. */ - ret = scsi_cmd_ioctl(ip->i_bdev->bd_disk, cmd, arg); - if (ret != -ENOTTY) + cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n"); + + if (!(cdi->ops->capability & CDC_MULTI_SESSION)) + return -ENOSYS; + + if (copy_from_user(&ms_info, argp, sizeof(ms_info))) + return -EFAULT; + + requested_format = ms_info.addr_format; + if (requested_format != CDROM_MSF && requested_format != CDROM_LBA) + return -EINVAL; + ms_info.addr_format = CDROM_LBA; + + ret = cdi->ops->get_last_session(cdi, &ms_info); + if (ret) return ret; - /* the first few commands do not deal with audio drive_info, but - only with routines in cdrom device operations. */ - switch (cmd) { - case CDROMMULTISESSION: { - struct cdrom_multisession ms_info; - u_char requested_format; - cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n"); - if (!(cdo->capability & CDC_MULTI_SESSION)) - return -ENOSYS; - IOCTL_IN(arg, struct cdrom_multisession, ms_info); - requested_format = ms_info.addr_format; - if (!((requested_format == CDROM_MSF) || - (requested_format == CDROM_LBA))) - return -EINVAL; - ms_info.addr_format = CDROM_LBA; - if ((ret=cdo->get_last_session(cdi, &ms_info))) + sanitize_format(&ms_info.addr, &ms_info.addr_format, requested_format); + + if (copy_to_user(argp, &ms_info, sizeof(ms_info))) + return -EFAULT; + + cdinfo(CD_DO_IOCTL, "CDROMMULTISESSION successful\n"); + return 0; +} + +static int cdrom_ioctl_eject(struct cdrom_device_info *cdi) +{ + cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); + + if (!CDROM_CAN(CDC_OPEN_TRAY)) + return -ENOSYS; + if (cdi->use_count != 1 || keeplocked) + return -EBUSY; + if (CDROM_CAN(CDC_LOCK)) { + int ret = cdi->ops->lock_door(cdi, 0); + if (ret) return ret; - sanitize_format(&ms_info.addr, &ms_info.addr_format, - requested_format); - IOCTL_OUT(arg, struct cdrom_multisession, ms_info); - cdinfo(CD_DO_IOCTL, "CDROMMULTISESSION successful\n"); - return 0; - } + } - case CDROMEJECT: { - cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); - if (!CDROM_CAN(CDC_OPEN_TRAY)) - return -ENOSYS; - if (cdi->use_count != 1 || keeplocked) - return -EBUSY; - if (CDROM_CAN(CDC_LOCK)) - if ((ret=cdo->lock_door(cdi, 0))) - return ret; + return cdi->ops->tray_move(cdi, 1); +} - return cdo->tray_move(cdi, 1); - } +static int cdrom_ioctl_closetray(struct cdrom_device_info *cdi) +{ + cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n"); - case CDROMCLOSETRAY: { - cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n"); - if (!CDROM_CAN(CDC_CLOSE_TRAY)) - return -ENOSYS; - return cdo->tray_move(cdi, 0); - } + if (!CDROM_CAN(CDC_CLOSE_TRAY)) + return -ENOSYS; + return cdi->ops->tray_move(cdi, 0); +} - case CDROMEJECT_SW: { - cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n"); - if (!CDROM_CAN(CDC_OPEN_TRAY)) - return -ENOSYS; - if (keeplocked) - return -EBUSY; - cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT); - if (arg) - cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT; - return 0; - } +static int cdrom_ioctl_eject_sw(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n"); - case CDROM_MEDIA_CHANGED: { - struct cdrom_changer_info *info; - int changed; + if (!CDROM_CAN(CDC_OPEN_TRAY)) + return -ENOSYS; + if (keeplocked) + return -EBUSY; - cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n"); - if (!CDROM_CAN(CDC_MEDIA_CHANGED)) - return -ENOSYS; + cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT); + if (arg) + cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT; + return 0; +} - /* cannot select disc or select current disc */ - if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT) - return media_changed(cdi, 1); +static int cdrom_ioctl_media_changed(struct cdrom_device_info *cdi, + unsigned long arg) +{ + struct cdrom_changer_info *info; + int ret; - if ((unsigned int)arg >= cdi->capacity) - return -EINVAL; + cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n"); - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; + if (!CDROM_CAN(CDC_MEDIA_CHANGED)) + return -ENOSYS; - if ((ret = cdrom_read_mech_status(cdi, info))) { - kfree(info); - return ret; - } + /* cannot select disc or select current disc */ + if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT) + return media_changed(cdi, 1); - changed = info->slots[arg].change; - kfree(info); - return changed; - } + if ((unsigned int)arg >= cdi->capacity) + return -EINVAL; - case CDROM_SET_OPTIONS: { - cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n"); - /* options need to be in sync with capability. too late for - that, so we have to check each one separately... */ - switch (arg) { - case CDO_USE_FFLAGS: - case CDO_CHECK_TYPE: - break; - case CDO_LOCK: - if (!CDROM_CAN(CDC_LOCK)) - return -ENOSYS; - break; - case 0: - return cdi->options; - /* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */ - default: - if (!CDROM_CAN(arg)) - return -ENOSYS; - } - cdi->options |= (int) arg; - return cdi->options; - } + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; - case CDROM_CLEAR_OPTIONS: { - cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n"); - cdi->options &= ~(int) arg; - return cdi->options; - } + ret = cdrom_read_mech_status(cdi, info); + if (!ret) + ret = info->slots[arg].change; + kfree(info); + return ret; +} - case CDROM_SELECT_SPEED: { - cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n"); - if (!CDROM_CAN(CDC_SELECT_SPEED)) - return -ENOSYS; - return cdo->select_speed(cdi, arg); - } +static int cdrom_ioctl_set_options(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n"); - case CDROM_SELECT_DISC: { - cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n"); - if (!CDROM_CAN(CDC_SELECT_DISC)) + /* + * Options need to be in sync with capability. + * Too late for that, so we have to check each one separately. + */ + switch (arg) { + case CDO_USE_FFLAGS: + case CDO_CHECK_TYPE: + break; + case CDO_LOCK: + if (!CDROM_CAN(CDC_LOCK)) + return -ENOSYS; + break; + case 0: + return cdi->options; + /* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */ + default: + if (!CDROM_CAN(arg)) return -ENOSYS; + } + cdi->options |= (int) arg; + return cdi->options; +} - if ((arg != CDSL_CURRENT) && (arg != CDSL_NONE)) - if ((int)arg >= cdi->capacity) - return -EINVAL; - - /* cdo->select_disc is a hook to allow a driver-specific - * way of seleting disc. However, since there is no - * equiv hook for cdrom_slot_status this may not - * actually be useful... - */ - if (cdo->select_disc != NULL) - return cdo->select_disc(cdi, arg); - - /* no driver specific select_disc(), call our own */ - cdinfo(CD_CHANGER, "Using generic cdrom_select_disc()\n"); - return cdrom_select_disc(cdi, arg); - } +static int cdrom_ioctl_clear_options(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n"); - case CDROMRESET: { - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n"); - if (!CDROM_CAN(CDC_RESET)) - return -ENOSYS; - invalidate_bdev(ip->i_bdev, 0); - return cdo->reset(cdi); - } + cdi->options &= ~(int) arg; + return cdi->options; +} - case CDROM_LOCKDOOR: { - cdinfo(CD_DO_IOCTL, "%socking door.\n", arg ? "L" : "Unl"); - if (!CDROM_CAN(CDC_LOCK)) - return -EDRIVE_CANT_DO_THIS; - keeplocked = arg ? 1 : 0; - /* don't unlock the door on multiple opens,but allow root - * to do so */ - if ((cdi->use_count != 1) && !arg && !capable(CAP_SYS_ADMIN)) - return -EBUSY; - return cdo->lock_door(cdi, arg); - } +static int cdrom_ioctl_select_speed(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n"); - case CDROM_DEBUG: { - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - cdinfo(CD_DO_IOCTL, "%sabling debug.\n", arg ? "En" : "Dis"); - debug = arg ? 1 : 0; - return debug; - } + if (!CDROM_CAN(CDC_SELECT_SPEED)) + return -ENOSYS; + return cdi->ops->select_speed(cdi, arg); +} - case CDROM_GET_CAPABILITY: { - cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n"); - return (cdo->capability & ~cdi->mask); - } +static int cdrom_ioctl_select_disc(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n"); + + if (!CDROM_CAN(CDC_SELECT_DISC)) + return -ENOSYS; + + if (arg != CDSL_CURRENT && arg != CDSL_NONE) { + if ((int)arg >= cdi->capacity) + return -EINVAL; + } + + /* + * ->select_disc is a hook to allow a driver-specific way of + * seleting disc. However, since there is no equivalent hook for + * cdrom_slot_status this may not actually be useful... + */ + if (cdi->ops->select_disc) + return cdi->ops->select_disc(cdi, arg); + + cdinfo(CD_CHANGER, "Using generic cdrom_select_disc()\n"); + return cdrom_select_disc(cdi, arg); +} + +static int cdrom_ioctl_reset(struct cdrom_device_info *cdi, + struct block_device *bdev) +{ + cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n"); + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (!CDROM_CAN(CDC_RESET)) + return -ENOSYS; + invalidate_bdev(bdev, 0); + return cdi->ops->reset(cdi); +} + +static int cdrom_ioctl_lock_door(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cdinfo(CD_DO_IOCTL, "%socking door.\n", arg ? "L" : "Unl"); + + if (!CDROM_CAN(CDC_LOCK)) + return -EDRIVE_CANT_DO_THIS; + + keeplocked = arg ? 1 : 0; + + /* + * Don't unlock the door on multiple opens by default, but allow + * root to do so. + */ + if (cdi->use_count != 1 && !arg && !capable(CAP_SYS_ADMIN)) + return -EBUSY; + return cdi->ops->lock_door(cdi, arg); +} + +static int cdrom_ioctl_debug(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cdinfo(CD_DO_IOCTL, "%sabling debug.\n", arg ? "En" : "Dis"); -/* The following function is implemented, although very few audio + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + debug = arg ? 1 : 0; + return debug; +} + +static int cdrom_ioctl_get_capability(struct cdrom_device_info *cdi) +{ + cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n"); + return (cdi->ops->capability & ~cdi->mask); +} + +/* + * The following function is implemented, although very few audio * discs give Universal Product Code information, which should just be * the Medium Catalog Number on the box. Note, that the way the code * is written on the CD is /not/ uniform across all discs! */ - case CDROM_GET_MCN: { - struct cdrom_mcn mcn; - cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n"); - if (!(cdo->capability & CDC_MCN)) - return -ENOSYS; - if ((ret=cdo->get_mcn(cdi, &mcn))) - return ret; - IOCTL_OUT(arg, struct cdrom_mcn, mcn); - cdinfo(CD_DO_IOCTL, "CDROM_GET_MCN successful\n"); - return 0; - } +static int cdrom_ioctl_get_mcn(struct cdrom_device_info *cdi, + void __user *argp) +{ + struct cdrom_mcn mcn; + int ret; - case CDROM_DRIVE_STATUS: { - cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n"); - if (!(cdo->capability & CDC_DRIVE_STATUS)) - return -ENOSYS; - if (!CDROM_CAN(CDC_SELECT_DISC)) - return cdo->drive_status(cdi, CDSL_CURRENT); - if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) - return cdo->drive_status(cdi, CDSL_CURRENT); - if (((int)arg >= cdi->capacity)) - return -EINVAL; - return cdrom_slot_status(cdi, arg); - } + cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n"); - /* Ok, this is where problems start. The current interface for the - CDROM_DISC_STATUS ioctl is flawed. It makes the false assumption - that CDs are all CDS_DATA_1 or all CDS_AUDIO, etc. Unfortunatly, - while this is often the case, it is also very common for CDs to - have some tracks with data, and some tracks with audio. Just - because I feel like it, I declare the following to be the best - way to cope. If the CD has ANY data tracks on it, it will be - returned as a data CD. If it has any XA tracks, I will return - it as that. Now I could simplify this interface by combining these - returns with the above, but this more clearly demonstrates - the problem with the current interface. Too bad this wasn't - designed to use bitmasks... -Erik - - Well, now we have the option CDS_MIXED: a mixed-type CD. - User level programmers might feel the ioctl is not very useful. - ---david - */ - case CDROM_DISC_STATUS: { - tracktype tracks; - cdinfo(CD_DO_IOCTL, "entering CDROM_DISC_STATUS\n"); - cdrom_count_tracks(cdi, &tracks); - if (tracks.error) - return(tracks.error); - - /* Policy mode on */ - if (tracks.audio > 0) { - if (tracks.data==0 && tracks.cdi==0 && tracks.xa==0) - return CDS_AUDIO; - else - return CDS_MIXED; - } - if (tracks.cdi > 0) return CDS_XA_2_2; - if (tracks.xa > 0) return CDS_XA_2_1; - if (tracks.data > 0) return CDS_DATA_1; - /* Policy mode off */ + if (!(cdi->ops->capability & CDC_MCN)) + return -ENOSYS; + ret = cdi->ops->get_mcn(cdi, &mcn); + if (ret) + return ret; - cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognize!\n"); - return CDS_NO_INFO; - } + if (copy_to_user(argp, &mcn, sizeof(mcn))) + return -EFAULT; + cdinfo(CD_DO_IOCTL, "CDROM_GET_MCN successful\n"); + return 0; +} - case CDROM_CHANGER_NSLOTS: { - cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n"); - return cdi->capacity; - } +static int cdrom_ioctl_drive_status(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n"); + + if (!(cdi->ops->capability & CDC_DRIVE_STATUS)) + return -ENOSYS; + if (!CDROM_CAN(CDC_SELECT_DISC) || + (arg == CDSL_CURRENT || arg == CDSL_NONE)) + return cdi->ops->drive_status(cdi, CDSL_CURRENT); + if (((int)arg >= cdi->capacity)) + return -EINVAL; + return cdrom_slot_status(cdi, arg); +} + +/* + * Ok, this is where problems start. The current interface for the + * CDROM_DISC_STATUS ioctl is flawed. It makes the false assumption that + * CDs are all CDS_DATA_1 or all CDS_AUDIO, etc. Unfortunatly, while this + * is often the case, it is also very common for CDs to have some tracks + * with data, and some tracks with audio. Just because I feel like it, + * I declare the following to be the best way to cope. If the CD has ANY + * data tracks on it, it will be returned as a data CD. If it has any XA + * tracks, I will return it as that. Now I could simplify this interface + * by combining these returns with the above, but this more clearly + * demonstrates the problem with the current interface. Too bad this + * wasn't designed to use bitmasks... -Erik + * + * Well, now we have the option CDS_MIXED: a mixed-type CD. + * User level programmers might feel the ioctl is not very useful. + * ---david + */ +static int cdrom_ioctl_disc_status(struct cdrom_device_info *cdi) +{ + tracktype tracks; + + cdinfo(CD_DO_IOCTL, "entering CDROM_DISC_STATUS\n"); + + cdrom_count_tracks(cdi, &tracks); + if (tracks.error) + return tracks.error; + + /* Policy mode on */ + if (tracks.audio > 0) { + if (!tracks.data && !tracks.cdi && !tracks.xa) + return CDS_AUDIO; + else + return CDS_MIXED; } - /* use the ioctls that are implemented through the generic_packet() - interface. this may look at bit funny, but if -ENOTTY is - returned that particular ioctl is not implemented and we - let it go through the device specific ones. */ + if (tracks.cdi > 0) + return CDS_XA_2_2; + if (tracks.xa > 0) + return CDS_XA_2_1; + if (tracks.data > 0) + return CDS_DATA_1; + /* Policy mode off */ + + cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognize!\n"); + return CDS_NO_INFO; +} + +static int cdrom_ioctl_changer_nslots(struct cdrom_device_info *cdi) +{ + cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n"); + return cdi->capacity; +} + +static int cdrom_ioctl_get_subchnl(struct cdrom_device_info *cdi, + void __user *argp) +{ + struct cdrom_subchnl q; + u8 requested, back; + int ret; + + /* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ + + if (!CDROM_CAN(CDC_PLAY_AUDIO)) + return -ENOSYS; + if (copy_from_user(&q, argp, sizeof(q))) + return -EFAULT; + + requested = q.cdsc_format; + if (requested != CDROM_MSF && requested != CDROM_LBA) + return -EINVAL; + q.cdsc_format = CDROM_MSF; + + ret = cdi->ops->audio_ioctl(cdi, CDROMSUBCHNL, &q); + if (ret) + return ret; + + back = q.cdsc_format; /* local copy */ + sanitize_format(&q.cdsc_absaddr, &back, requested); + sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested); + + if (copy_to_user(argp, &q, sizeof(q))) + return -EFAULT; + /* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ + return 0; +} + +static int cdrom_ioctl_read_tochdr(struct cdrom_device_info *cdi, + void __user *argp) +{ + struct cdrom_tochdr header; + int ret; + + /* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ + + if (!CDROM_CAN(CDC_PLAY_AUDIO)) + return -ENOSYS; + if (copy_from_user(&header, argp, sizeof(header))) + return -EFAULT; + + ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header); + if (ret) + return ret; + + if (copy_to_user(argp, &header, sizeof(header))) + return -EFAULT; + /* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */ + return 0; +} + +static int cdrom_ioctl_read_tocentry(struct cdrom_device_info *cdi, + void __user *argp) +{ + struct cdrom_tocentry entry; + u8 requested_format; + int ret; + + /* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ + + if (!CDROM_CAN(CDC_PLAY_AUDIO)) + return -ENOSYS; + if (copy_from_user(&entry, argp, sizeof(entry))) + return -EFAULT; + + requested_format = entry.cdte_format; + if (requested_format != CDROM_MSF && requested_format != CDROM_LBA) + return -EINVAL; + /* make interface to low-level uniform */ + entry.cdte_format = CDROM_MSF; + ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry); + if (ret) + return ret; + sanitize_format(&entry.cdte_addr, &entry.cdte_format, requested_format); + + if (copy_to_user(argp, &entry, sizeof(entry))) + return -EFAULT; + /* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY successful\n"); */ + return 0; +} + +static int cdrom_ioctl_play_msf(struct cdrom_device_info *cdi, + void __user *argp) +{ + struct cdrom_msf msf; + + cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); + + if (!CDROM_CAN(CDC_PLAY_AUDIO)) + return -ENOSYS; + if (copy_from_user(&msf, argp, sizeof(msf))) + return -EFAULT; + return cdi->ops->audio_ioctl(cdi, CDROMPLAYMSF, &msf); +} + +static int cdrom_ioctl_play_trkind(struct cdrom_device_info *cdi, + void __user *argp) +{ + struct cdrom_ti ti; + int ret; + + cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n"); + + if (!CDROM_CAN(CDC_PLAY_AUDIO)) + return -ENOSYS; + if (copy_from_user(&ti, argp, sizeof(ti))) + return -EFAULT; + + ret = check_for_audio_disc(cdi, cdi->ops); + if (ret) + return ret; + return cdi->ops->audio_ioctl(cdi, CDROMPLAYTRKIND, &ti); +} +static int cdrom_ioctl_volctrl(struct cdrom_device_info *cdi, + void __user *argp) +{ + struct cdrom_volctrl volume; + + cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n"); + + if (!CDROM_CAN(CDC_PLAY_AUDIO)) + return -ENOSYS; + if (copy_from_user(&volume, argp, sizeof(volume))) + return -EFAULT; + return cdi->ops->audio_ioctl(cdi, CDROMVOLCTRL, &volume); +} + +static int cdrom_ioctl_volread(struct cdrom_device_info *cdi, + void __user *argp) +{ + struct cdrom_volctrl volume; + int ret; + + cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n"); + + if (!CDROM_CAN(CDC_PLAY_AUDIO)) + return -ENOSYS; + + ret = cdi->ops->audio_ioctl(cdi, CDROMVOLREAD, &volume); + if (ret) + return ret; + + if (copy_to_user(argp, &volume, sizeof(volume))) + return -EFAULT; + return 0; +} + +static int cdrom_ioctl_audioctl(struct cdrom_device_info *cdi, + unsigned int cmd) +{ + int ret; + + cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n"); + + if (!CDROM_CAN(CDC_PLAY_AUDIO)) + return -ENOSYS; + ret = check_for_audio_disc(cdi, cdi->ops); + if (ret) + return ret; + return cdi->ops->audio_ioctl(cdi, cmd, NULL); +} + +/* + * Just about every imaginable ioctl is supported in the Uniform layer + * these days. + * ATAPI / SCSI specific code now mainly resides in mmc_ioctl(). + */ +int cdrom_ioctl(struct file * file, struct cdrom_device_info *cdi, + struct inode *ip, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int ret; + + /* + * Try the generic SCSI command ioctl's first. + */ + ret = scsi_cmd_ioctl(file, ip->i_bdev->bd_disk, cmd, argp); + if (ret != -ENOTTY) + return ret; + + switch (cmd) { + case CDROMMULTISESSION: + return cdrom_ioctl_multisession(cdi, argp); + case CDROMEJECT: + return cdrom_ioctl_eject(cdi); + case CDROMCLOSETRAY: + return cdrom_ioctl_closetray(cdi); + case CDROMEJECT_SW: + return cdrom_ioctl_eject_sw(cdi, arg); + case CDROM_MEDIA_CHANGED: + return cdrom_ioctl_media_changed(cdi, arg); + case CDROM_SET_OPTIONS: + return cdrom_ioctl_set_options(cdi, arg); + case CDROM_CLEAR_OPTIONS: + return cdrom_ioctl_clear_options(cdi, arg); + case CDROM_SELECT_SPEED: + return cdrom_ioctl_select_speed(cdi, arg); + case CDROM_SELECT_DISC: + return cdrom_ioctl_select_disc(cdi, arg); + case CDROMRESET: + return cdrom_ioctl_reset(cdi, ip->i_bdev); + case CDROM_LOCKDOOR: + return cdrom_ioctl_lock_door(cdi, arg); + case CDROM_DEBUG: + return cdrom_ioctl_debug(cdi, arg); + case CDROM_GET_CAPABILITY: + return cdrom_ioctl_get_capability(cdi); + case CDROM_GET_MCN: + return cdrom_ioctl_get_mcn(cdi, argp); + case CDROM_DRIVE_STATUS: + return cdrom_ioctl_drive_status(cdi, arg); + case CDROM_DISC_STATUS: + return cdrom_ioctl_disc_status(cdi); + case CDROM_CHANGER_NSLOTS: + return cdrom_ioctl_changer_nslots(cdi); + } + + /* + * Use the ioctls that are implemented through the generic_packet() + * interface. this may look at bit funny, but if -ENOTTY is + * returned that particular ioctl is not implemented and we + * let it go through the device specific ones. + */ if (CDROM_CAN(CDC_GENERIC_PACKET)) { ret = mmc_ioctl(cdi, cmd, arg); - if (ret != -ENOTTY) { + if (ret != -ENOTTY) return ret; - } } - /* note: most of the cdinfo() calls are commented out here, - because they fill up the sys log when CD players poll - the drive. */ + /* + * Note: most of the cdinfo() calls are commented out here, + * because they fill up the sys log when CD players poll + * the drive. + */ switch (cmd) { - case CDROMSUBCHNL: { - struct cdrom_subchnl q; - u_char requested, back; - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - /* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ - IOCTL_IN(arg, struct cdrom_subchnl, q); - requested = q.cdsc_format; - if (!((requested == CDROM_MSF) || - (requested == CDROM_LBA))) - return -EINVAL; - q.cdsc_format = CDROM_MSF; - if ((ret=cdo->audio_ioctl(cdi, cmd, &q))) - return ret; - back = q.cdsc_format; /* local copy */ - sanitize_format(&q.cdsc_absaddr, &back, requested); - sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested); - IOCTL_OUT(arg, struct cdrom_subchnl, q); - /* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ - return 0; - } - case CDROMREADTOCHDR: { - struct cdrom_tochdr header; - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - /* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ - IOCTL_IN(arg, struct cdrom_tochdr, header); - if ((ret=cdo->audio_ioctl(cdi, cmd, &header))) - return ret; - IOCTL_OUT(arg, struct cdrom_tochdr, header); - /* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */ - return 0; - } - case CDROMREADTOCENTRY: { - struct cdrom_tocentry entry; - u_char requested_format; - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - /* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ - IOCTL_IN(arg, struct cdrom_tocentry, entry); - requested_format = entry.cdte_format; - if (!((requested_format == CDROM_MSF) || - (requested_format == CDROM_LBA))) - return -EINVAL; - /* make interface to low-level uniform */ - entry.cdte_format = CDROM_MSF; - if ((ret=cdo->audio_ioctl(cdi, cmd, &entry))) - return ret; - sanitize_format(&entry.cdte_addr, - &entry.cdte_format, requested_format); - IOCTL_OUT(arg, struct cdrom_tocentry, entry); - /* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY successful\n"); */ - return 0; - } - case CDROMPLAYMSF: { - struct cdrom_msf msf; - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); - IOCTL_IN(arg, struct cdrom_msf, msf); - return cdo->audio_ioctl(cdi, cmd, &msf); - } - case CDROMPLAYTRKIND: { - struct cdrom_ti ti; - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n"); - IOCTL_IN(arg, struct cdrom_ti, ti); - CHECKAUDIO; - return cdo->audio_ioctl(cdi, cmd, &ti); - } - case CDROMVOLCTRL: { - struct cdrom_volctrl volume; - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n"); - IOCTL_IN(arg, struct cdrom_volctrl, volume); - return cdo->audio_ioctl(cdi, cmd, &volume); - } - case CDROMVOLREAD: { - struct cdrom_volctrl volume; - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n"); - if ((ret=cdo->audio_ioctl(cdi, cmd, &volume))) - return ret; - IOCTL_OUT(arg, struct cdrom_volctrl, volume); - return 0; - } + case CDROMSUBCHNL: + return cdrom_ioctl_get_subchnl(cdi, argp); + case CDROMREADTOCHDR: + return cdrom_ioctl_read_tochdr(cdi, argp); + case CDROMREADTOCENTRY: + return cdrom_ioctl_read_tocentry(cdi, argp); + case CDROMPLAYMSF: + return cdrom_ioctl_play_msf(cdi, argp); + case CDROMPLAYTRKIND: + return cdrom_ioctl_play_trkind(cdi, argp); + case CDROMVOLCTRL: + return cdrom_ioctl_volctrl(cdi, argp); + case CDROMVOLREAD: + return cdrom_ioctl_volread(cdi, argp); case CDROMSTART: case CDROMSTOP: case CDROMPAUSE: - case CDROMRESUME: { - if (!CDROM_CAN(CDC_PLAY_AUDIO)) - return -ENOSYS; - cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n"); - CHECKAUDIO; - return cdo->audio_ioctl(cdi, cmd, NULL); - } - } /* switch */ + case CDROMRESUME: + return cdrom_ioctl_audioctl(cdi, cmd); + } - /* do the device specific ioctls */ - if (CDROM_CAN(CDC_IOCTLS)) - return cdo->dev_ioctl(cdi, cmd, arg); - return -ENOSYS; } @@ -2446,7 +2795,7 @@ int msf_to_lba(char m, char s, char f) 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)); @@ -2472,9 +2821,9 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, 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)); @@ -2505,7 +2854,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, /* FIXME: we need upper bound checking, too!! */ if (lba < 0) return -EINVAL; - cgc.buffer = (char *) kmalloc(blocksize, GFP_KERNEL); + cgc.buffer = kmalloc(blocksize, GFP_KERNEL); if (cgc.buffer == NULL) return -ENOMEM; memset(&sense, 0, sizeof(sense)); @@ -2526,7 +2875,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, 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; @@ -2601,8 +2950,9 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, 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); @@ -2612,17 +2962,27 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, 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(*(__be16 *)(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 */ @@ -2647,7 +3007,8 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, 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); } @@ -2675,10 +3036,10 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, int size = sizeof(dvd_struct); if (!CDROM_CAN(CDC_DVD)) return -ENOSYS; - if ((s = (dvd_struct *) kmalloc(size, GFP_KERNEL)) == NULL) + if ((s = 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; } @@ -2686,7 +3047,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, 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; @@ -2729,8 +3090,8 @@ static int cdrom_get_track_info(struct cdrom_device_info *cdi, __u16 track, __u8 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; @@ -2743,22 +3104,26 @@ static int cdrom_get_track_info(struct cdrom_device_info *cdi, __u16 track, __u8 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); @@ -2772,14 +3137,18 @@ static int cdrom_get_disc_info(struct cdrom_device_info *cdi, disc_information * /* 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 @@ -2790,27 +3159,37 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written) 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 */ @@ -2823,11 +3202,12 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written) /* 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; @@ -2840,32 +3220,38 @@ static int cdrom_get_next_writable(struct cdrom_device_info *cdi, long *next_wri 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))) { @@ -2889,14 +3275,12 @@ EXPORT_SYMBOL(cdrom_mode_select); 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 */ @@ -2905,14 +3289,14 @@ struct cdrom_sysctl_settings { 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; } @@ -3001,14 +3385,14 @@ int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp, 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; @@ -3033,13 +3417,13 @@ void cdrom_update_settings(void) } 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) { @@ -3086,7 +3470,7 @@ static int cdrom_sysctl_handler(ctl_table *ctl, int write, struct file * filp, } /* Place files in /proc/sys/dev/cdrom */ -ctl_table cdrom_table[] = { +static ctl_table cdrom_table[] = { { .ctl_name = DEV_CDROM_INFO, .procname = "info", @@ -3138,7 +3522,7 @@ ctl_table cdrom_table[] = { { .ctl_name = 0 } }; -ctl_table cdrom_cdrom_table[] = { +static ctl_table cdrom_cdrom_table[] = { { .ctl_name = DEV_CDROM, .procname = "cdrom", @@ -3150,8 +3534,7 @@ ctl_table cdrom_cdrom_table[] = { }; /* 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", @@ -3159,7 +3542,6 @@ ctl_table cdrom_root_table[] = { .mode = 0555, .child = cdrom_cdrom_table, }, -#endif /* CONFIG_PROC_FS */ { .ctl_name = 0 } }; static struct ctl_table_header *cdrom_sysctl_header;