#define REVISION "Revision: 3.20" #define VERSION "Id: cdrom.c 3.20 2003/12/17"
/* I use an error-log mask to give fine grain control over the type of
messages dumped to the system logs. The available masks include: */ #define CD_NOTHING 0x0 #define CD_WARNING 0x1 #define CD_REG_UNREG 0x2 #define CD_DO_IOCTL 0x4 #define CD_OPEN 0x8 #define CD_CLOSE 0x10 #define CD_COUNT_TRACKS 0x20 #define CD_CHANGER 0x40 #define CD_DVD 0x80
/* Define this to remove _all_ the debugging messages */ /* #define ERRLOGMASK CD_NOTHING */ #define ERRLOGMASK CD_WARNING /* #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) */
#if (ERRLOGMASK != CD_NOTHING) #define cd_dbg(type, fmt, ...) \ do { \ if ((ERRLOGMASK & type) || debug == 1) \
pr_debug(fmt, ##__VA_ARGS__); \
} while (0) #else #define cd_dbg(type, fmt, ...) \ do { \ if (0 && (ERRLOGMASK & type) || debug == 1) \
pr_debug(fmt, ##__VA_ARGS__); \
} while (0) #endif
/* 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))
/* 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;
ret = cdo->generic_packet(cdi, &cgc); if (ret) return ret;
/* not all drives have the same disc_info length, so requeue *packetwiththelengththedrivetellsusitcansupply
*/
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;
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 *existenceintherun-timeroutinesbelow.Change_capabilityisa *hacktohavethecapabilityflagsdefinedconst,whilewecanstill *changeitherewithoutgcccomplainingateveryline.
*/ #define ENSURE(cdo, call, bits) \ do { \ if (cdo->call == NULL) \
WARN_ON_ONCE((cdo)->capability & (bits)); \
} while (0)
/* *returns0foroktoopenwrite,non-0todisallow
*/ staticint cdrom_open_write(struct cdrom_device_info *cdi)
{ int mrw, mrw_write, ram_write; int ret = 1;
mrw = 0; if (!cdrom_is_mrw(cdi, &mrw_write))
mrw = 1;
if (CDROM_CAN(CDC_MO_DRIVE))
ram_write = 1; else
(void) cdrom_is_random_writable(cdi, &ram_write);
if (mrw)
cdi->mask &= ~CDC_MRW; else
cdi->mask |= CDC_MRW;
if (mrw_write)
cdi->mask &= ~CDC_MRW_W; else
cdi->mask |= CDC_MRW_W;
if (ram_write)
cdi->mask &= ~CDC_RAM; else
cdi->mask |= CDC_RAM;
if (CDROM_CAN(CDC_MRW_W))
ret = cdrom_mrw_open_write(cdi); elseif (CDROM_CAN(CDC_DVD_RAM))
ret = cdrom_dvdram_open_write(cdi); elseif (CDROM_CAN(CDC_RAM) &&
!CDROM_CAN(CDC_CD_R|CDC_CD_RW|CDC_DVD|CDC_DVD_R|CDC_MRW|CDC_MO_DRIVE))
ret = cdrom_ram_open_write(cdi); elseif (CDROM_CAN(CDC_MO_DRIVE))
ret = mo_open_write(cdi); elseif (!cdrom_is_dvd_rw(cdi))
ret = 0;
/* badly broken, I know. Is due for a fixup anytime. */ staticvoid 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;
cd_dbg(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 */
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++; elseif (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);
}
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 ret; conststruct cdrom_device_ops *cdo = cdi->ops;
tracktype tracks;
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);
cd_dbg(CD_OPEN, "drive_status=%d\n", ret); if (ret == CDS_TRAY_OPEN) {
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) {
cd_dbg(CD_OPEN, "trying to close the tray\n");
ret=cdo->tray_move(cdi,0); if (ret) {
cd_dbg(CD_OPEN, "bummer. tried to close the tray but failed.\n"); /* Ignore the error from the low leveldriver.Wedon'tcarewhyit couldn'tclosethetray.Weonlycare thatthereisnodiscinthedrive,
since that is the _REAL_ problem here.*/
ret=-ENOMEDIUM; goto clean_up_and_return;
}
} else {
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)) {
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;
}
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); if (ret!=CDS_DISC_OK) {
ret = -ENOMEDIUM; goto clean_up_and_return;
}
}
cdrom_count_tracks(cdi, &tracks); if (tracks.error == CDS_NO_DISC) {
cd_dbg(CD_OPEN, "bummer. no disc.\n");
ret=-ENOMEDIUM; goto clean_up_and_return;
} /* CD-Players which don't use O_NONBLOCK, workman
* for example, need bit CDO_CHECK_TYPE cleared! */ if (tracks.data==0) { if (cdi->options & CDO_CHECK_TYPE) { /* give people a warning shot, now that CDO_CHECK_TYPE
is the default case! */
cd_dbg(CD_OPEN, "bummer. wrong media type.\n");
cd_dbg(CD_WARNING, "pid %d must open device O_NONBLOCK!\n",
(unsignedint)task_pid_nr(current));
ret=-EMEDIUMTYPE; goto clean_up_and_return;
} else {
cd_dbg(CD_OPEN, "wrong media type, but CDO_CHECK_TYPE not set\n");
}
}
cd_dbg(CD_OPEN, "all seems well, opening the device\n");
/* all seems well, we can open the device */
ret = cdo->open(cdi, 0); /* open for data */
cd_dbg(CD_OPEN, "opening the device gave me %d\n", ret); /* After all this careful checking, we shouldn't have problems openingthedevice,butwedon'twantthedevicelockedif
this somehow fails... */ if (ret) {
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);
cd_dbg(CD_OPEN, "door locked\n");
}
cd_dbg(CD_OPEN, "device opened successfully\n"); return ret;
/* Something failed. Try to unlock the drive, because some drivers (notablyide-cd)lockthedriveaftereverycommand.Thisproduced anastybugwhereaftermountfailed,thedrivewouldremainlocked! Thisensuresthatthedrivegetsunlockedafteramountfails.This
is a goto to avoid bloating the driver with redundant code. */
clean_up_and_return:
cd_dbg(CD_OPEN, "open failed\n"); if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) {
cdo->lock_door(cdi, 0);
cd_dbg(CD_OPEN, "door unlocked\n");
} return ret;
}
/* We use the open-option O_NONBLOCK to indicate that the *purposeofopeningisonlyforsubsequentioctl()calls;nodevice *integritychecksareperformed. * *Wehopethatallcd-playerprogramswilladoptthisconvention.It *isintheirowninterest:devicecontrolbecomesaloteasier *thisway.
*/ int cdrom_open(struct cdrom_device_info *cdi, blk_mode_t mode)
{ int ret;
cd_dbg(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 ((mode & BLK_OPEN_NDELAY) && (cdi->options & CDO_USE_FFLAGS)) {
ret = cdi->ops->open(cdi, 1);
} else {
ret = open_for_data(cdi); if (ret) goto err; if (CDROM_CAN(CDC_GENERIC_PACKET))
cdrom_mmc3_profile(cdi); if (mode & BLK_OPEN_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;
}
cdi->opened_for_data = true;
}
/* This code is similar to that in open_for_data. The routine is called wheneveranaudioplayoperationisrequested.
*/ staticint check_for_audio_disc(struct cdrom_device_info *cdi, conststruct cdrom_device_ops *cdo)
{ int ret;
tracktype tracks;
cd_dbg(CD_OPEN, "entering check_for_audio_disc\n"); if (!(cdi->options & CDO_CHECK_TYPE)) return0; if (cdo->drive_status != NULL) {
ret = cdo->drive_status(cdi, CDSL_CURRENT);
cd_dbg(CD_OPEN, "drive_status=%d\n", ret); if (ret == CDS_TRAY_OPEN) {
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) {
cd_dbg(CD_OPEN, "trying to close the tray\n");
ret=cdo->tray_move(cdi,0); if (ret) {
cd_dbg(CD_OPEN, "bummer. tried to close tray but failed.\n"); /* Ignore the error from the low leveldriver.Wedon'tcarewhyit couldn'tclosethetray.Weonlycare thatthereisnodiscinthedrive,
since that is the _REAL_ problem here.*/ return -ENOMEDIUM;
}
} else {
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)) {
cd_dbg(CD_OPEN, "bummer. the tray is still not closed.\n"); return -ENOMEDIUM;
} if (ret!=CDS_DISC_OK) {
cd_dbg(CD_OPEN, "bummer. disc isn't ready.\n"); return -EIO;
}
cd_dbg(CD_OPEN, "the tray is now closed\n");
}
}
cdrom_count_tracks(cdi, &tracks); if (tracks.error) return(tracks.error);
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 (CDROM_CAN(CDC_MRW_W))
cdrom_mrw_exit(cdi);
staticint cdrom_slot_status(struct cdrom_device_info *cdi, int slot)
{ struct cdrom_changer_info *info; int ret;
cd_dbg(CD_CHANGER, "entering cdrom_slot_status()\n"); if (cdi->sanyo_slot) return CDS_NO_INFO;
info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM;
if ((ret = cdrom_read_mech_status(cdi, info))) goto out_free;
if (info->slots[slot].disc_present)
ret = CDS_DISC_OK; else
ret = CDS_NO_DISC;
out_free:
kfree(info); return ret;
}
/* Return the number of slots for an ATAPI/SCSI cdrom, *return1ifnotachanger.
*/ int cdrom_number_of_slots(struct cdrom_device_info *cdi)
{ int nslots = 1; struct cdrom_changer_info *info;
cd_dbg(CD_CHANGER, "entering cdrom_number_of_slots()\n"); /* cdrom_read_mech_status requires a valid value for capacity: */
cdi->capacity = 0;
info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM;
if (cdrom_read_mech_status(cdi, info) == 0)
nslots = info->hdr.nslots;
/* If SLOT < 0, unload the current slot. Otherwise, try to load SLOT. */ staticint cdrom_load_unload(struct cdrom_device_info *cdi, int slot)
{ struct packet_command cgc;
cd_dbg(CD_CHANGER, "entering cdrom_load_unload()\n"); if (cdi->sanyo_slot && slot < 0) return0;
/* Specifying CDSL_CURRENT will attempt to load the currnet slot, whichisusefulifithadbeenpreviouslyunloaded. Whetheritcanornot,itreturnsthecurrentslot. Similarly,ifslothappenstobethecurrentone,westill
try and load it. */ if (slot == CDSL_CURRENT)
slot = curslot;
/* set media changed bits on both queues */
signal_media_change(cdi); if ((ret = cdrom_load_unload(cdi, slot))) return ret;
/* We want to make media_changed accessible to the user through an *ioctl.Themainproblemnowisthatwemustdouble-bufferthe *low-levelimplementation,toassurethattheVFSandtheuserboth *seeamediumchangeonce.
*/
static int media_changed(struct cdrom_device_info *cdi, int queue)
{ unsignedint mask = (1 << (queue & 1)); int ret = !!(cdi->mc_flags & mask); bool changed;
if (!CDROM_CAN(CDC_MEDIA_CHANGED)) return ret;
/* changed since last call? */
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;
if (changed) {
signal_media_change(cdi);
ret |= 1;
cdi->media_written = 0;
}
cdi->mc_flags &= ~mask; /* clear bit */ return ret;
}
/* Requests to the low-level drivers will /always/ be done in the followingformatconvention:
/* very generic interface for reading the various types of blocks */ staticint cdrom_read_block(struct cdrom_device_info *cdi, struct packet_command *cgc, int lba, int nblocks, int format, int blksize)
{ conststruct cdrom_device_ops *cdo = cdi->ops;
/* set the header info returned */ switch (blksize) { case CD_FRAMESIZE_RAW0 : cgc->cmd[9] = 0x58; break; case CD_FRAMESIZE_RAW1 : cgc->cmd[9] = 0x78; break; case CD_FRAMESIZE_RAW : cgc->cmd[9] = 0xf8; break; default : cgc->cmd[9] = 0x10;
}
return cdo->generic_packet(cdi, cgc);
}
staticint cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, int lba, int nframes)
{ struct packet_command cgc; int ret = 0; int nr;
cdi->last_sense = 0;
memset(&cgc, 0, sizeof(cgc));
/* *startwithwillra.nframessize,backdownifallocfails
*/
nr = nframes; do {
cgc.buffer = kmalloc_array(nr, CD_FRAMESIZE_RAW, GFP_KERNEL); if (cgc.buffer) break;
nr >>= 1;
} while (nr);
if (!nr) return -ENOMEM;
cgc.data_direction = CGC_DATA_READ; while (nframes > 0) { if (nr > nframes)
nr = nframes;
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)) {
ret = -EFAULT; break;
}
ubuf += CD_FRAMESIZE_RAW * nr;
nframes -= nr;
lba += nr;
}
kfree(cgc.buffer); return ret;
}
staticint cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, int lba, int nframes)
{ int max_frames = (queue_max_sectors(cdi->disk->queue) << 9) /
CD_FRAMESIZE_RAW; int nr, ret = 0;
cdi->last_sense = 0;
while (nframes) { if (cdi->cdda_method == CDDA_BPC_SINGLE)
nr = 1; else
nr = min(nframes, max_frames);
ret = cdi->ops->read_cdda_bpc(cdi, ubuf, lba, nr,
&cdi->last_sense); if (ret) break;
if (copy_from_user(&info, argp, sizeof(info))) return -EFAULT;
ret = cdrom_multisession(cdi, &info); if (ret) return ret; if (copy_to_user(argp, &info, sizeof(info))) return -EFAULT;
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;
}
if (buflen > sizeof(track_information))
buflen = sizeof(track_information);
cgc.cmd[8] = cgc.buflen = buflen;
ret = cdo->generic_packet(cdi, &cgc); if (ret) return ret;
/* return actual fill size */ return buflen;
}
/* return the last written block on the CD-R media. this is for the udf
file system. */ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written)
{ struct cdrom_tocentry toc;
disc_information di;
track_information ti;
__u32 last_track; int ret = -1, ti_size;
if (!CDROM_CAN(CDC_GENERIC_PACKET)) goto use_toc;
ret = cdrom_get_disc_info(cdi, &di); if (ret < (int)(offsetof(typeof(di), last_track_lsb)
+ sizeof(di.last_track_lsb))) goto use_toc;
/* if unit didn't return msb, it's zeroed by cdrom_get_disc_info */
last_track = (di.last_track_msb << 8) | di.last_track_lsb;
ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); if (ti_size < (int)offsetof(typeof(ti), track_start)) goto use_toc;
/* if this track is blank, try the previous. */ if (ti.blank) { if (last_track == 1) goto use_toc;
last_track--;
ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti);
}
if (ti_size < (int)(offsetof(typeof(ti), track_size)
+ sizeof(ti.track_size))) goto use_toc;
/* if last recorded field is valid, return it. */ if (ti.lra_v && ti_size >= (int)(offsetof(typeof(ti), last_rec_address)
+ sizeof(ti.last_rec_address))) {
*last_written = be32_to_cpu(ti.last_rec_address);
} else { /* make it up instead */
*last_written = be32_to_cpu(ti.track_start) +
be32_to_cpu(ti.track_size); if (ti.free_blocks)
*last_written -= (be32_to_cpu(ti.free_blocks) + 7);
} return0;
/* this is where we end up if the drive either can't do a GPCMD_READ_DISC_INFOorGPCMD_READ_TRACK_RZONE_INFOorif itdoesn'tgiveenoughinformationorfails.thenwereturn
the toc contents. */
use_toc: if (!CDROM_CAN(CDC_PLAY_AUDIO)) return -ENOSYS;
/* return the next writable block. also for udf file system. */ staticint cdrom_get_next_writable(struct cdrom_device_info *cdi, long *next_writable)
{
disc_information di;
track_information ti;
__u16 last_track; int ret, ti_size;
if (!CDROM_CAN(CDC_GENERIC_PACKET)) goto use_last_written;
ret = cdrom_get_disc_info(cdi, &di); if (ret < 0 || ret < offsetof(typeof(di), last_track_lsb)
+ sizeof(di.last_track_lsb)) goto use_last_written;
/* if unit didn't return msb, it's zeroed by cdrom_get_disc_info */
last_track = (di.last_track_msb << 8) | di.last_track_lsb;
ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); if (ti_size < 0 || ti_size < offsetof(typeof(ti), track_start)) goto use_last_written;
/* if this track is blank, try the previous. */ if (ti.blank) { if (last_track == 1) goto use_last_written;
last_track--;
ti_size = cdrom_get_track_info(cdi, last_track, 1, &ti); if (ti_size < 0) goto use_last_written;
}
/* if next recordable address field is valid, use it. */ if (ti.nwa_v && ti_size >= offsetof(typeof(ti), next_writable)
+ sizeof(ti.next_writable)) {
*next_writable = be32_to_cpu(ti.next_writable); return0;
}
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 howmuchdataisavailablefortransfer.buffer[1]is unfortunatelyambigiousandtheonlyreliablewayseem
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;
}
/* 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; return0;
}
/* get the volume mask */
cgc->buffer = mask;
ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 1); if (ret) return ret;
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; return0;
}
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; return0;
}
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 (in_compat_syscall()) return put_user(last, (__s32 __user *)arg);
/* 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);
}
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_TIMED_MEDIA_CHANGE: return cdrom_ioctl_timed_media_change(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);
}
/* *Usetheioctlsthatareimplementedthroughthegeneric_packet() *interface.thismaylookatbitfunny,butif-ENOTTYis *returnedthatparticularioctlisnotimplementedandwe *letitgothroughthedevicespecificones.
*/ if (CDROM_CAN(CDC_GENERIC_PACKET)) {
ret = mmc_ioctl(cdi, cmd, arg); if (ret != -ENOTTY) return ret;
}
/* *Note:mostofthecd_dbg()callsarecommentedouthere, *becausetheyfillupthesyslogwhenCDplayerspoll *thedrive.
*/ 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_ioctl);
#ifdef CONFIG_SYSCTL
#define CDROM_STR_SIZE 1000
staticstruct cdrom_sysctl_settings { char info[CDROM_STR_SIZE]; /* general info */ int autoclose; /* close tray upon mount, etc */ int autoeject; /* eject on umount */ int debug; /* turn on debugging messages */ int lock; /* lock the door on device open */ int check; /* check media type */
} cdrom_sysctl_settings;
pos = sprintf(info, "CD-ROM information, " VERSION "\n");
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 procfs/sysctlyet.Whentheyare,thiswillnaturallydisappear.Fornow justupdatealldrives.Laterthiswillbecomethetemplateonwhich
new registered drives will be based. */ staticvoid cdrom_update_settings(void)
{ struct cdrom_device_info *cdi;
staticint cdrom_sysctl_handler(conststruct ctl_table *ctl, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ int ret;
ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
if (write) {
/* we only care for 1 or 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;
/* update the option flags according to the changes. we don'thaveperdeviceoptionsthroughsysctlyet,
but we will have and then this will disappear. */
cdrom_update_settings();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.