📄 cdrom.c
字号:
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;
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
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;
}
memcpy(s->bca.value, &buf[4], s->bca.len);
return 0;
}
static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s)
{
int ret = 0, size;
u_char *buf;
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
size = sizeof(s->manufact.value) + 4;
if ((buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL)
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;
if ((ret = cdo->generic_packet(cdi, &cgc))) {
kfree(buf);
return ret;
}
s->manufact.len = buf[0] << 8 | buf[1];
if (s->manufact.len < 0 || s->manufact.len > 2048) {
cdinfo(CD_WARNING, "Received invalid manufacture info length"
" (%d)\n", s->bca.len);
ret = -EIO;
} else {
memcpy(s->manufact.value, &buf[4], s->manufact.len);
}
kfree(buf);
return ret;
}
static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s)
{
switch (s->type) {
case DVD_STRUCT_PHYSICAL:
return dvd_read_physical(cdi, s);
case DVD_STRUCT_COPYRIGHT:
return dvd_read_copyright(cdi, s);
case DVD_STRUCT_DISCKEY:
return dvd_read_disckey(cdi, s);
case DVD_STRUCT_BCA:
return dvd_read_bca(cdi, s);
case DVD_STRUCT_MANUFACT:
return dvd_read_manufact(cdi, s);
default:
cdinfo(CD_WARNING, ": Invalid DVD structure read requested (%d)\n",
s->type);
return -EINVAL;
}
}
int cdrom_mode_sense(struct cdrom_device_info *cdi,
struct cdrom_generic_command *cgc,
int page_code, int page_control)
{
struct cdrom_device_ops *cdo = cdi->ops;
memset(cgc->cmd, 0, sizeof(cgc->cmd));
cgc->cmd[0] = GPCMD_MODE_SENSE_10;
cgc->cmd[2] = page_code | (page_control << 6);
cgc->cmd[7] = cgc->buflen >> 8;
cgc->cmd[8] = cgc->buflen & 0xff;
cgc->data_direction = CGC_DATA_READ;
return cdo->generic_packet(cdi, cgc);
}
int cdrom_mode_select(struct cdrom_device_info *cdi,
struct cdrom_generic_command *cgc)
{
struct cdrom_device_ops *cdo = cdi->ops;
memset(cgc->cmd, 0, sizeof(cgc->cmd));
memset(cgc->buffer, 0, 2);
cgc->cmd[0] = GPCMD_MODE_SELECT_10;
cgc->cmd[1] = 0x10; /* PF */
cgc->cmd[7] = cgc->buflen >> 8;
cgc->cmd[8] = cgc->buflen & 0xff;
cgc->data_direction = CGC_DATA_WRITE;
return cdo->generic_packet(cdi, cgc);
}
static int cdrom_read_subchannel(struct cdrom_device_info *cdi,
struct cdrom_subchnl *subchnl, int mcn)
{
struct cdrom_device_ops *cdo = cdi->ops;
struct cdrom_generic_command cgc;
char buffer[32];
int ret;
init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_SUBCHANNEL;
cgc.cmd[1] = 2; /* MSF addressing */
cgc.cmd[2] = 0x40; /* request subQ data */
cgc.cmd[3] = mcn ? 2 : 1;
cgc.cmd[8] = 16;
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
subchnl->cdsc_audiostatus = cgc.buffer[1];
subchnl->cdsc_format = CDROM_MSF;
subchnl->cdsc_ctrl = cgc.buffer[5] & 0xf;
subchnl->cdsc_trk = cgc.buffer[6];
subchnl->cdsc_ind = cgc.buffer[7];
subchnl->cdsc_reladdr.msf.minute = cgc.buffer[13];
subchnl->cdsc_reladdr.msf.second = cgc.buffer[14];
subchnl->cdsc_reladdr.msf.frame = cgc.buffer[15];
subchnl->cdsc_absaddr.msf.minute = cgc.buffer[9];
subchnl->cdsc_absaddr.msf.second = cgc.buffer[10];
subchnl->cdsc_absaddr.msf.frame = cgc.buffer[11];
return 0;
}
/*
* Specific READ_10 interface
*/
static int cdrom_read_cd(struct cdrom_device_info *cdi,
struct cdrom_generic_command *cgc, int lba,
int blocksize, int nblocks)
{
struct cdrom_device_ops *cdo = cdi->ops;
memset(&cgc->cmd, 0, sizeof(cgc->cmd));
cgc->cmd[0] = GPCMD_READ_10;
cgc->cmd[2] = (lba >> 24) & 0xff;
cgc->cmd[3] = (lba >> 16) & 0xff;
cgc->cmd[4] = (lba >> 8) & 0xff;
cgc->cmd[5] = lba & 0xff;
cgc->cmd[6] = (nblocks >> 16) & 0xff;
cgc->cmd[7] = (nblocks >> 8) & 0xff;
cgc->cmd[8] = nblocks & 0xff;
cgc->buflen = blocksize * nblocks;
return cdo->generic_packet(cdi, cgc);
}
/* very generic interface for reading the various types of blocks */
static int cdrom_read_block(struct cdrom_device_info *cdi,
struct cdrom_generic_command *cgc,
int lba, int nblocks, int format, int blksize)
{
struct cdrom_device_ops *cdo = cdi->ops;
memset(&cgc->cmd, 0, sizeof(cgc->cmd));
cgc->cmd[0] = GPCMD_READ_CD;
/* expected sector size - cdda,mode1,etc. */
cgc->cmd[1] = format << 2;
/* starting address */
cgc->cmd[2] = (lba >> 24) & 0xff;
cgc->cmd[3] = (lba >> 16) & 0xff;
cgc->cmd[4] = (lba >> 8) & 0xff;
cgc->cmd[5] = lba & 0xff;
/* number of blocks */
cgc->cmd[6] = (nblocks >> 16) & 0xff;
cgc->cmd[7] = (nblocks >> 8) & 0xff;
cgc->cmd[8] = nblocks & 0xff;
cgc->buflen = blksize * nblocks;
/* 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);
}
/* 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 inode *ip, struct file *fp, unsigned int cmd,
unsigned long arg)
{
kdev_t dev = ip->i_rdev;
struct cdrom_device_info *cdi = cdrom_find_device(dev);
struct cdrom_device_ops *cdo = cdi->ops;
int 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)))
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 cdo->tray_move(cdi, 1);
}
case CDROMCLOSETRAY: {
cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n");
if (!CDROM_CAN(CDC_CLOSE_TRAY))
return -ENOSYS;
return cdo->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;
}
case CDROM_MEDIA_CHANGED: {
struct cdrom_changer_info info;
cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n");
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
return -ENOSYS;
/* cannot select disc or select current disc */
if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
return media_changed(cdi, 1);
if ((unsigned int)arg >= cdi->capacity)
return -EINVAL;
if ((ret = cdrom_read_mech_status(cdi, &info)))
return ret;
return info.slots[arg].change;
}
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;
}
case CDROM_CLEAR_OPTIONS: {
cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n");
cdi->options &= ~(int) arg;
return cdi->options;
}
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);
}
case CDROM_SELECT_DISC: {
cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n");
if (!CDROM_CAN(CDC_SELECT_DISC))
return -ENOSYS;
if ((arg != CDSL_CURRENT) && (arg != CDSL_NONE))
if ((int)arg >= cdi->capacity)
return -EINVAL;
/* cdo->select_disc is a hook to allow a driver-specific
* way of seleting disc. However, since there is no
* equiv hook for cdrom_slot_status this may not
* actually be useful...
*/
if (cdo->select_disc != NULL)
return cdo->select_disc(cdi, arg);
/* no driver specific select_disc(), call our own */
cdinfo(CD_CHANGER, "Using generic cdrom_select_disc()\n");
return cdrom_select_disc(cdi, arg);
}
case CDROMRESET: {
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n");
if (!CDROM_CAN(CDC_RESET))
return -ENOSYS;
return cdo->reset(cdi);
}
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);
}
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;
}
case CDROM_GET_CAPABILITY: {
cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n");
return (cdo->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;
}
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);
}
/* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -