📄 cdrom.c
字号:
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 inlineint 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; 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; cgc.data_direction = CGC_DATA_READ; ret = cdrom_read_block(cdi, &cgc, lba, 1, format, blocksize); if (ret) { /* * 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; 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; if ((cgc.buffer = (char *) kmalloc(CD_FRAMESIZE_RAW, GFP_KERNEL)) == NULL) 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) { ret = cdrom_read_block(cdi, &cgc, lba, 1, 1, CD_FRAMESIZE_RAW); if (ret) break; __copy_to_user(ra.buf, cgc.buffer, CD_FRAMESIZE_RAW); ra.buf += CD_FRAMESIZE_RAW; ra.nframes--; lba++; } 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] > cgc.buflen) { cgc.buflen = buffer[1] + 2; if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_AUDIO_CTL_PAGE, 0))) return ret; } /* get the offset from the length of the page. length is measure from byte 2 an on, thus the 14. */ offset = buffer[1] - 14; /* 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; 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 *)arg, size)) { kfree(s); return -EFAULT; } if ((ret = dvd_read_struct(cdi, s))) { kfree(s); return ret; } if (copy_to_user((dvd_struct *)arg, s, size)) ret =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -