📄 cdrom.c
字号:
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 */
cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognize!\n");
return CDS_NO_INFO;
}
case CDROM_CHANGER_NSLOTS: {
cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n");
return cdi->capacity;
}
}
/* 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 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 */
/* do the device specific ioctls */
if (CDROM_CAN(CDC_IOCTLS))
return cdo->dev_ioctl(cdi, cmd, arg);
return -ENOSYS;
}
static inline
int msf_to_lba(char m, char s, char f)
{
return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
}
/*
* Required when we need to use READ_10 to issue other than 2048 block
* reads
*/
static int cdrom_switch_blocksize(struct cdrom_device_info *cdi, int size)
{
struct cdrom_device_ops *cdo = cdi->ops;
struct cdrom_generic_command cgc;
struct modesel_head mh;
memset(&mh, 0, sizeof(mh));
mh.block_desc_length = 0x08;
mh.block_length_med = (size >> 8) & 0xff;
mh.block_length_lo = size & 0xff;
memset(&cgc, 0, sizeof(cgc));
cgc.cmd[0] = 0x15;
cgc.cmd[1] = 1 << 4;
cgc.cmd[4] = 12;
cgc.buflen = sizeof(mh);
cgc.buffer = (char *) &mh;
cgc.data_direction = CGC_DATA_WRITE;
mh.block_desc_length = 0x08;
mh.block_length_med = (size >> 8) & 0xff;
mh.block_length_lo = size & 0xff;
return cdo->generic_packet(cdi, &cgc);
}
static int cdrom_do_cmd(struct cdrom_device_info *cdi,
struct cdrom_generic_command *cgc)
{
struct request_sense *usense, sense;
unsigned char *ubuf;
int ret;
if (cgc->data_direction == CGC_DATA_UNKNOWN)
return -EINVAL;
if (cgc->buflen < 0 || cgc->buflen >= 131072)
return -EINVAL;
if ((ubuf = cgc->buffer)) {
cgc->buffer = kmalloc(cgc->buflen, GFP_KERNEL);
if (cgc->buffer == NULL)
return -ENOMEM;
}
usense = cgc->sense;
cgc->sense = &sense;
if (usense && !access_ok(VERIFY_WRITE, usense, sizeof(*usense)))
return -EFAULT;
if (cgc->data_direction == CGC_DATA_READ) {
if (!access_ok(VERIFY_READ, ubuf, cgc->buflen))
return -EFAULT;
} else if (cgc->data_direction == CGC_DATA_WRITE) {
if (copy_from_user(cgc->buffer, ubuf, cgc->buflen)) {
kfree(cgc->buffer);
return -EFAULT;
}
}
ret = cdi->ops->generic_packet(cdi, cgc);
__copy_to_user(usense, cgc->sense, sizeof(*usense));
if (!ret && cgc->data_direction == CGC_DATA_READ)
__copy_to_user(ubuf, cgc->buffer, cgc->buflen);
kfree(cgc->buffer);
return ret;
}
static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
unsigned long arg)
{
struct cdrom_device_ops *cdo = cdi->ops;
struct cdrom_generic_command cgc;
struct request_sense sense;
kdev_t dev = cdi->dev;
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 *)arg, cgc.buffer, blocksize))
ret = -EFAULT;
kfree(cgc.buffer);
return ret;
}
case CDROMREADAUDIO: {
struct cdrom_read_audio ra;
int lba, nr;
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)
return -EINVAL;
/*
* start with will ra.nframes size, back down if alloc fails
*/
nr = ra.nframes;
do {
cgc.buffer = kmalloc(CD_FRAMESIZE_RAW * nr, GFP_KERNEL);
if (cgc.buffer)
break;
nr >>= 1;
} while (nr);
if (!nr)
return -ENOMEM;
if (!access_ok(VERIFY_WRITE, ra.buf, ra.nframes*CD_FRAMESIZE_RAW)) {
kfree(cgc.buffer);
return -EFAULT;
}
cgc.data_direction = CGC_DATA_READ;
while (ra.nframes > 0) {
if (nr > ra.nframes)
nr = ra.nframes;
ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW);
if (ret)
break;
__copy_to_user(ra.buf, cgc.buffer, CD_FRAMESIZE_RAW*nr);
ra.buf += CD_FRAMESIZE_RAW * nr;
ra.nframes -= nr;
lba += nr;
}
kfree(cgc.buffer);
return ret;
}
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[32];
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;
/* some drives have longer pages, adjust and reread. */
if (buffer[1] > c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -