diff options
Diffstat (limited to 'drivers/cdrom/cdrom.c')
| -rw-r--r-- | drivers/cdrom/cdrom.c | 2832 |
1 files changed, 1581 insertions, 1251 deletions
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 879bbc26ce9..898b84bba28 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 <andersee@debian.org> Copyright (c) 1998, 1999 Jens Axboe <axboe@image.dk> @@ -30,7 +30,7 @@ changelog for the 1.x series, David? 2.00 Dec 2, 1997 -- Erik Andersen <andersee@debian.org> - -- New maintainer! As David A. van Leeuwen has been too busy to activly + -- New maintainer! As David A. van Leeuwen has been too busy to actively maintain and improve this driver, I am now carrying on the torch. If you have a problem with this driver, please feel free to contact me. @@ -242,6 +242,8 @@ -------------------------------------------------------------------------*/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define REVISION "Revision: 3.20" #define VERSION "Id: cdrom.c 3.20 2003/12/17" @@ -263,10 +265,8 @@ /* #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 <linux/config.h> #include <linux/module.h> #include <linux/fs.h> -#include <linux/buffer_head.h> #include <linux/major.h> #include <linux/types.h> #include <linux/errno.h> @@ -285,17 +285,15 @@ #include <asm/uaccess.h> /* used to tell the module to turn on full debugging messages */ -static int debug; -/* used to keep tray locked at all times */ -static int keeplocked; +static bool debug; /* default compatibility mode */ -static int autoclose=1; -static int autoeject; -static int lockdoor = 1; +static bool autoclose=1; +static bool autoeject; +static bool lockdoor = 1; /* will we ever get to use this... sigh. */ -static int check_media_type; +static bool check_media_type; /* automatically restart mrw format */ -static int mrw_format_restart = 1; +static bool mrw_format_restart = 1; module_param(debug, bool, 0); module_param(autoclose, bool, 0); module_param(autoeject, bool, 0); @@ -303,7 +301,7 @@ module_param(lockdoor, bool, 0); module_param(check_media_type, bool, 0); module_param(mrw_format_restart, bool, 0); -static DEFINE_SPINLOCK(cdrom_lock); +static DEFINE_MUTEX(cdrom_mutex); static const char *mrw_format_status[] = { "not mrw", @@ -314,51 +312,35 @@ static const char *mrw_format_status[] = { static const char *mrw_address_space[] = { "DMA", "GAA" }; -#if (ERRLOGMASK!=CD_NOTHING) -#define cdinfo(type, fmt, args...) \ - if ((ERRLOGMASK & type) || debug==1 ) \ - printk(KERN_INFO "cdrom: " fmt, ## args) +#if (ERRLOGMASK != CD_NOTHING) +#define cd_dbg(type, fmt, ...) \ +do { \ + if ((ERRLOGMASK & type) || debug == 1) \ + pr_debug(fmt, ##__VA_ARGS__); \ +} while (0) #else -#define cdinfo(type, fmt, args...) +#define cd_dbg(type, fmt, ...) \ +do { \ + if (0 && (ERRLOGMASK & type) || debug == 1) \ + pr_debug(fmt, ##__VA_ARGS__); \ +} while (0) #endif -/* 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 __user *) (arg), sizeof (in))) \ - return -EFAULT; - -#define IOCTL_OUT(arg, type, out) \ - if (copy_to_user((type __user *) (arg), &(out), sizeof (out))) \ - return -EFAULT; - /* The (cdo->capability & ~cdi->mask & CDC_XXX) construct was used in a lot of places. This macro makes the code more clear. */ #define CDROM_CAN(type) (cdi->ops->capability & ~cdi->mask & (type)) -/* 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, - struct cdrom_device_ops * cdo); -static void sanitize_format(union cdrom_addr *addr, - u_char * curr, u_char requested); -static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, - unsigned long arg); -int cdrom_get_last_written(struct cdrom_device_info *, long *); -static int cdrom_get_next_writable(struct cdrom_device_info *, long *); -static void cdrom_count_tracks(struct cdrom_device_info *, tracktype*); - -static int cdrom_mrw_exit(struct cdrom_device_info *cdi); - -static int cdrom_get_disc_info(struct cdrom_device_info *cdi, disc_information *di); - -#ifdef CONFIG_SYSCTL static void cdrom_sysctl_register(void); -#endif /* CONFIG_SYSCTL */ -static struct cdrom_device_info *topCdromPtr; + +static LIST_HEAD(cdrom_list); static int cdrom_dummy_generic_packet(struct cdrom_device_info *cdi, struct packet_command *cgc) @@ -373,134 +355,65 @@ static int cdrom_dummy_generic_packet(struct cdrom_device_info *cdi, 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 - * change it here without gcc complaining at every line. - */ -#define ENSURE(call, bits) if (cdo->call == NULL) *change_capability &= ~(bits) - -int register_cdrom(struct cdrom_device_info *cdi) -{ - static char banner_printed; - struct cdrom_device_ops *cdo = cdi->ops; - int *change_capability = (int *)&cdo->capability; /* hack */ - - cdinfo(CD_OPEN, "entering register_cdrom\n"); - - if (cdo->open == NULL || cdo->release == NULL) - return -2; - if (!banner_printed) { - printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n"); - banner_printed = 1; -#ifdef CONFIG_SYSCTL - cdrom_sysctl_register(); -#endif /* CONFIG_SYSCTL */ - } - - ENSURE(drive_status, CDC_DRIVE_STATUS ); - ENSURE(media_changed, CDC_MEDIA_CHANGED); - ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY); - ENSURE(lock_door, CDC_LOCK); - ENSURE(select_speed, CDC_SELECT_SPEED); - ENSURE(get_last_session, CDC_MULTI_SESSION); - 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; - cdi->options = CDO_USE_FFLAGS; - - if (autoclose==1 && CDROM_CAN(CDC_CLOSE_TRAY)) - cdi->options |= (int) CDO_AUTO_CLOSE; - if (autoeject==1 && CDROM_CAN(CDC_OPEN_TRAY)) - cdi->options |= (int) CDO_AUTO_EJECT; - if (lockdoor==1) - cdi->options |= (int) CDO_LOCK; - if (check_media_type==1) - cdi->options |= (int) CDO_CHECK_TYPE; - - if (CDROM_CAN(CDC_MRW_W)) - cdi->exit = cdrom_mrw_exit; - - if (cdi->disk) - cdi->cdda_method = CDDA_BPC_FULL; - 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; - topCdromPtr = cdi; - spin_unlock(&cdrom_lock); - return 0; -} -#undef ENSURE - -int unregister_cdrom(struct cdrom_device_info *unreg) +static int cdrom_flush_cache(struct cdrom_device_info *cdi) { - struct cdrom_device_info *cdi, *prev; - cdinfo(CD_OPEN, "entering unregister_cdrom\n"); - - prev = NULL; - spin_lock(&cdrom_lock); - cdi = topCdromPtr; - while (cdi && cdi != unreg) { - prev = cdi; - cdi = cdi->next; - } - - if (cdi == NULL) { - spin_unlock(&cdrom_lock); - return -2; - } - if (prev) - prev->next = cdi->next; - else - topCdromPtr = cdi->next; + struct packet_command cgc; - spin_unlock(&cdrom_lock); + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_FLUSH_CACHE; - if (cdi->exit) - cdi->exit(cdi); + cgc.timeout = 5 * 60 * HZ; - cdi->ops->n_minors--; - cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name); - return 0; + return cdi->ops->generic_packet(cdi, &cgc); } -int cdrom_get_media_event(struct cdrom_device_info *cdi, - struct media_event_desc *med) +/* 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 packet_command cgc; - unsigned char buffer[8]; - struct event_header *eh = (struct event_header *) buffer; + int ret, buflen; - init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); - cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION; - cgc.cmd[1] = 1; /* IMMED */ - cgc.cmd[4] = 1 << 4; /* media event */ - cgc.cmd[8] = sizeof(buffer); + /* set up command and get the disc info */ + init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ); + cgc.cmd[0] = GPCMD_READ_DISC_INFO; + cgc.cmd[8] = cgc.buflen = 2; cgc.quiet = 1; - if (cdi->ops->generic_packet(cdi, &cgc)) - return 1; + ret = cdo->generic_packet(cdi, &cgc); + if (ret) + return ret; - if (be16_to_cpu(eh->data_len) < sizeof(*med)) - return 1; + /* not all drives have the same disc_info length, so requeue + * packet with the length the drive tells us it can supply + */ + buflen = be16_to_cpu(di->disc_information_length) + + sizeof(di->disc_information_length); - if (eh->nea || eh->notification_class != 0x4) - return 1; + if (buflen > sizeof(disc_information)) + buflen = sizeof(disc_information); - memcpy(med, &buffer[sizeof(*eh)], sizeof(*med)); - return 0; + cgc.cmd[8] = cgc.buflen = buflen; + ret = cdo->generic_packet(cdi, &cgc); + if (ret) + return ret; + + /* return actual fill size */ + return buflen; } +/* 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 + * change it here without gcc complaining at every line. + */ +#define ENSURE(call, bits) \ +do { \ + if (cdo->call == NULL) \ + *change_capability &= ~(bits); \ +} while (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 @@ -564,7 +477,7 @@ static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont) unsigned char buffer[12]; int ret; - printk(KERN_INFO "cdrom: %sstarting format\n", cont ? "Re" : ""); + pr_info("%sstarting format\n", cont ? "Re" : ""); /* * FmtData bit set (bit 4), format type is 1 @@ -594,7 +507,7 @@ static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont) ret = cdi->ops->generic_packet(cdi, &cgc); if (ret) - printk(KERN_INFO "cdrom: bgformat failed\n"); + pr_info("bgformat failed\n"); return ret; } @@ -617,18 +530,6 @@ static int cdrom_mrw_bgformat_susp(struct cdrom_device_info *cdi, int immed) return cdi->ops->generic_packet(cdi, &cgc); } -static int cdrom_flush_cache(struct cdrom_device_info *cdi) -{ - struct packet_command cgc; - - init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); - cgc.cmd[0] = GPCMD_FLUSH_CACHE; - - cgc.timeout = 5 * 60 * HZ; - - return cdi->ops->generic_packet(cdi, &cgc); -} - static int cdrom_mrw_exit(struct cdrom_device_info *cdi) { disc_information di; @@ -640,8 +541,7 @@ static int cdrom_mrw_exit(struct cdrom_device_info *cdi) ret = 0; if (di.mrw_status == CDM_MRW_BGFORMAT_ACTIVE) { - printk(KERN_INFO "cdrom: issuing MRW back ground " - "format suspend\n"); + pr_info("issuing MRW background format suspend\n"); ret = cdrom_mrw_bgformat_susp(cdi, 0); } @@ -663,20 +563,123 @@ static int cdrom_mrw_set_lba_space(struct cdrom_device_info *cdi, int space) cgc.buffer = buffer; cgc.buflen = sizeof(buffer); - if ((ret = cdrom_mode_sense(cdi, &cgc, cdi->mrw_mode_page, 0))) + ret = cdrom_mode_sense(cdi, &cgc, cdi->mrw_mode_page, 0); + if (ret) return ret; - mph = (struct mode_page_header *) buffer; + mph = (struct mode_page_header *)buffer; offset = be16_to_cpu(mph->desc_length); size = be16_to_cpu(mph->mode_data_length) + 2; buffer[offset + 3] = space; cgc.buflen = size; - if ((ret = cdrom_mode_select(cdi, &cgc))) + ret = cdrom_mode_select(cdi, &cgc); + if (ret) return ret; - printk(KERN_INFO "cdrom: %s: mrw address space %s selected\n", cdi->name, mrw_address_space[space]); + pr_info("%s: mrw address space %s selected\n", + cdi->name, mrw_address_space[space]); + return 0; +} + +int register_cdrom(struct cdrom_device_info *cdi) +{ + static char banner_printed; + struct cdrom_device_ops *cdo = cdi->ops; + int *change_capability = (int *)&cdo->capability; /* hack */ + + cd_dbg(CD_OPEN, "entering register_cdrom\n"); + + if (cdo->open == NULL || cdo->release == NULL) + return -EINVAL; + if (!banner_printed) { + pr_info("Uniform CD-ROM driver " REVISION "\n"); + banner_printed = 1; + cdrom_sysctl_register(); + } + + ENSURE(drive_status, CDC_DRIVE_STATUS); + if (cdo->check_events == NULL && cdo->media_changed == NULL) + *change_capability = ~(CDC_MEDIA_CHANGED | CDC_SELECT_DISC); + ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY); + ENSURE(lock_door, CDC_LOCK); + ENSURE(select_speed, CDC_SELECT_SPEED); + ENSURE(get_last_session, CDC_MULTI_SESSION); + ENSURE(get_mcn, CDC_MCN); + ENSURE(reset, CDC_RESET); + ENSURE(generic_packet, CDC_GENERIC_PACKET); + cdi->mc_flags = 0; + cdo->n_minors = 0; + cdi->options = CDO_USE_FFLAGS; + + if (autoclose == 1 && CDROM_CAN(CDC_CLOSE_TRAY)) + cdi->options |= (int) CDO_AUTO_CLOSE; + if (autoeject == 1 && CDROM_CAN(CDC_OPEN_TRAY)) + cdi->options |= (int) CDO_AUTO_EJECT; + if (lockdoor == 1) + cdi->options |= (int) CDO_LOCK; + if (check_media_type == 1) + cdi->options |= (int) CDO_CHECK_TYPE; + + if (CDROM_CAN(CDC_MRW_W)) + cdi->exit = cdrom_mrw_exit; + + if (cdi->disk) + cdi->cdda_method = CDDA_BPC_FULL; + else + cdi->cdda_method = CDDA_OLD; + + if (!cdo->generic_packet) + cdo->generic_packet = cdrom_dummy_generic_packet; + + cd_dbg(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); + mutex_lock(&cdrom_mutex); + list_add(&cdi->list, &cdrom_list); + mutex_unlock(&cdrom_mutex); + return 0; +} +#undef ENSURE + +void unregister_cdrom(struct cdrom_device_info *cdi) +{ + cd_dbg(CD_OPEN, "entering unregister_cdrom\n"); + + mutex_lock(&cdrom_mutex); + list_del(&cdi->list); + mutex_unlock(&cdrom_mutex); + + if (cdi->exit) + cdi->exit(cdi); + + cdi->ops->n_minors--; + cd_dbg(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name); +} + +int cdrom_get_media_event(struct cdrom_device_info *cdi, + struct media_event_desc *med) +{ + struct packet_command cgc; + unsigned char buffer[8]; + struct event_header *eh = (struct event_header *)buffer; + + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); + cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION; + cgc.cmd[1] = 1; /* IMMED */ + cgc.cmd[4] = 1 << 4; /* media event */ + cgc.cmd[8] = sizeof(buffer); + cgc.quiet = 1; + + if (cdi->ops->generic_packet(cdi, &cgc)) + return 1; + + 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; } @@ -705,7 +708,7 @@ static int cdrom_has_defect_mgt(struct cdrom_device_info *cdi) { struct packet_command cgc; char buffer[16]; - __u16 *feature_code; + __be16 *feature_code; int ret; init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); @@ -718,7 +721,7 @@ static int cdrom_has_defect_mgt(struct cdrom_device_info *cdi) if ((ret = cdi->ops->generic_packet(cdi, &cgc))) return ret; - feature_code = (__u16 *) &buffer[sizeof(struct feature_header)]; + feature_code = (__be16 *) &buffer[sizeof(struct feature_header)]; if (be16_to_cpu(*feature_code) == CDF_HWDM) return 0; @@ -780,7 +783,7 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi) * always reset to DMA lba space on open */ if (cdrom_mrw_set_lba_space(cdi, MRW_LBA_DMA)) { - printk(KERN_ERR "cdrom: failed setting lba address space\n"); + pr_err("failed setting lba address space\n"); return 1; } @@ -799,8 +802,7 @@ 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]); + pr_info("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 && @@ -852,7 +854,7 @@ static int cdrom_ram_open_write(struct cdrom_device_info *cdi) else if (CDF_RWRT == be16_to_cpu(rfd.feature_code)) ret = !rfd.curr; - cdinfo(CD_OPEN, "can open for random write\n"); + cd_dbg(CD_OPEN, "can open for random write\n"); return ret; } @@ -941,17 +943,16 @@ 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); + cd_dbg(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); + cd_dbg(CD_CLOSE, "%s: DVD+RW media clean\n", cdi->name); return; } - printk(KERN_INFO "cdrom: %s: dirty DVD+RW media, \"finalizing\"\n", - cdi->name); + pr_info("%s: dirty DVD+RW media, \"finalizing\"\n", cdi->name); init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); cgc.cmd[0] = GPCMD_FLUSH_CACHE; @@ -983,76 +984,74 @@ static int cdrom_close_write(struct cdrom_device_info *cdi) #endif } -/* We use the open-option O_NONBLOCK to indicate that the - * purpose of opening is only for subsequent ioctl() calls; no device - * integrity checks are performed. - * - * We hope that all cd-player programs will adopt this convention. It - * is in their own interest: device control becomes a lot easier - * this way. - */ -int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp) +/* badly broken, I know. Is due for a fixup anytime. */ +static void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype *tracks) { - int ret; - - cdinfo(CD_OPEN, "entering cdrom_open\n"); - - /* if this was a O_NONBLOCK open and we should honor the flags, - * do a quick open without drive/disc integrity checks. */ - cdi->use_count++; - 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_open_write(cdi)) - goto err; - if (!CDROM_CAN(CDC_RAM)) - goto err; - ret = 0; - cdi->media_written = 0; + struct cdrom_tochdr header; + struct cdrom_tocentry entry; + int ret, i; + tracks->data = 0; + tracks->audio = 0; + tracks->cdi = 0; + tracks->xa = 0; + tracks->error = 0; + cd_dbg(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n"); + /* Grab the TOC header so we can see how many tracks there are */ + ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header); + if (ret) { + if (ret == -ENOMEDIUM) + tracks->error = CDS_NO_DISC; + else + tracks->error = CDS_NO_INFO; + return; + } + /* check what type of tracks are on this disc */ + entry.cdte_format = CDROM_MSF; + for (i = header.cdth_trk0; i <= header.cdth_trk1; i++) { + entry.cdte_track = i; + if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry)) { + tracks->error = CDS_NO_INFO; + return; + } + if (entry.cdte_ctrl & CDROM_DATA_TRACK) { + if (entry.cdte_format == 0x10) + tracks->cdi++; + else if (entry.cdte_format == 0x20) + tracks->xa++; + else + tracks->data++; + } else { + tracks->audio++; } + cd_dbg(CD_COUNT_TRACKS, "track %d: format=%d, ctrl=%d\n", + i, entry.cdte_format, entry.cdte_ctrl); } - - if (ret) - goto err; - - cdinfo(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", - cdi->name, cdi->use_count); - /* Do this on open. Don't wait for mount, because they might - not be mounting, but opening with O_NONBLOCK */ - check_disk_change(ip->i_bdev); - return 0; -err: - cdi->use_count--; - return ret; + cd_dbg(CD_COUNT_TRACKS, "disc has %d tracks: %d=audio %d=data %d=Cd-I %d=XA\n", + header.cdth_trk1, tracks->audio, tracks->data, + tracks->cdi, tracks->xa); } static -int open_for_data(struct cdrom_device_info * cdi) +int open_for_data(struct cdrom_device_info *cdi) { int ret; struct cdrom_device_ops *cdo = cdi->ops; tracktype tracks; - cdinfo(CD_OPEN, "entering open_for_data\n"); + cd_dbg(CD_OPEN, "entering open_for_data\n"); /* Check if the driver can report drive status. If it can, we can do clever things. If it can't, well, we at least tried! */ if (cdo->drive_status != NULL) { ret = cdo->drive_status(cdi, CDSL_CURRENT); - cdinfo(CD_OPEN, "drive_status=%d\n", ret); + cd_dbg(CD_OPEN, "drive_status=%d\n", ret); if (ret == CDS_TRAY_OPEN) { - cdinfo(CD_OPEN, "the tray is open...\n"); + cd_dbg(CD_OPEN, "the tray is open...\n"); /* can/may i close it? */ if (CDROM_CAN(CDC_CLOSE_TRAY) && cdi->options & CDO_AUTO_CLOSE) { - cdinfo(CD_OPEN, "trying to close the tray.\n"); + cd_dbg(CD_OPEN, "trying to close the tray\n"); ret=cdo->tray_move(cdi,0); if (ret) { - cdinfo(CD_OPEN, "bummer. tried to close the tray but failed.\n"); + cd_dbg(CD_OPEN, "bummer. tried to close the tray but failed.\n"); /* Ignore the error from the low level driver. We don't care why it couldn't close the tray. We only care @@ -1062,19 +1061,19 @@ int open_for_data(struct cdrom_device_info * cdi) goto clean_up_and_return; } } else { - cdinfo(CD_OPEN, "bummer. this drive can't close the tray.\n"); + cd_dbg(CD_OPEN, "bummer. this drive can't close the tray.\n"); ret=-ENOMEDIUM; goto clean_up_and_return; } /* Ok, the door should be closed now.. Check again */ ret = cdo->drive_status(cdi, CDSL_CURRENT); if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) { - cdinfo(CD_OPEN, "bummer. the tray is still not closed.\n"); - cdinfo(CD_OPEN, "tray might not contain a medium.\n"); + cd_dbg(CD_OPEN, "bummer. the tray is still not closed.\n"); + cd_dbg(CD_OPEN, "tray might not contain a medium\n"); ret=-ENOMEDIUM; goto clean_up_and_return; } - cdinfo(CD_OPEN, "the tray is now closed.\n"); + cd_dbg(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); @@ -1085,7 +1084,7 @@ int open_for_data(struct cdrom_device_info * cdi) } cdrom_count_tracks(cdi, &tracks); if (tracks.error == CDS_NO_DISC) { - cdinfo(CD_OPEN, "bummer. no disc.\n"); + cd_dbg(CD_OPEN, "bummer. no disc.\n"); ret=-ENOMEDIUM; goto clean_up_and_return; } @@ -1095,34 +1094,34 @@ int open_for_data(struct cdrom_device_info * cdi) if (cdi->options & CDO_CHECK_TYPE) { /* give people a warning shot, now that CDO_CHECK_TYPE is the default case! */ - cdinfo(CD_OPEN, "bummer. wrong media type.\n"); - cdinfo(CD_WARNING, "pid %d must open device O_NONBLOCK!\n", - (unsigned int)current->pid); + cd_dbg(CD_OPEN, "bummer. wrong media type.\n"); + cd_dbg(CD_WARNING, "pid %d must open device O_NONBLOCK!\n", + (unsigned int)task_pid_nr(current)); ret=-EMEDIUMTYPE; goto clean_up_and_return; } else { - cdinfo(CD_OPEN, "wrong media type, but CDO_CHECK_TYPE not set.\n"); + cd_dbg(CD_OPEN, "wrong media type, but CDO_CHECK_TYPE not set\n"); } } - cdinfo(CD_OPEN, "all seems well, opening the device.\n"); + cd_dbg(CD_OPEN, "all seems well, opening the devicen"); /* all seems well, we can open the device */ ret = cdo->open(cdi, 0); /* open for data */ - cdinfo(CD_OPEN, "opening the device gave me %d.\n", ret); + cd_dbg(CD_OPEN, "opening the device gave me %d\n", ret); /* After all this careful checking, we shouldn't have problems opening the device, but we don't want the device locked if this somehow fails... */ if (ret) { - cdinfo(CD_OPEN, "open device failed.\n"); + cd_dbg(CD_OPEN, "open device failed\n"); goto clean_up_and_return; } if (CDROM_CAN(CDC_LOCK) && (cdi->options & CDO_LOCK)) { cdo->lock_door(cdi, 1); - cdinfo(CD_OPEN, "door locked.\n"); + cd_dbg(CD_OPEN, "door locked\n"); } - cdinfo(CD_OPEN, "device opened successfully.\n"); + cd_dbg(CD_OPEN, "device opened successfully\n"); return ret; /* Something failed. Try to unlock the drive, because some drivers @@ -1131,37 +1130,93 @@ 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_OPEN, "open failed.\n"); + cd_dbg(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"); + cd_dbg(CD_OPEN, "door unlocked\n"); + } + return ret; +} + +/* We use the open-option O_NONBLOCK to indicate that the + * purpose of opening is only for subsequent ioctl() calls; no device + * integrity checks are performed. + * + * We hope that all cd-player programs will adopt this convention. It + * is in their own interest: device control becomes a lot easier + * this way. + */ +int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev, + fmode_t mode) +{ + int ret; + + cd_dbg(CD_OPEN, "entering cdrom_open\n"); + + /* open is event synchronization point, check events first */ + check_disk_change(bdev); + + /* if this was a O_NONBLOCK open and we should honor the flags, + * do a quick open without drive/disc integrity checks. */ + cdi->use_count++; + if ((mode & FMODE_NDELAY) && (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 (mode & FMODE_WRITE) { + ret = -EROFS; + if (cdrom_open_write(cdi)) + goto err_release; + if (!CDROM_CAN(CDC_RAM)) + goto err_release; + ret = 0; + cdi->media_written = 0; + } + } + + if (ret) + goto err; + + cd_dbg(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", + cdi->name, cdi->use_count); + return 0; +err_release: + if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) { + cdi->ops->lock_door(cdi, 0); + cd_dbg(CD_OPEN, "door unlocked\n"); } + cdi->ops->release(cdi); +err: + cdi->use_count--; return ret; } /* This code is similar to that in open_for_data. The routine is called whenever an audio play operation is requested. */ -int check_for_audio_disc(struct cdrom_device_info * cdi, - struct cdrom_device_ops * cdo) +static int check_for_audio_disc(struct cdrom_device_info * cdi, + struct cdrom_device_ops * cdo) { int ret; tracktype tracks; - cdinfo(CD_OPEN, "entering check_for_audio_disc\n"); + cd_dbg(CD_OPEN, "entering check_for_audio_disc\n"); if (!(cdi->options & CDO_CHECK_TYPE)) return 0; if (cdo->drive_status != NULL) { ret = cdo->drive_status(cdi, CDSL_CURRENT); - cdinfo(CD_OPEN, "drive_status=%d\n", ret); + cd_dbg(CD_OPEN, "drive_status=%d\n", ret); if (ret == CDS_TRAY_OPEN) { - cdinfo(CD_OPEN, "the tray is open...\n"); + cd_dbg(CD_OPEN, "the tray is open...\n"); /* can/may i close it? */ if (CDROM_CAN(CDC_CLOSE_TRAY) && cdi->options & CDO_AUTO_CLOSE) { - cdinfo(CD_OPEN, "trying to close the tray.\n"); + cd_dbg(CD_OPEN, "trying to close the tray\n"); ret=cdo->tray_move(cdi,0); if (ret) { - cdinfo(CD_OPEN, "bummer. tried to close tray but failed.\n"); + cd_dbg(CD_OPEN, "bummer. tried to close tray but failed.\n"); /* Ignore the error from the low level driver. We don't care why it couldn't close the tray. We only care @@ -1170,20 +1225,20 @@ int check_for_audio_disc(struct cdrom_device_info * cdi, return -ENOMEDIUM; } } else { - cdinfo(CD_OPEN, "bummer. this driver can't close the tray.\n"); + cd_dbg(CD_OPEN, "bummer. this driver can't close the tray.\n"); return -ENOMEDIUM; } /* Ok, the door should be closed now.. Check again */ ret = cdo->drive_status(cdi, CDSL_CURRENT); if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) { - cdinfo(CD_OPEN, "bummer. the tray is still not closed.\n"); + cd_dbg(CD_OPEN, "bummer. the tray is still not closed.\n"); return -ENOMEDIUM; } if (ret!=CDS_DISC_OK) { - cdinfo(CD_OPEN, "bummer. disc isn't ready.\n"); + cd_dbg(CD_OPEN, "bummer. disc isn't ready.\n"); return -EIO; } - cdinfo(CD_OPEN, "the tray is now closed.\n"); + cd_dbg(CD_OPEN, "the tray is now closed\n"); } } cdrom_count_tracks(cdi, &tracks); @@ -1196,27 +1251,29 @@ int check_for_audio_disc(struct cdrom_device_info * cdi, return 0; } -/* Admittedly, the logic below could be performed in a nicer way. */ -int cdrom_release(struct cdrom_device_info *cdi, struct file *fp) +void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode) { struct cdrom_device_ops *cdo = cdi->ops; int opened_for_data; - cdinfo(CD_CLOSE, "entering cdrom_release\n"); + cd_dbg(CD_CLOSE, "entering cdrom_release\n"); if (cdi->use_count > 0) cdi->use_count--; - if (cdi->use_count == 0) - cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); - if (cdi->use_count == 0) + + if (cdi->use_count == 0) { + cd_dbg(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", + cdi->name); cdrom_dvd_rw_close_write(cdi); - if (cdi->use_count == 0 && - (cdo->capability & CDC_LOCK) && !keeplocked) { - cdinfo(CD_CLOSE, "Unlocking door!\n"); - cdo->lock_door(cdi, 0); + + if ((cdo->capability & CDC_LOCK) && !cdi->keeplocked) { + cd_dbg(CD_CLOSE, "Unlocking door!\n"); + cdo->lock_door(cdi, 0); + } } + opened_for_data = !(cdi->options & CDO_USE_FFLAGS) || - !(fp && fp->f_flags & O_NONBLOCK); + !(mode & FMODE_NDELAY); /* * flush cache on last write release @@ -1230,7 +1287,6 @@ int cdrom_release(struct cdrom_device_info *cdi, struct file *fp) cdi->options & CDO_AUTO_EJECT && CDROM_CAN(CDC_OPEN_TRAY)) cdo->tray_move(cdi, 1); } - return 0; } static int cdrom_read_mech_status(struct cdrom_device_info *cdi, @@ -1270,7 +1326,7 @@ static int cdrom_slot_status(struct cdrom_device_info *cdi, int slot) struct cdrom_changer_info *info; int ret; - cdinfo(CD_CHANGER, "entering cdrom_slot_status()\n"); + cd_dbg(CD_CHANGER, "entering cdrom_slot_status()\n"); if (cdi->sanyo_slot) return CDS_NO_INFO; @@ -1300,7 +1356,7 @@ int cdrom_number_of_slots(struct cdrom_device_info *cdi) int nslots = 1; struct cdrom_changer_info *info; - cdinfo(CD_CHANGER, "entering cdrom_number_of_slots()\n"); + cd_dbg(CD_CHANGER, "entering cdrom_number_of_slots()\n"); /* cdrom_read_mech_status requires a valid value for capacity: */ cdi->capacity = 0; @@ -1321,7 +1377,7 @@ static int cdrom_load_unload(struct cdrom_device_info *cdi, int slot) { struct packet_command cgc; - cdinfo(CD_CHANGER, "entering cdrom_load_unload()\n"); + cd_dbg(CD_CHANGER, "entering cdrom_load_unload()\n"); if (cdi->sanyo_slot && slot < 0) return 0; @@ -1350,11 +1406,14 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot) int curslot; int ret; - cdinfo(CD_CHANGER, "entering cdrom_select_disc()\n"); + cd_dbg(CD_CHANGER, "entering cdrom_select_disc()\n"); if (!CDROM_CAN(CDC_SELECT_DISC)) return -EDRIVE_CANT_DO_THIS; - (void) cdi->ops->media_changed(cdi, slot); + if (cdi->ops->check_events) + cdi->ops->check_events(cdi, 0, slot); + else + cdi->ops->media_changed(cdi, slot); if (slot == CDSL_NONE) { /* set media changed bits, on both queues */ @@ -1374,7 +1433,7 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot) curslot = info->hdr.curslot; kfree(info); - if (cdi->use_count > 1 || keeplocked) { + if (cdi->use_count > 1 || cdi->keeplocked) { if (slot == CDSL_CURRENT) { return curslot; } else { @@ -1398,6 +1457,42 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot) return slot; } +/* + * As cdrom implements an extra ioctl consumer for media changed + * event, it needs to buffer ->check_events() output, such that event + * is not lost for both the usual VFS and ioctl paths. + * cdi->{vfs|ioctl}_events are used to buffer pending events for each + * path. + * + * XXX: Locking is non-existent. cdi->ops->check_events() can be + * called in parallel and buffering fields are accessed without any + * exclusion. The original media_changed code had the same problem. + * It might be better to simply deprecate CDROM_MEDIA_CHANGED ioctl + * and remove this cruft altogether. It doesn't have much usefulness + * at this point. + */ +static void cdrom_update_events(struct cdrom_device_info *cdi, + unsigned int clearing) +{ + unsigned int events; + + events = cdi->ops->check_events(cdi, clearing, CDSL_CURRENT); + cdi->vfs_events |= events; + cdi->ioctl_events |= events; +} + +unsigned int cdrom_check_events(struct cdrom_device_info *cdi, + unsigned int clearing) +{ + unsigned int events; + + cdrom_update_events(cdi, clearing); + events = cdi->vfs_events; + cdi->vfs_events = 0; + return events; +} +EXPORT_SYMBOL(cdrom_check_events); + /* We want to make media_changed accessible to the user through an * ioctl. The main problem now is that we must double-buffer the * low-level implementation, to assure that the VFS and the user both @@ -1409,15 +1504,26 @@ int media_changed(struct cdrom_device_info *cdi, int queue) { unsigned int mask = (1 << (queue & 1)); int ret = !!(cdi->mc_flags & mask); + bool changed; if (!CDROM_CAN(CDC_MEDIA_CHANGED)) - return ret; + return ret; + /* changed since last call? */ - if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) { + if (cdi->ops->check_events) { + BUG_ON(!queue); /* shouldn't be called from VFS path */ + cdrom_update_events(cdi, DISK_EVENT_MEDIA_CHANGE); + changed = cdi->ioctl_events & DISK_EVENT_MEDIA_CHANGE; + cdi->ioctl_events = 0; + } else + changed = cdi->ops->media_changed(cdi, CDSL_CURRENT); + + if (changed) { cdi->mc_flags = 0x3; /* set bit on both queues */ ret |= 1; cdi->media_written = 0; } + cdi->mc_flags &= ~mask; /* clear bit */ return ret; } @@ -1434,55 +1540,6 @@ int cdrom_media_changed(struct cdrom_device_info *cdi) return media_changed(cdi, 0); } -/* badly broken, I know. Is due for a fixup anytime. */ -static void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype* tracks) -{ - struct cdrom_tochdr header; - struct cdrom_tocentry entry; - int ret, i; - tracks->data=0; - tracks->audio=0; - tracks->cdi=0; - tracks->xa=0; - tracks->error=0; - cdinfo(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n"); - if (!CDROM_CAN(CDC_PLAY_AUDIO)) { - tracks->error=CDS_NO_INFO; - return; - } - /* Grab the TOC header so we can see how many tracks there are */ - if ((ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header))) { - if (ret == -ENOMEDIUM) - tracks->error = CDS_NO_DISC; - else - tracks->error = CDS_NO_INFO; - return; - } - /* check what type of tracks are on this disc */ - entry.cdte_format = CDROM_MSF; - for (i = header.cdth_trk0; i <= header.cdth_trk1; i++) { - entry.cdte_track = i; - if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry)) { - tracks->error=CDS_NO_INFO; - return; - } - if (entry.cdte_ctrl & CDROM_DATA_TRACK) { - if (entry.cdte_format == 0x10) - tracks->cdi++; - else if (entry.cdte_format == 0x20) - tracks->xa++; - else - tracks->data++; - } else - tracks->audio++; - cdinfo(CD_COUNT_TRACKS, "track %d: format=%d, ctrl=%d\n", - i, entry.cdte_format, entry.cdte_ctrl); - } - cdinfo(CD_COUNT_TRACKS, "disc has %d tracks: %d=audio %d=data %d=Cd-I %d=XA\n", - header.cdth_trk1, tracks->audio, tracks->data, - tracks->cdi, tracks->xa); -} - /* Requests to the low-level drivers will /always/ be done in the following format convention: @@ -1528,7 +1585,7 @@ void init_cdrom_command(struct packet_command *cgc, void *buf, int len, cgc->buffer = (char *) buf; cgc->buflen = len; cgc->data_direction = type; - cgc->timeout = 5*HZ; + cgc->timeout = CDROM_DEF_TIMEOUT; } /* DVD handling */ @@ -1594,7 +1651,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) switch (ai->type) { /* LU data send */ case DVD_LU_SEND_AGID: - cdinfo(CD_DVD, "entering DVD_LU_SEND_AGID\n"); + cd_dbg(CD_DVD, "entering DVD_LU_SEND_AGID\n"); cgc.quiet = 1; setup_report_key(&cgc, ai->lsa.agid, 0); @@ -1606,7 +1663,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) break; case DVD_LU_SEND_KEY1: - cdinfo(CD_DVD, "entering DVD_LU_SEND_KEY1\n"); + cd_dbg(CD_DVD, "entering DVD_LU_SEND_KEY1\n"); setup_report_key(&cgc, ai->lsk.agid, 2); if ((ret = cdo->generic_packet(cdi, &cgc))) @@ -1617,7 +1674,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) break; case DVD_LU_SEND_CHALLENGE: - cdinfo(CD_DVD, "entering DVD_LU_SEND_CHALLENGE\n"); + cd_dbg(CD_DVD, "entering DVD_LU_SEND_CHALLENGE\n"); setup_report_key(&cgc, ai->lsc.agid, 1); if ((ret = cdo->generic_packet(cdi, &cgc))) @@ -1629,7 +1686,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"); + cd_dbg(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; @@ -1648,7 +1705,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) break; case DVD_LU_SEND_ASF: - cdinfo(CD_DVD, "entering DVD_LU_SEND_ASF\n"); + cd_dbg(CD_DVD, "entering DVD_LU_SEND_ASF\n"); setup_report_key(&cgc, ai->lsasf.agid, 5); if ((ret = cdo->generic_packet(cdi, &cgc))) @@ -1659,7 +1716,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) /* LU data receive (LU changes state) */ case DVD_HOST_SEND_CHALLENGE: - cdinfo(CD_DVD, "entering DVD_HOST_SEND_CHALLENGE\n"); + cd_dbg(CD_DVD, "entering DVD_HOST_SEND_CHALLENGE\n"); setup_send_key(&cgc, ai->hsc.agid, 1); buf[1] = 0xe; copy_chal(&buf[4], ai->hsc.chal); @@ -1671,7 +1728,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) break; case DVD_HOST_SEND_KEY2: - cdinfo(CD_DVD, "entering DVD_HOST_SEND_KEY2\n"); + cd_dbg(CD_DVD, "entering DVD_HOST_SEND_KEY2\n"); setup_send_key(&cgc, ai->hsk.agid, 3); buf[1] = 0xa; copy_key(&buf[4], ai->hsk.key); @@ -1686,7 +1743,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) /* Misc */ case DVD_INVALIDATE_AGID: cgc.quiet = 1; - cdinfo(CD_DVD, "entering DVD_INVALIDATE_AGID\n"); + cd_dbg(CD_DVD, "entering DVD_INVALIDATE_AGID\n"); setup_report_key(&cgc, ai->lsa.agid, 0x3f); if ((ret = cdo->generic_packet(cdi, &cgc))) return ret; @@ -1694,7 +1751,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) /* Get region settings */ case DVD_LU_SEND_RPC_STATE: - cdinfo(CD_DVD, "entering DVD_LU_SEND_RPC_STATE\n"); + cd_dbg(CD_DVD, "entering DVD_LU_SEND_RPC_STATE\n"); setup_report_key(&cgc, 0, 8); memset(&rpc_state, 0, sizeof(rpc_state_t)); cgc.buffer = (char *) &rpc_state; @@ -1711,7 +1768,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) /* Set region settings */ case DVD_HOST_SEND_RPC_STATE: - cdinfo(CD_DVD, "entering DVD_HOST_SEND_RPC_STATE\n"); + cd_dbg(CD_DVD, "entering DVD_HOST_SEND_RPC_STATE\n"); setup_send_key(&cgc, 0, 6); buf[1] = 6; buf[4] = ai->hrpcs.pdrc; @@ -1721,36 +1778,37 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) break; default: - cdinfo(CD_WARNING, "Invalid DVD key ioctl (%d)\n", ai->type); + cd_dbg(CD_WARNING, "Invalid DVD key ioctl (%d)\n", ai->type); return -ENOTTY; } return 0; } -static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { unsigned char buf[21], *base; struct dvd_layer *layer; - struct packet_command cgc; struct cdrom_device_ops *cdo = cdi->ops; int ret, layer_num = s->physical.layer_num; if (layer_num >= DVD_LAYERS) return -EINVAL; - init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc.cmd[6] = layer_num; - cgc.cmd[7] = s->type; - cgc.cmd[9] = cgc.buflen & 0xff; + init_cdrom_command(cgc, buf, sizeof(buf), CGC_DATA_READ); + cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; + cgc->cmd[6] = layer_num; + cgc->cmd[7] = s->type; + cgc->cmd[9] = cgc->buflen & 0xff; /* * refrain from reporting errors on non-existing layers (mainly) */ - cgc.quiet = 1; + cgc->quiet = 1; - if ((ret = cdo->generic_packet(cdi, &cgc))) + ret = cdo->generic_packet(cdi, cgc); + if (ret) return ret; base = &buf[4]; @@ -1778,21 +1836,22 @@ static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s) return 0; } -static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { int ret; u_char buf[8]; - 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[6] = s->copyright.layer_num; - cgc.cmd[7] = s->type; - cgc.cmd[8] = cgc.buflen >> 8; - cgc.cmd[9] = cgc.buflen & 0xff; + init_cdrom_command(cgc, buf, sizeof(buf), CGC_DATA_READ); + cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; + cgc->cmd[6] = s->copyright.layer_num; + cgc->cmd[7] = s->type; + cgc->cmd[8] = cgc->buflen >> 8; + cgc->cmd[9] = cgc->buflen & 0xff; - if ((ret = cdo->generic_packet(cdi, &cgc))) + ret = cdo->generic_packet(cdi, cgc); + if (ret) return ret; s->copyright.cpst = buf[4]; @@ -1801,114 +1860,132 @@ static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s) return 0; } -static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { int ret, size; u_char *buf; - 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) + buf = kmalloc(size, GFP_KERNEL); + if (!buf) return -ENOMEM; - init_cdrom_command(&cgc, buf, size, CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc.cmd[7] = s->type; - cgc.cmd[8] = size >> 8; - cgc.cmd[9] = size & 0xff; - cgc.cmd[10] = s->disckey.agid << 6; + init_cdrom_command(cgc, buf, size, CGC_DATA_READ); + cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; + cgc->cmd[7] = s->type; + cgc->cmd[8] = size >> 8; + cgc->cmd[9] = size & 0xff; + cgc->cmd[10] = s->disckey.agid << 6; - if (!(ret = cdo->generic_packet(cdi, &cgc))) + ret = cdo->generic_packet(cdi, cgc); + if (!ret) memcpy(s->disckey.value, &buf[4], sizeof(s->disckey.value)); kfree(buf); return ret; } -static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { - int ret; - u_char buf[4 + 188]; - struct packet_command cgc; + int ret, size = 4 + 188; + u_char *buf; 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; + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + init_cdrom_command(cgc, buf, size, CGC_DATA_READ); + cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; + cgc->cmd[7] = s->type; + cgc->cmd[9] = cgc->buflen & 0xff; - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; + ret = cdo->generic_packet(cdi, cgc); + if (ret) + goto out; s->bca.len = buf[0] << 8 | buf[1]; if (s->bca.len < 12 || s->bca.len > 188) { - cdinfo(CD_WARNING, "Received invalid BCA length (%d)\n", s->bca.len); - return -EIO; + cd_dbg(CD_WARNING, "Received invalid BCA length (%d)\n", + s->bca.len); + ret = -EIO; + goto out; } memcpy(s->bca.value, &buf[4], s->bca.len); - - return 0; + ret = 0; +out: + kfree(buf); + return ret; } -static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { int ret = 0, size; u_char *buf; - 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) + buf = kmalloc(size, GFP_KERNEL); + if (!buf) return -ENOMEM; - init_cdrom_command(&cgc, buf, size, CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; - cgc.cmd[7] = s->type; - cgc.cmd[8] = size >> 8; - cgc.cmd[9] = size & 0xff; + init_cdrom_command(cgc, buf, size, CGC_DATA_READ); + cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE; + cgc->cmd[7] = s->type; + cgc->cmd[8] = size >> 8; + cgc->cmd[9] = size & 0xff; - if ((ret = cdo->generic_packet(cdi, &cgc))) { - kfree(buf); - return ret; - } + ret = cdo->generic_packet(cdi, cgc); + if (ret) + goto out; 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->manufact.len); + if (s->manufact.len < 0) { + cd_dbg(CD_WARNING, "Received invalid manufacture info length (%d)\n", + s->manufact.len); ret = -EIO; } else { + if (s->manufact.len > 2048) { + cd_dbg(CD_WARNING, "Received invalid manufacture info length (%d): truncating to 2048\n", + s->manufact.len); + s->manufact.len = 2048; + } memcpy(s->manufact.value, &buf[4], s->manufact.len); } +out: kfree(buf); return ret; } -static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s) +static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s, + struct packet_command *cgc) { switch (s->type) { case DVD_STRUCT_PHYSICAL: - return dvd_read_physical(cdi, s); + return dvd_read_physical(cdi, s, cgc); case DVD_STRUCT_COPYRIGHT: - return dvd_read_copyright(cdi, s); + return dvd_read_copyright(cdi, s, cgc); case DVD_STRUCT_DISCKEY: - return dvd_read_disckey(cdi, s); + return dvd_read_disckey(cdi, s, cgc); case DVD_STRUCT_BCA: - return dvd_read_bca(cdi, s); + return dvd_read_bca(cdi, s, cgc); case DVD_STRUCT_MANUFACT: - return dvd_read_manufact(cdi, s); + return dvd_read_manufact(cdi, s, cgc); default: - cdinfo(CD_WARNING, ": Invalid DVD structure read requested (%d)\n", - s->type); + cd_dbg(CD_WARNING, ": Invalid DVD structure read requested (%d)\n", + s->type); return -EINVAL; } } @@ -2059,11 +2136,6 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, if (!nr) return -ENOMEM; - if (!access_ok(VERIFY_WRITE, ubuf, nframes * CD_FRAMESIZE_RAW)) { - ret = -EFAULT; - goto out; - } - cgc.data_direction = CGC_DATA_READ; while (nframes > 0) { if (nr > nframes) @@ -2072,7 +2144,7 @@ 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; - if (__copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) { + if (copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) { ret = -EFAULT; break; } @@ -2080,7 +2152,6 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, nframes -= nr; lba += nr; } -out: kfree(cgc.buffer); return ret; } @@ -2088,7 +2159,7 @@ out: static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, int lba, int nframes) { - request_queue_t *q = cdi->disk->queue; + struct request_queue *q = cdi->disk->queue; struct request *rq; struct bio *bio; unsigned int len; @@ -2097,26 +2168,30 @@ 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) nr = 1; - if (nr * CD_FRAMESIZE_RAW > (q->max_sectors << 9)) - nr = (q->max_sectors << 9) / CD_FRAMESIZE_RAW; + if (nr * CD_FRAMESIZE_RAW > (queue_max_sectors(q) << 9)) + nr = (queue_max_sectors(q) << 9) / CD_FRAMESIZE_RAW; len = nr * CD_FRAMESIZE_RAW; - ret = blk_rq_map_user(q, rq, ubuf, len); - if (ret) + rq = blk_get_request(q, READ, GFP_KERNEL); + if (!rq) { + ret = -ENOMEM; + break; + } + blk_rq_set_block_pc(rq); + + ret = blk_rq_map_user(q, rq, NULL, ubuf, len, GFP_KERNEL); + if (ret) { + blk_put_request(rq); break; + } - memset(rq->cmd, 0, sizeof(rq->cmd)); rq->cmd[0] = GPCMD_READ_CD; rq->cmd[1] = 1 << 2; rq->cmd[2] = (lba >> 24) & 0xff; @@ -2129,21 +2204,18 @@ 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->timeout = 60 * HZ; bio = rq->bio; - if (rq->bio) - blk_queue_bounce(q, &rq->bio); - 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(bio, len)) + if (blk_rq_unmap_user(bio)) ret = -EFAULT; + blk_put_request(rq); if (ret) break; @@ -2153,7 +2225,6 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, ubuf += len; } - blk_put_request(rq); return ret; } @@ -2178,7 +2249,7 @@ retry: * frame dma, so drop to single frame dma if we need to */ if (cdi->cdda_method == CDDA_BPC_FULL && nframes > 1) { - printk("cdrom: dropping to single frame dma\n"); + pr_info("dropping to single frame dma\n"); cdi->cdda_method = CDDA_BPC_SINGLE; goto retry; } @@ -2191,407 +2262,489 @@ retry: if (cdi->last_sense != 0x04 && cdi->last_sense != 0x0b) return ret; - printk("cdrom: dropping to old style cdda (sense=%x)\n", cdi->last_sense); + pr_info("dropping to old style cdda (sense=%x)\n", cdi->last_sense); cdi->cdda_method = CDDA_OLD; 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 file * file, 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(file, ip->i_bdev->bd_disk, cmd, (void __user *)arg); - if (ret != -ENOTTY) + cd_dbg(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; + + cd_dbg(CD_DO_IOCTL, "CDROMMULTISESSION successful\n"); + return 0; +} + +static int cdrom_ioctl_eject(struct cdrom_device_info *cdi) +{ + cd_dbg(CD_DO_IOCTL, "entering CDROMEJECT\n"); + + if (!CDROM_CAN(CDC_OPEN_TRAY)) + return -ENOSYS; + if (cdi->use_count != 1 || cdi->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) +{ + cd_dbg(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) +{ + cd_dbg(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 (cdi->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; + cd_dbg(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) +{ + cd_dbg(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; +static int cdrom_ioctl_clear_options(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cd_dbg(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n"); - /* 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); + cdi->options &= ~(int) arg; + return cdi->options; +} - /* 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_select_speed(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cd_dbg(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\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); - } + if (!CDROM_CAN(CDC_SELECT_SPEED)) + return -ENOSYS; + return cdi->ops->select_speed(cdi, arg); +} - 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_disc(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cd_dbg(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\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_DISC)) + return -ENOSYS; - case CDROM_GET_CAPABILITY: { - cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n"); - return (cdo->capability & ~cdi->mask); - } + 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); + + cd_dbg(CD_CHANGER, "Using generic cdrom_select_disc()\n"); + return cdrom_select_disc(cdi, arg); +} -/* The following function is implemented, although very few audio +static int cdrom_ioctl_reset(struct cdrom_device_info *cdi, + struct block_device *bdev) +{ + cd_dbg(CD_DO_IOCTL, "entering CDROM_RESET\n"); + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (!CDROM_CAN(CDC_RESET)) + return -ENOSYS; + invalidate_bdev(bdev); + return cdi->ops->reset(cdi); +} + +static int cdrom_ioctl_lock_door(struct cdrom_device_info *cdi, + unsigned long arg) +{ + cd_dbg(CD_DO_IOCTL, "%socking door\n", arg ? "L" : "Unl"); + + if (!CDROM_CAN(CDC_LOCK)) + return -EDRIVE_CANT_DO_THIS; + + cdi->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) +{ + cd_dbg(CD_DO_IOCTL, "%sabling debug\n", arg ? "En" : "Dis"); + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + debug = arg ? 1 : 0; + return debug; +} + +static int cdrom_ioctl_get_capability(struct cdrom_device_info *cdi) +{ + cd_dbg(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); - } + cd_dbg(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; + cd_dbg(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) +{ + cd_dbg(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n"); - /* 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) { - return ret; - } + 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. Unfortunately, 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; + + cd_dbg(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; } - /* 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 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 */ + 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 */ - /* do the device specific ioctls */ - if (CDROM_CAN(CDC_IOCTLS)) - return cdo->dev_ioctl(cdi, cmd, arg); - - return -ENOSYS; + cd_dbg(CD_WARNING, "This disc doesn't have any tracks I recognize!\n"); + return CDS_NO_INFO; } -static inline -int msf_to_lba(char m, char s, char f) +static int cdrom_ioctl_changer_nslots(struct cdrom_device_info *cdi) { - return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET; + cd_dbg(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; + + /* cd_dbg(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ + + 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; + /* cd_dbg(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; + + /* cd_dbg(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ + + 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; + /* cd_dbg(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; + + /* cd_dbg(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ + + 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; + /* cd_dbg(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; + + cd_dbg(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; + + cd_dbg(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; + + cd_dbg(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; + + cd_dbg(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; + + cd_dbg(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); } /* @@ -2623,277 +2776,8 @@ static int cdrom_switch_blocksize(struct cdrom_device_info *cdi, int size) return cdo->generic_packet(cdi, &cgc); } -static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, - unsigned long arg) -{ - struct cdrom_device_ops *cdo = cdi->ops; - struct packet_command cgc; - struct request_sense sense; - unsigned char buffer[32]; - int ret = 0; - - memset(&cgc, 0, sizeof(cgc)); - - /* build a unified command and queue it through - cdo->generic_packet() */ - switch (cmd) { - case CDROMREADRAW: - case CDROMREADMODE1: - case CDROMREADMODE2: { - struct cdrom_msf msf; - int blocksize = 0, format = 0, lba; - - switch (cmd) { - case CDROMREADRAW: - blocksize = CD_FRAMESIZE_RAW; - break; - case CDROMREADMODE1: - blocksize = CD_FRAMESIZE; - format = 2; - break; - case CDROMREADMODE2: - blocksize = CD_FRAMESIZE_RAW0; - break; - } - IOCTL_IN(arg, struct cdrom_msf, msf); - lba = msf_to_lba(msf.cdmsf_min0,msf.cdmsf_sec0,msf.cdmsf_frame0); - /* FIXME: we need upper bound checking, too!! */ - if (lba < 0) - return -EINVAL; - cgc.buffer = (char *) kmalloc(blocksize, GFP_KERNEL); - if (cgc.buffer == NULL) - return -ENOMEM; - memset(&sense, 0, sizeof(sense)); - cgc.sense = &sense; - cgc.data_direction = CGC_DATA_READ; - ret = cdrom_read_block(cdi, &cgc, lba, 1, format, blocksize); - if (ret && sense.sense_key==0x05 && sense.asc==0x20 && sense.ascq==0x00) { - /* - * SCSI-II devices are not required to support - * READ_CD, so let's try switching block size - */ - /* FIXME: switch back again... */ - if ((ret = cdrom_switch_blocksize(cdi, blocksize))) { - kfree(cgc.buffer); - return ret; - } - cgc.sense = NULL; - ret = cdrom_read_cd(cdi, &cgc, lba, blocksize, 1); - ret |= cdrom_switch_blocksize(cdi, blocksize); - } - if (!ret && copy_to_user((char __user *)arg, cgc.buffer, blocksize)) - ret = -EFAULT; - kfree(cgc.buffer); - return ret; - } - case CDROMREADAUDIO: { - struct cdrom_read_audio ra; - int lba; - - IOCTL_IN(arg, struct cdrom_read_audio, ra); - - if (ra.addr_format == CDROM_MSF) - lba = msf_to_lba(ra.addr.msf.minute, - ra.addr.msf.second, - ra.addr.msf.frame); - else if (ra.addr_format == CDROM_LBA) - lba = ra.addr.lba; - else - return -EINVAL; - - /* FIXME: we need upper bound checking, too!! */ - if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES) - return -EINVAL; - - return cdrom_read_cdda(cdi, ra.buf, lba, ra.nframes); - } - case CDROMSUBCHNL: { - struct cdrom_subchnl q; - u_char requested, back; - 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 = cdrom_read_subchannel(cdi, &q, 0))) - 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 CDROMPLAYMSF: { - struct cdrom_msf msf; - cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); - IOCTL_IN(arg, struct cdrom_msf, msf); - cgc.cmd[0] = GPCMD_PLAY_AUDIO_MSF; - cgc.cmd[3] = msf.cdmsf_min0; - cgc.cmd[4] = msf.cdmsf_sec0; - cgc.cmd[5] = msf.cdmsf_frame0; - cgc.cmd[6] = msf.cdmsf_min1; - cgc.cmd[7] = msf.cdmsf_sec1; - cgc.cmd[8] = msf.cdmsf_frame1; - cgc.data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, &cgc); - } - case CDROMPLAYBLK: { - struct cdrom_blk blk; - cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n"); - IOCTL_IN(arg, struct cdrom_blk, blk); - cgc.cmd[0] = GPCMD_PLAY_AUDIO_10; - cgc.cmd[2] = (blk.from >> 24) & 0xff; - cgc.cmd[3] = (blk.from >> 16) & 0xff; - cgc.cmd[4] = (blk.from >> 8) & 0xff; - cgc.cmd[5] = blk.from & 0xff; - cgc.cmd[7] = (blk.len >> 8) & 0xff; - cgc.cmd[8] = blk.len & 0xff; - cgc.data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, &cgc); - } - case CDROMVOLCTRL: - case CDROMVOLREAD: { - struct cdrom_volctrl volctrl; - char mask[sizeof(buffer)]; - unsigned short offset; - - cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n"); - - IOCTL_IN(arg, struct cdrom_volctrl, volctrl); - - cgc.buffer = buffer; - cgc.buflen = 24; - 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; - } - - /* 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 */ - if (cmd == CDROMVOLREAD) { - volctrl.channel0 = buffer[offset+9]; - volctrl.channel1 = buffer[offset+11]; - volctrl.channel2 = buffer[offset+13]; - volctrl.channel3 = buffer[offset+15]; - IOCTL_OUT(arg, struct cdrom_volctrl, volctrl); - return 0; - } - - /* get the volume mask */ - cgc.buffer = mask; - if ((ret = cdrom_mode_sense(cdi, &cgc, - GPMODE_AUDIO_CTL_PAGE, 1))) - return ret; - - buffer[offset+9] = volctrl.channel0 & mask[offset+9]; - buffer[offset+11] = volctrl.channel1 & mask[offset+11]; - buffer[offset+13] = volctrl.channel2 & mask[offset+13]; - buffer[offset+15] = volctrl.channel3 & mask[offset+15]; - - /* set volume */ - cgc.buffer = buffer + offset - 8; - memset(cgc.buffer, 0, 8); - return cdrom_mode_select(cdi, &cgc); - } - - case CDROMSTART: - case CDROMSTOP: { - cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n"); - cgc.cmd[0] = GPCMD_START_STOP_UNIT; - cgc.cmd[1] = 1; - cgc.cmd[4] = (cmd == CDROMSTART) ? 1 : 0; - cgc.data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, &cgc); - } - - case CDROMPAUSE: - case CDROMRESUME: { - cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n"); - cgc.cmd[0] = GPCMD_PAUSE_RESUME; - cgc.cmd[8] = (cmd == CDROMRESUME) ? 1 : 0; - cgc.data_direction = CGC_DATA_NONE; - return cdo->generic_packet(cdi, &cgc); - } - - case DVD_READ_STRUCT: { - dvd_struct *s; - int size = sizeof(dvd_struct); - if (!CDROM_CAN(CDC_DVD)) - return -ENOSYS; - 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 __user *)arg, size)) { - kfree(s); - return -EFAULT; - } - if ((ret = dvd_read_struct(cdi, s))) { - kfree(s); - return ret; - } - if (copy_to_user((dvd_struct __user *)arg, s, size)) - ret = -EFAULT; - kfree(s); - return ret; - } - - case DVD_AUTH: { - dvd_authinfo ai; - if (!CDROM_CAN(CDC_DVD)) - return -ENOSYS; - cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n"); - IOCTL_IN(arg, dvd_authinfo, ai); - if ((ret = dvd_do_auth (cdi, &ai))) - return ret; - IOCTL_OUT(arg, dvd_authinfo, ai); - return 0; - } - - case CDROM_NEXT_WRITABLE: { - long next = 0; - cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n"); - if ((ret = cdrom_get_next_writable(cdi, &next))) - return ret; - IOCTL_OUT(arg, long, next); - return 0; - } - case CDROM_LAST_WRITTEN: { - long last = 0; - cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n"); - if ((ret = cdrom_get_last_written(cdi, &last))) - return ret; - IOCTL_OUT(arg, long, last); - return 0; - } - } /* switch */ - - return -ENOTTY; -} - -static int cdrom_get_track_info(struct cdrom_device_info *cdi, __u16 track, __u8 type, - track_information *ti) +static int cdrom_get_track_info(struct cdrom_device_info *cdi, + __u16 track, __u8 type, track_information *ti) { struct cdrom_device_ops *cdo = cdi->ops; struct packet_command cgc; @@ -2907,50 +2791,19 @@ static int cdrom_get_track_info(struct cdrom_device_info *cdi, __u16 track, __u8 cgc.cmd[8] = 8; cgc.quiet = 1; - if ((ret = cdo->generic_packet(cdi, &cgc))) + ret = cdo->generic_packet(cdi, &cgc); + if (ret) return ret; - + buflen = be16_to_cpu(ti->track_information_length) + - sizeof(ti->track_information_length); + sizeof(ti->track_information_length); if (buflen > sizeof(track_information)) buflen = sizeof(track_information); 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 packet_command cgc; - int ret, buflen; - - /* set up command and get the disc info */ - init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ); - cgc.cmd[0] = GPCMD_READ_DISC_INFO; - cgc.cmd[8] = cgc.buflen = 2; - cgc.quiet = 1; - - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - /* not all drives have the same disc_info length, so requeue - * packet with the length the drive tells us it can supply - */ - buflen = be16_to_cpu(di->disc_information_length) + - sizeof(di->disc_information_length); - - if (buflen > sizeof(disc_information)) - buflen = sizeof(disc_information); - - cgc.cmd[8] = cgc.buflen = buflen; - if ((ret = cdo->generic_packet(cdi, &cgc))) + ret = cdo->generic_packet(cdi, &cgc); + if (ret) return ret; /* return actual fill size */ @@ -2983,7 +2836,7 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written) /* if this track is blank, try the previous. */ if (ti.blank) { - if (last_track==1) + if (last_track == 1) goto use_toc; last_track--; ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); @@ -3021,7 +2874,8 @@ use_toc: } /* return the next writable block. also for udf file system. */ -static int cdrom_get_next_writable(struct cdrom_device_info *cdi, long *next_writable) +static int cdrom_get_next_writable(struct cdrom_device_info *cdi, + long *next_writable) { disc_information di; track_information ti; @@ -3042,7 +2896,7 @@ static int cdrom_get_next_writable(struct cdrom_device_info *cdi, long *next_wri if (ti_size < 0 || ti_size < offsetof(typeof(ti), track_start)) goto use_last_written; - /* if this track is blank, try the previous. */ + /* if this track is blank, try the previous. */ if (ti.blank) { if (last_track == 1) goto use_last_written; @@ -3060,7 +2914,8 @@ static int cdrom_get_next_writable(struct cdrom_device_info *cdi, long *next_wri } use_last_written: - if ((ret = cdrom_get_last_written(cdi, next_writable))) { + ret = cdrom_get_last_written(cdi, next_writable); + if (ret) { *next_writable = 0; return ret; } else { @@ -3069,6 +2924,476 @@ use_last_written: } } +static noinline int mmc_ioctl_cdrom_read_data(struct cdrom_device_info *cdi, + void __user *arg, + struct packet_command *cgc, + int cmd) +{ + struct request_sense sense; + struct cdrom_msf msf; + int blocksize = 0, format = 0, lba; + int ret; + + switch (cmd) { + case CDROMREADRAW: + blocksize = CD_FRAMESIZE_RAW; + break; + case CDROMREADMODE1: + blocksize = CD_FRAMESIZE; + format = 2; + break; + case CDROMREADMODE2: + blocksize = CD_FRAMESIZE_RAW0; + break; + } + if (copy_from_user(&msf, (struct cdrom_msf __user *)arg, sizeof(msf))) + return -EFAULT; + lba = msf_to_lba(msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0); + /* FIXME: we need upper bound checking, too!! */ + if (lba < 0) + return -EINVAL; + + cgc->buffer = kzalloc(blocksize, GFP_KERNEL); + if (cgc->buffer == NULL) + return -ENOMEM; + + memset(&sense, 0, sizeof(sense)); + cgc->sense = &sense; + cgc->data_direction = CGC_DATA_READ; + ret = cdrom_read_block(cdi, cgc, lba, 1, format, blocksize); + if (ret && sense.sense_key == 0x05 && + sense.asc == 0x20 && + sense.ascq == 0x00) { + /* + * SCSI-II devices are not required to support + * READ_CD, so let's try switching block size + */ + /* FIXME: switch back again... */ + ret = cdrom_switch_blocksize(cdi, blocksize); + if (ret) + goto out; + cgc->sense = NULL; + ret = cdrom_read_cd(cdi, cgc, lba, blocksize, 1); + ret |= cdrom_switch_blocksize(cdi, blocksize); + } + if (!ret && copy_to_user(arg, cgc->buffer, blocksize)) + ret = -EFAULT; +out: + kfree(cgc->buffer); + return ret; +} + +static noinline int mmc_ioctl_cdrom_read_audio(struct cdrom_device_info *cdi, + void __user *arg) +{ + struct cdrom_read_audio ra; + int lba; + + if (copy_from_user(&ra, (struct cdrom_read_audio __user *)arg, + sizeof(ra))) + return -EFAULT; + + if (ra.addr_format == CDROM_MSF) + lba = msf_to_lba(ra.addr.msf.minute, + ra.addr.msf.second, + ra.addr.msf.frame); + else if (ra.addr_format == CDROM_LBA) + lba = ra.addr.lba; + else + return -EINVAL; + + /* FIXME: we need upper bound checking, too!! */ + if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES) + return -EINVAL; + + return cdrom_read_cdda(cdi, ra.buf, lba, ra.nframes); +} + +static noinline int mmc_ioctl_cdrom_subchannel(struct cdrom_device_info *cdi, + void __user *arg) +{ + int ret; + struct cdrom_subchnl q; + u_char requested, back; + if (copy_from_user(&q, (struct cdrom_subchnl __user *)arg, sizeof(q))) + return -EFAULT; + requested = q.cdsc_format; + if (!((requested == CDROM_MSF) || + (requested == CDROM_LBA))) + return -EINVAL; + q.cdsc_format = CDROM_MSF; + ret = cdrom_read_subchannel(cdi, &q, 0); + 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((struct cdrom_subchnl __user *)arg, &q, sizeof(q))) + return -EFAULT; + /* cd_dbg(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ + return 0; +} + +static noinline int mmc_ioctl_cdrom_play_msf(struct cdrom_device_info *cdi, + void __user *arg, + struct packet_command *cgc) +{ + struct cdrom_device_ops *cdo = cdi->ops; + struct cdrom_msf msf; + cd_dbg(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); + if (copy_from_user(&msf, (struct cdrom_msf __user *)arg, sizeof(msf))) + return -EFAULT; + cgc->cmd[0] = GPCMD_PLAY_AUDIO_MSF; + cgc->cmd[3] = msf.cdmsf_min0; + cgc->cmd[4] = msf.cdmsf_sec0; + cgc->cmd[5] = msf.cdmsf_frame0; + cgc->cmd[6] = msf.cdmsf_min1; + cgc->cmd[7] = msf.cdmsf_sec1; + cgc->cmd[8] = msf.cdmsf_frame1; + cgc->data_direction = CGC_DATA_NONE; + return cdo->generic_packet(cdi, cgc); +} + +static noinline int mmc_ioctl_cdrom_play_blk(struct cdrom_device_info *cdi, + void __user *arg, + struct packet_command *cgc) +{ + struct cdrom_device_ops *cdo = cdi->ops; + struct cdrom_blk blk; + cd_dbg(CD_DO_IOCTL, "entering CDROMPLAYBLK\n"); + if (copy_from_user(&blk, (struct cdrom_blk __user *)arg, sizeof(blk))) + return -EFAULT; + cgc->cmd[0] = GPCMD_PLAY_AUDIO_10; + cgc->cmd[2] = (blk.from >> 24) & 0xff; + cgc->cmd[3] = (blk.from >> 16) & 0xff; + cgc->cmd[4] = (blk.from >> 8) & 0xff; + cgc->cmd[5] = blk.from & 0xff; + cgc->cmd[7] = (blk.len >> 8) & 0xff; + cgc->cmd[8] = blk.len & 0xff; + cgc->data_direction = CGC_DATA_NONE; + return cdo->generic_packet(cdi, cgc); +} + +static noinline int mmc_ioctl_cdrom_volume(struct cdrom_device_info *cdi, + void __user *arg, + struct packet_command *cgc, + unsigned int cmd) +{ + struct cdrom_volctrl volctrl; + unsigned char buffer[32]; + char mask[sizeof(buffer)]; + unsigned short offset; + int ret; + + cd_dbg(CD_DO_IOCTL, "entering CDROMVOLUME\n"); + + if (copy_from_user(&volctrl, (struct cdrom_volctrl __user *)arg, + sizeof(volctrl))) + return -EFAULT; + + cgc->buffer = buffer; + cgc->buflen = 24; + ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 0); + if (ret) + 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; + } + + /* 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 */ + if (cmd == CDROMVOLREAD) { + volctrl.channel0 = buffer[offset+9]; + volctrl.channel1 = buffer[offset+11]; + volctrl.channel2 = buffer[offset+13]; + volctrl.channel3 = buffer[offset+15]; + if (copy_to_user((struct cdrom_volctrl __user *)arg, &volctrl, + sizeof(volctrl))) + return -EFAULT; + return 0; + } + + /* get the volume mask */ + cgc->buffer = mask; + ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 1); + if (ret) + return ret; + + buffer[offset + 9] = volctrl.channel0 & mask[offset + 9]; + buffer[offset + 11] = volctrl.channel1 & mask[offset + 11]; + buffer[offset + 13] = volctrl.channel2 & mask[offset + 13]; + buffer[offset + 15] = volctrl.channel3 & mask[offset + 15]; + + /* set volume */ + cgc->buffer = buffer + offset - 8; + memset(cgc->buffer, 0, 8); + return cdrom_mode_select(cdi, cgc); +} + +static noinline int mmc_ioctl_cdrom_start_stop(struct cdrom_device_info *cdi, + struct packet_command *cgc, + int cmd) +{ + struct cdrom_device_ops *cdo = cdi->ops; + cd_dbg(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n"); + cgc->cmd[0] = GPCMD_START_STOP_UNIT; + cgc->cmd[1] = 1; + cgc->cmd[4] = (cmd == CDROMSTART) ? 1 : 0; + cgc->data_direction = CGC_DATA_NONE; + return cdo->generic_packet(cdi, cgc); +} + +static noinline int mmc_ioctl_cdrom_pause_resume(struct cdrom_device_info *cdi, + struct packet_command *cgc, + int cmd) +{ + struct cdrom_device_ops *cdo = cdi->ops; + cd_dbg(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n"); + cgc->cmd[0] = GPCMD_PAUSE_RESUME; + cgc->cmd[8] = (cmd == CDROMRESUME) ? 1 : 0; + cgc->data_direction = CGC_DATA_NONE; + return cdo->generic_packet(cdi, cgc); +} + +static noinline int mmc_ioctl_dvd_read_struct(struct cdrom_device_info *cdi, + void __user *arg, + struct packet_command *cgc) +{ + int ret; + dvd_struct *s; + int size = sizeof(dvd_struct); + + if (!CDROM_CAN(CDC_DVD)) + return -ENOSYS; + + s = kmalloc(size, GFP_KERNEL); + if (!s) + return -ENOMEM; + + cd_dbg(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n"); + if (copy_from_user(s, arg, size)) { + kfree(s); + return -EFAULT; + } + + ret = dvd_read_struct(cdi, s, cgc); + if (ret) + goto out; + + if (copy_to_user(arg, s, size)) + ret = -EFAULT; +out: + kfree(s); + return ret; +} + +static noinline int mmc_ioctl_dvd_auth(struct cdrom_device_info *cdi, + void __user *arg) +{ + int ret; + dvd_authinfo ai; + if (!CDROM_CAN(CDC_DVD)) + return -ENOSYS; + cd_dbg(CD_DO_IOCTL, "entering DVD_AUTH\n"); + if (copy_from_user(&ai, (dvd_authinfo __user *)arg, sizeof(ai))) + return -EFAULT; + ret = dvd_do_auth(cdi, &ai); + if (ret) + return ret; + if (copy_to_user((dvd_authinfo __user *)arg, &ai, sizeof(ai))) + return -EFAULT; + return 0; +} + +static noinline int mmc_ioctl_cdrom_next_writable(struct cdrom_device_info *cdi, + void __user *arg) +{ + int ret; + long next = 0; + cd_dbg(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n"); + ret = cdrom_get_next_writable(cdi, &next); + if (ret) + return ret; + if (copy_to_user((long __user *)arg, &next, sizeof(next))) + return -EFAULT; + return 0; +} + +static noinline int mmc_ioctl_cdrom_last_written(struct cdrom_device_info *cdi, + void __user *arg) +{ + int ret; + long last = 0; + cd_dbg(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n"); + ret = cdrom_get_last_written(cdi, &last); + if (ret) + return ret; + if (copy_to_user((long __user *)arg, &last, sizeof(last))) + return -EFAULT; + return 0; +} + +static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, + unsigned long arg) +{ + struct packet_command cgc; + void __user *userptr = (void __user *)arg; + + memset(&cgc, 0, sizeof(cgc)); + + /* build a unified command and queue it through + cdo->generic_packet() */ + switch (cmd) { + case CDROMREADRAW: + case CDROMREADMODE1: + case CDROMREADMODE2: + return mmc_ioctl_cdrom_read_data(cdi, userptr, &cgc, cmd); + case CDROMREADAUDIO: + return mmc_ioctl_cdrom_read_audio(cdi, userptr); + case CDROMSUBCHNL: + return mmc_ioctl_cdrom_subchannel(cdi, userptr); + case CDROMPLAYMSF: + return mmc_ioctl_cdrom_play_msf(cdi, userptr, &cgc); + case CDROMPLAYBLK: + return mmc_ioctl_cdrom_play_blk(cdi, userptr, &cgc); + case CDROMVOLCTRL: + case CDROMVOLREAD: + return mmc_ioctl_cdrom_volume(cdi, userptr, &cgc, cmd); + case CDROMSTART: + case CDROMSTOP: + return mmc_ioctl_cdrom_start_stop(cdi, &cgc, cmd); + case CDROMPAUSE: + case CDROMRESUME: + return mmc_ioctl_cdrom_pause_resume(cdi, &cgc, cmd); + case DVD_READ_STRUCT: + return mmc_ioctl_dvd_read_struct(cdi, userptr, &cgc); + case DVD_AUTH: + return mmc_ioctl_dvd_auth(cdi, userptr); + case CDROM_NEXT_WRITABLE: + return mmc_ioctl_cdrom_next_writable(cdi, userptr); + case CDROM_LAST_WRITTEN: + return mmc_ioctl_cdrom_last_written(cdi, userptr); + } + + return -ENOTTY; +} + +/* + * 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 cdrom_device_info *cdi, struct block_device *bdev, + fmode_t mode, 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_blk_ioctl(bdev, mode, 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, 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) + return ret; + } + + /* + * Note: most of the cd_dbg() calls are commented out here, + * because they fill up the sys log when CD players poll + * the drive. + */ + switch (cmd) { + 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: + return cdrom_ioctl_audioctl(cdi, cmd); + } + + return -ENOSYS; +} + EXPORT_SYMBOL(cdrom_get_last_written); EXPORT_SYMBOL(register_cdrom); EXPORT_SYMBOL(unregister_cdrom); @@ -3095,103 +3420,137 @@ static struct cdrom_sysctl_settings { int check; /* check media type */ } cdrom_sysctl_settings; -static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp, - void __user *buffer, size_t *lenp, loff_t *ppos) +enum cdrom_print_option { + CTL_NAME, + CTL_SPEED, + CTL_SLOTS, + CTL_CAPABILITY +}; + +static int cdrom_print_info(const char *header, int val, char *info, + int *pos, enum cdrom_print_option option) { - int pos; + const int max_size = sizeof(cdrom_sysctl_settings.info); struct cdrom_device_info *cdi; + int ret; + + ret = scnprintf(info + *pos, max_size - *pos, header); + if (!ret) + return 1; + + *pos += ret; + + list_for_each_entry(cdi, &cdrom_list, list) { + switch (option) { + case CTL_NAME: + ret = scnprintf(info + *pos, max_size - *pos, + "\t%s", cdi->name); + break; + case CTL_SPEED: + ret = scnprintf(info + *pos, max_size - *pos, + "\t%d", cdi->speed); + break; + case CTL_SLOTS: + ret = scnprintf(info + *pos, max_size - *pos, + "\t%d", cdi->capacity); + break; + case CTL_CAPABILITY: + ret = scnprintf(info + *pos, max_size - *pos, + "\t%d", CDROM_CAN(val) != 0); + break; + default: + pr_info("invalid option%d\n", option); + return 1; + } + if (!ret) + return 1; + *pos += ret; + } + + return 0; +} + +static int cdrom_sysctl_info(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int pos; char *info = cdrom_sysctl_settings.info; + const int max_size = sizeof(cdrom_sysctl_settings.info); if (!*lenp || (*ppos && !write)) { *lenp = 0; return 0; } + mutex_lock(&cdrom_mutex); + pos = sprintf(info, "CD-ROM information, " VERSION "\n"); - pos += sprintf(info+pos, "\ndrive name:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%s", cdi->name); - - pos += sprintf(info+pos, "\ndrive speed:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", cdi->speed); - - pos += sprintf(info+pos, "\ndrive # of slots:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", cdi->capacity); - - pos += sprintf(info+pos, "\nCan close tray:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_CLOSE_TRAY) != 0); - - pos += sprintf(info+pos, "\nCan open tray:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_OPEN_TRAY) != 0); - - pos += sprintf(info+pos, "\nCan lock tray:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_LOCK) != 0); - - pos += sprintf(info+pos, "\nCan change speed:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_SELECT_SPEED) != 0); - - pos += sprintf(info+pos, "\nCan select disk:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_SELECT_DISC) != 0); - - pos += sprintf(info+pos, "\nCan read multisession:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MULTI_SESSION) != 0); - - pos += sprintf(info+pos, "\nCan read MCN:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MCN) != 0); - - pos += sprintf(info+pos, "\nReports media changed:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MEDIA_CHANGED) != 0); - - pos += sprintf(info+pos, "\nCan play audio:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_PLAY_AUDIO) != 0); - - pos += sprintf(info+pos, "\nCan write CD-R:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_CD_R) != 0); - - pos += sprintf(info+pos, "\nCan write CD-RW:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_CD_RW) != 0); - - pos += sprintf(info+pos, "\nCan read DVD:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD) != 0); - - pos += sprintf(info+pos, "\nCan write DVD-R:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD_R) != 0); - - pos += sprintf(info+pos, "\nCan write DVD-RAM:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD_RAM) != 0); - - pos += sprintf(info+pos, "\nCan read MRW:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MRW) != 0); - - pos += sprintf(info+pos, "\nCan write MRW:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MRW_W) != 0); - - pos += sprintf(info+pos, "\nCan write RAM:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_RAM) != 0); - - strcpy(info+pos,"\n\n"); - - return proc_dostring(ctl, write, filp, buffer, lenp, ppos); + if (cdrom_print_info("\ndrive name:\t", 0, info, &pos, CTL_NAME)) + goto done; + if (cdrom_print_info("\ndrive speed:\t", 0, info, &pos, CTL_SPEED)) + goto done; + if (cdrom_print_info("\ndrive # of slots:", 0, info, &pos, CTL_SLOTS)) + goto done; + if (cdrom_print_info("\nCan close tray:\t", + CDC_CLOSE_TRAY, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan open tray:\t", + CDC_OPEN_TRAY, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan lock tray:\t", + CDC_LOCK, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan change speed:", + CDC_SELECT_SPEED, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan select disk:", + CDC_SELECT_DISC, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan read multisession:", + CDC_MULTI_SESSION, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan read MCN:\t", + CDC_MCN, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nReports media changed:", + CDC_MEDIA_CHANGED, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan play audio:\t", + CDC_PLAY_AUDIO, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write CD-R:\t", + CDC_CD_R, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write CD-RW:", + CDC_CD_RW, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan read DVD:\t", + CDC_DVD, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write DVD-R:", + CDC_DVD_R, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write DVD-RAM:", + CDC_DVD_RAM, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan read MRW:\t", + CDC_MRW, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write MRW:\t", + CDC_MRW_W, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write RAM:\t", + CDC_RAM, info, &pos, CTL_CAPABILITY)) + goto done; + if (!scnprintf(info + pos, max_size - pos, "\n\n")) + goto done; +doit: + mutex_unlock(&cdrom_mutex); + return proc_dostring(ctl, write, buffer, lenp, ppos); +done: + pr_info("info buffer too small\n"); + goto doit; } /* Unfortunately, per device settings are not implemented through @@ -3202,7 +3561,8 @@ static void cdrom_update_settings(void) { struct cdrom_device_info *cdi; - for (cdi = topCdromPtr; cdi != NULL; cdi = cdi->next) { + mutex_lock(&cdrom_mutex); + list_for_each_entry(cdi, &cdrom_list, list) { if (autoclose && CDROM_CAN(CDC_CLOSE_TRAY)) cdi->options |= CDO_AUTO_CLOSE; else if (!autoclose) @@ -3220,52 +3580,25 @@ static void cdrom_update_settings(void) else cdi->options &= ~CDO_CHECK_TYPE; } + mutex_unlock(&cdrom_mutex); } -static int cdrom_sysctl_handler(ctl_table *ctl, int write, struct file * filp, +static int cdrom_sysctl_handler(struct ctl_table *ctl, int write, 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, ppos); + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); - if (write && *valp != val) { + if (write) { /* we only care for 1 or 0. */ - if (*valp) - *valp = 1; - else - *valp = 0; + autoclose = !!cdrom_sysctl_settings.autoclose; + autoeject = !!cdrom_sysctl_settings.autoeject; + debug = !!cdrom_sysctl_settings.debug; + lockdoor = !!cdrom_sysctl_settings.lock; + check_media_type = !!cdrom_sysctl_settings.check; - switch (ctl->ctl_name) { - case DEV_CDROM_AUTOCLOSE: { - if (valp == &cdrom_sysctl_settings.autoclose) - autoclose = cdrom_sysctl_settings.autoclose; - break; - } - case DEV_CDROM_AUTOEJECT: { - if (valp == &cdrom_sysctl_settings.autoeject) - autoeject = cdrom_sysctl_settings.autoeject; - break; - } - case DEV_CDROM_DEBUG: { - if (valp == &cdrom_sysctl_settings.debug) - debug = cdrom_sysctl_settings.debug; - break; - } - case DEV_CDROM_LOCK: { - if (valp == &cdrom_sysctl_settings.lock) - lockdoor = cdrom_sysctl_settings.lock; - break; - } - case DEV_CDROM_CHECK_MEDIA: { - if (valp == &cdrom_sysctl_settings.check) - check_media_type = cdrom_sysctl_settings.check; - break; - } - } /* update the option flags according to the changes. we don't have per device options through sysctl yet, but we will have and then this will disappear. */ @@ -3276,79 +3609,71 @@ static int cdrom_sysctl_handler(ctl_table *ctl, int write, struct file * filp, } /* Place files in /proc/sys/dev/cdrom */ -static ctl_table cdrom_table[] = { +static struct ctl_table cdrom_table[] = { { - .ctl_name = DEV_CDROM_INFO, .procname = "info", .data = &cdrom_sysctl_settings.info, .maxlen = CDROM_STR_SIZE, .mode = 0444, - .proc_handler = &cdrom_sysctl_info, + .proc_handler = cdrom_sysctl_info, }, { - .ctl_name = DEV_CDROM_AUTOCLOSE, .procname = "autoclose", .data = &cdrom_sysctl_settings.autoclose, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &cdrom_sysctl_handler, + .proc_handler = cdrom_sysctl_handler, }, { - .ctl_name = DEV_CDROM_AUTOEJECT, .procname = "autoeject", .data = &cdrom_sysctl_settings.autoeject, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &cdrom_sysctl_handler, + .proc_handler = cdrom_sysctl_handler, }, { - .ctl_name = DEV_CDROM_DEBUG, .procname = "debug", .data = &cdrom_sysctl_settings.debug, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &cdrom_sysctl_handler, + .proc_handler = cdrom_sysctl_handler, }, { - .ctl_name = DEV_CDROM_LOCK, .procname = "lock", .data = &cdrom_sysctl_settings.lock, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &cdrom_sysctl_handler, + .proc_handler = cdrom_sysctl_handler, }, { - .ctl_name = DEV_CDROM_CHECK_MEDIA, .procname = "check_media", .data = &cdrom_sysctl_settings.check, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &cdrom_sysctl_handler + .proc_handler = cdrom_sysctl_handler }, - { .ctl_name = 0 } + { } }; -static ctl_table cdrom_cdrom_table[] = { +static struct ctl_table cdrom_cdrom_table[] = { { - .ctl_name = DEV_CDROM, .procname = "cdrom", .maxlen = 0, .mode = 0555, .child = cdrom_table, }, - { .ctl_name = 0 } + { } }; /* Make sure that /proc/sys/dev is there */ -static ctl_table cdrom_root_table[] = { +static struct ctl_table cdrom_root_table[] = { { - .ctl_name = CTL_DEV, .procname = "dev", .maxlen = 0, .mode = 0555, .child = cdrom_cdrom_table, }, - { .ctl_name = 0 } + { } }; static struct ctl_table_header *cdrom_sysctl_header; @@ -3359,9 +3684,7 @@ static void cdrom_sysctl_register(void) if (initialized == 1) return; - cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 1); - if (cdrom_root_table->ctl_name && cdrom_root_table->child->de) - cdrom_root_table->child->de->owner = THIS_MODULE; + cdrom_sysctl_header = register_sysctl_table(cdrom_root_table); /* set the defaults */ cdrom_sysctl_settings.autoclose = autoclose; @@ -3379,22 +3702,29 @@ static void cdrom_sysctl_unregister(void) unregister_sysctl_table(cdrom_sysctl_header); } +#else /* CONFIG_SYSCTL */ + +static void cdrom_sysctl_register(void) +{ +} + +static void cdrom_sysctl_unregister(void) +{ +} + #endif /* CONFIG_SYSCTL */ static int __init cdrom_init(void) { -#ifdef CONFIG_SYSCTL cdrom_sysctl_register(); -#endif + return 0; } static void __exit cdrom_exit(void) { - printk(KERN_INFO "Uniform CD-ROM driver unloaded\n"); -#ifdef CONFIG_SYSCTL + pr_info("Uniform CD-ROM driver unloaded\n"); cdrom_sysctl_unregister(); -#endif } module_init(cdrom_init); |
