📄 cdrom.c
字号:
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(). */static 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 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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -