📄 cdrom.c
字号:
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) { cdinfo(CD_OPEN, "bummer. wrong media type.\n"); ret=-EMEDIUMTYPE; goto clean_up_and_return; } else { cdinfo(CD_OPEN, "wrong media type, but CDO_CHECK_TYPE not set.\n"); } } cdinfo(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 */ cdinfo(CD_OPEN, "opening the device gave me %d.\n", ret); /* After all this careful checking, we shouldn't have problems opening the device, but we don't want the device locked if this somehow fails... */ if (ret) { cdinfo(CD_OPEN, "open device failed.\n"); goto clean_up_and_return; } if (cdo->capability & ~cdi->mask & CDC_LOCK && cdi->options & CDO_LOCK) { cdo->lock_door(cdi, 1); cdinfo(CD_OPEN, "door locked.\n"); } cdinfo(CD_OPEN, "device opened successfully.\n"); return ret; /* Something failed. Try to unlock the drive, because some drivers (notably ide-cd) lock the drive after every command. This produced a nasty bug where after mount failed, the drive would remain locked! This ensures that the drive gets unlocked after a mount fails. This is a goto to avoid bloating the driver with redundant code. */ clean_up_and_return: cdinfo(CD_WARNING, "open failed.\n"); if (cdo->capability & ~cdi->mask & CDC_LOCK && cdi->options & CDO_LOCK) { cdo->lock_door(cdi, 0); cdinfo(CD_OPEN, "door unlocked.\n"); } return ret;}/* This code is similar to that in open_for_data. The routine is called whenever an audio play operation is requested.*/int check_for_audio_disc(struct cdrom_device_info * cdi, struct cdrom_device_ops * cdo){ int ret; tracktype tracks; cdinfo(CD_OPEN, "entering check_for_audio_disc\n"); if (!(cdi->options & CDO_CHECK_TYPE)) return 0; if (cdo->drive_status != NULL) { ret = cdo->drive_status(cdi, CDSL_CURRENT); cdinfo(CD_OPEN, "drive_status=%d\n", ret); if (ret == CDS_TRAY_OPEN) { cdinfo(CD_OPEN, "the tray is open...\n"); /* can/may i close it? */ if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY && cdi->options & CDO_AUTO_CLOSE) { cdinfo(CD_OPEN, "trying to close the tray.\n"); ret=cdo->tray_move(cdi,0); if (ret) { cdinfo(CD_OPEN, "bummer. tried to close tray but failed.\n"); /* Ignore the error from the low level driver. We don't care why it couldn't close the tray. We only care that there is no disc in the drive, since that is the _REAL_ problem here.*/ return -ENOMEDIUM; } } else { cdinfo(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)) { cdinfo(CD_OPEN, "bummer. the tray is still not closed.\n"); return -ENOMEDIUM; } if (ret!=CDS_DISC_OK) { cdinfo(CD_OPEN, "bummer. disc isn't ready.\n"); return -EIO; } cdinfo(CD_OPEN, "the tray is now closed.\n"); } } cdrom_count_tracks(cdi, &tracks); if (tracks.error) return(tracks.error); if (tracks.audio==0) return -EMEDIUMTYPE; return 0;}/* Admittedly, the logic below could be performed in a nicer way. */staticint cdrom_release(struct inode *ip, struct file *fp){ kdev_t dev = ip->i_rdev; struct cdrom_device_info *cdi = cdrom_find_device (dev); struct cdrom_device_ops *cdo = cdi->ops; int opened_for_data; cdinfo(CD_CLOSE, "entering cdrom_release\n"); if (cdi == NULL) return 0; if (cdi->use_count > 0) cdi->use_count--; if (cdi->use_count == 0) cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); if (cdi->use_count == 0 && /* last process that closes dev*/ cdo->capability & CDC_LOCK && !keeplocked) { cdinfo(CD_CLOSE, "Unlocking door!\n"); cdo->lock_door(cdi, 0); } opened_for_data = !(cdi->options & CDO_USE_FFLAGS) || !(fp && fp->f_flags & O_NONBLOCK); cdo->release(cdi); if (cdi->use_count == 0) { /* last process that closes dev*/ struct super_block *sb; sync_dev(dev); sb = get_super(dev); if (sb) invalidate_inodes(sb); invalidate_buffers(dev); if (opened_for_data && cdi->options & CDO_AUTO_EJECT && cdo->capability & ~cdi->mask & CDC_OPEN_TRAY) cdo->tray_move(cdi, 1); } return 0;}/* We want to make media_changed accessible to the user through an * ioctl. The main problem now is that we must double-buffer the * low-level implementation, to assure that the VFS and the user both * see a medium change once. */staticint media_changed(struct cdrom_device_info *cdi, int queue){ unsigned int mask = (1 << (queue & 1)); int ret = !!(cdi->mc_flags & mask); if (!(cdi->ops->capability & ~cdi->mask & CDC_MEDIA_CHANGED)) return ret; /* changed since last call? */ if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) { cdi->mc_flags = 0x3; /* set bit on both queues */ ret |= 1; } cdi->mc_flags &= ~mask; /* clear bit */ return ret;}staticint cdrom_media_changed(kdev_t dev){ struct cdrom_device_info *cdi = cdrom_find_device (dev); /* This talks to the VFS, which doesn't like errors - just 1 or 0. * Returning "0" is always safe (media hasn't been changed). Do that * if the low-level cdrom driver dosn't support media changed. */ if (cdi == NULL) return 0; if (cdi->ops->media_changed == NULL) return 0; if (!(cdi->ops->capability & ~cdi->mask & CDC_MEDIA_CHANGED)) return 0; return (media_changed(cdi, 0));}void 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; cdinfo(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n"); if (!(cdi->ops->capability & ~cdi->mask & 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++; else if (entry.cdte_format == 0x20) tracks->xa++; else tracks->data++; } else tracks->audio++; cdinfo(CD_COUNT_TRACKS, "track %d: format=%d, ctrl=%d\n", i, entry.cdte_format, entry.cdte_ctrl); } cdinfo(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);} /* Requests to the low-level drivers will /always/ be done in the following format convention: CDROM_LBA: all data-related requests. CDROM_MSF: all audio-related requests. However, a low-level implementation is allowed to refuse this request, and return information in its own favorite format. It doesn't make sense /at all/ to ask for a play_audio in LBA format, or ask for multi-session info in MSF format. However, for backward compatibility these format requests will be satisfied, but the requests to the low-level drivers will be sanitized in the more meaningful format indicated above. */staticvoid sanitize_format(union cdrom_addr *addr, u_char * curr, u_char requested){ if (*curr == requested) return; /* nothing to be done! */ if (requested == CDROM_LBA) { addr->lba = (int) addr->msf.frame + 75 * (addr->msf.second - 2 + 60 * addr->msf.minute); } else { /* CDROM_MSF */ int lba = addr->lba; addr->msf.frame = lba % 75; lba /= 75; lba += 2; addr->msf.second = lba % 60; addr->msf.minute = lba / 60; } *curr = requested;}/* Some of the cdrom ioctls are not implemented here, because these * appear to be either too device-specific, or it is not clear to me * what use they are. These are (number of drivers that support them * in parenthesis): CDROMREADMODE1 (2+ide), CDROMREADMODE2 (2+ide), * CDROMREADAUDIO (2+ide), CDROMREADRAW (2), CDROMREADCOOKED (2), * CDROMSEEK (2), CDROMPLAYBLK (scsi), CDROMREADALL (1). Read-audio, * OK (although i guess the record companies aren't too happy with * this, most drives therefore refuse to transport audio data). But * why are there 5 different READs defined? For now, these functions * are left over to the device-specific ioctl routine, * cdo->dev_ioctl. Note that as a result of this, no * memory-verification is performed for these ioctls. */staticint 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; if (cdi == NULL) return -ENODEV; cdo = cdi->ops; /* the first few commands do not deal with audio drive_info, but only with routines in cdrom device operations. */ switch (cmd) { /* maybe we should order cases after statistics of use? */ case CDROMMULTISESSION: { int ret; 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: { int ret; cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)) return -ENOSYS; if (cdi->use_count != 1 || keeplocked) return -EBUSY; if (cdo->capability & ~cdi->mask & 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 (!(cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY)) return -ENOSYS; return cdo->tray_move(cdi, 0); case CDROMEJECT_SW: cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n"); if (!(cdo->capability & ~cdi->mask & 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: { cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n"); if (!(cdo->capability & ~cdi->mask & CDC_MEDIA_CHANGED)) return -ENOSYS; if (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC) || arg == CDSL_CURRENT) /* cannot select disc or select current disc */ return media_changed(cdi, 1); if ((unsigned int)arg >= cdi->capacity) return -EINVAL; return cdo->media_changed (cdi, arg); } 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 (!(cdo->capability & ~cdi->mask & CDC_LOCK)) return -ENOSYS; break; case 0: return cdi->options; /* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */ default: if (!(cdo->capability & ~cdi->mask & 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 (!(cdo->capability & ~cdi->mask & 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 (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC)) return -ENOSYS; if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) return cdo->select_disc(cdi, arg); if ((int)arg >= cdi->capacity) return -EDRIVE_CANT_DO_THIS; return cdo->select_disc(cdi, arg); } case CDROMRESET: { cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -