📄 ide-cd.c
字号:
{ 0x5302, "Medium removal prevented" }, { 0x5700, "Unable to recover table of contents" }, { 0x5a00, "Operator request or state change input (unspecified)" }, { 0x5a01, "Operator medium removal request" }, { 0x5b00, "Threshold condition met" }, { 0x5c00, "Status change" }, { 0x6300, "End of user area encountered on this track" }, { 0x6400, "Illegal mode for this track" }, { 0xbf00, "Loss of streaming" },};#endif/**************************************************************************** * Generic packet command support and error handling routines. */staticvoid cdrom_analyze_sense_data (ide_drive_t *drive, struct atapi_request_sense *reqbuf, struct packet_command *failed_command){ /* Don't print not ready or unit attention errors for READ_SUBCHANNEL. Workman (and probably other programs) uses this command to poll the drive, and we don't want to fill the syslog with useless errors. */ if (failed_command && failed_command->c[0] == SCMD_READ_SUBCHANNEL && (reqbuf->sense_key == NOT_READY || reqbuf->sense_key == UNIT_ATTENTION)) return;#if VERBOSE_IDE_CD_ERRORS { int i; char *s; char buf[80]; printk ("ATAPI device %s:\n", drive->name); printk (" Error code: 0x%02x\n", reqbuf->error_code); if (reqbuf->sense_key >= 0 && reqbuf->sense_key < ARY_LEN (sense_key_texts)) s = sense_key_texts[reqbuf->sense_key]; else s = "(bad sense key)"; printk (" Sense key: 0x%02x - %s\n", reqbuf->sense_key, s); if (reqbuf->asc == 0x40) { sprintf (buf, "Diagnostic failure on component 0x%02x", reqbuf->ascq); s = buf; } else { int lo, hi; int key = (reqbuf->asc << 8); if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) ) key |= reqbuf->ascq; lo = 0; hi = ARY_LEN (sense_data_texts); s = NULL; while (hi > lo) { int mid = (lo + hi) / 2; if (sense_data_texts[mid].asc_ascq == key) { s = sense_data_texts[mid].text; break; } else if (sense_data_texts[mid].asc_ascq > key) hi = mid; else lo = mid+1; } } if (s == NULL) { if (reqbuf->asc > 0x80) s = "(vendor-specific error)"; else s = "(reserved error code)"; } printk (" Additional sense data: 0x%02x, 0x%02x - %s\n", reqbuf->asc, reqbuf->ascq, s); if (failed_command != NULL) { printk (" Failed packet command: "); for (i=0; i<sizeof (failed_command->c); i++) printk ("%02x ", failed_command->c[i]); printk ("\n"); } if (reqbuf->sense_key == ILLEGAL_REQUEST && (reqbuf->sense_key_specific[0] & 0x80) != 0) { printk (" Error in %s byte %d", (reqbuf->sense_key_specific[0] & 0x40) != 0 ? "command packet" : "command data", (reqbuf->sense_key_specific[1] << 8) + reqbuf->sense_key_specific[2]); if ((reqbuf->sense_key_specific[0] & 0x40) != 0) { printk (" bit %d", reqbuf->sense_key_specific[0] & 0x07); } printk ("\n"); } }#else /* not VERBOSE_IDE_CD_ERRORS */ /* Suppress printing unit attention and `in progress of becoming ready' errors when we're not being verbose. */ if (reqbuf->sense_key == UNIT_ATTENTION || (reqbuf->sense_key == NOT_READY && (reqbuf->asc == 4 || reqbuf->asc == 0x3a))) return; printk ("%s: code: 0x%02x key: 0x%02x asc: 0x%02x ascq: 0x%02x\n", drive->name, reqbuf->error_code, reqbuf->sense_key, reqbuf->asc, reqbuf->ascq);#endif /* not VERBOSE_IDE_CD_ERRORS */}/* Fix up a possibly partially-processed request so that we can start it over entirely, or even put it back on the request queue. */static void restore_request (struct request *rq){ if (rq->buffer != rq->bh->b_data) { int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE; rq->buffer = rq->bh->b_data; rq->nr_sectors += n; rq->sector -= n; } rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;}static void cdrom_queue_request_sense (ide_drive_t *drive, struct semaphore *sem, struct atapi_request_sense *reqbuf, struct packet_command *failed_command){ struct request *rq; struct packet_command *pc; int len; /* If the request didn't explicitly specify where to put the sense data, use the statically allocated structure. */ if (reqbuf == NULL) reqbuf = &drive->cdrom_info.sense_data; /* Make up a new request to retrieve sense information. */ pc = &HWIF(drive)->request_sense_pc; memset (pc, 0, sizeof (*pc)); /* The request_sense structure has an odd number of (16-bit) words, which won't work well with 32-bit transfers. However, we don't care about the last two bytes, so just truncate the structure down to an even length. */ len = sizeof (*reqbuf) / 4; len *= 4; pc->c[0] = REQUEST_SENSE; pc->c[4] = len; pc->buffer = (char *)reqbuf; pc->buflen = len; pc->sense_data = (struct atapi_request_sense *)failed_command; /* stuff the sense request in front of our current request */ rq = &HWIF(drive)->request_sense_request; ide_init_drive_cmd (rq); rq->cmd = REQUEST_SENSE_COMMAND; rq->buffer = (char *)pc; rq->sem = sem; (void) ide_do_drive_cmd (drive, rq, ide_preempt);}static void cdrom_end_request (int uptodate, ide_drive_t *drive){ struct request *rq = HWGROUP(drive)->rq; if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate) { struct packet_command *pc = (struct packet_command *) rq->buffer; cdrom_analyze_sense_data (drive, (struct atapi_request_sense *) (pc->buffer - pc->c[4]), (struct packet_command *) pc->sense_data); } ide_end_request (uptodate, HWGROUP(drive));}/* Mark that we've seen a media change, and invalidate our internal buffers. */static void cdrom_saw_media_change (ide_drive_t *drive){ CDROM_STATE_FLAGS (drive)->media_changed = 1; CDROM_STATE_FLAGS (drive)->toc_valid = 0; drive->cdrom_info.nsectors_buffered = 0;}/* Returns 0 if the request should be continued. Returns 1 if the request was ended. */static int cdrom_decode_status (ide_drive_t *drive, int good_stat, int *stat_ret){ struct request *rq = HWGROUP(drive)->rq; int stat, err, sense_key, cmd; /* Check for errors. */ stat = GET_STAT(); *stat_ret = stat; if (OK_STAT (stat, good_stat, BAD_R_STAT)) return 0; /* Got an error. */ err = IN_BYTE (IDE_ERROR_REG); sense_key = err >> 4; if (rq == NULL) printk ("%s : missing request in cdrom_decode_status\n", drive->name); else { cmd = rq->cmd; if (cmd == REQUEST_SENSE_COMMAND) { /* We got an error trying to get sense info from the drive (probably while trying to recover from a former error). Just give up. */ struct packet_command *pc = (struct packet_command *) rq->buffer; pc->stat = 1; cdrom_end_request (1, drive); ide_error (drive, "request sense failure", stat); return 1; } else if (cmd == PACKET_COMMAND) { /* All other functions, except for READ. */ struct packet_command *pc = (struct packet_command *) rq->buffer; struct semaphore *sem = NULL; /* Check for tray open. */ if (sense_key == NOT_READY) { cdrom_saw_media_change (drive); /* Print an error message to the syslog. Exception: don't print anything if this is a read subchannel command. This is because workman constantly polls the drive with this command, and we don't want to uselessly fill up the syslog. */ if (pc->c[0] != SCMD_READ_SUBCHANNEL) printk ("%s : tray open or drive not ready\n", drive->name); } else if (sense_key == UNIT_ATTENTION) { /* Check for media change. */ cdrom_saw_media_change (drive); printk ("%s: media changed\n", drive->name); } else { /* Otherwise, print an error. */ ide_dump_status (drive, "packet command error", stat); } /* Set the error flag and complete the request. Then, if we have a CHECK CONDITION status, queue a request sense command. We must be careful, though: we don't want the thread in cdrom_queue_packet_command to wake up until the request sense has completed. We do this by transferring the semaphore from the packet command request to the request sense request. */ if ((stat & ERR_STAT) != 0) { sem = rq->sem; rq->sem = NULL; } pc->stat = 1; cdrom_end_request (1, drive); if ((stat & ERR_STAT) != 0) cdrom_queue_request_sense (drive, sem, pc->sense_data, pc); } else { /* Handle errors from READ requests. */ if (sense_key == NOT_READY) { /* Tray open. */ cdrom_saw_media_change (drive); /* Fail the request. */ printk ("%s : tray open\n", drive->name); cdrom_end_request (0, drive); } else if (sense_key == UNIT_ATTENTION) { /* Media change. */ cdrom_saw_media_change (drive); /* Arrange to retry the request. But be sure to give up if we've retried too many times. */ if (++rq->errors > ERROR_MAX) cdrom_end_request (0, drive); } else if (sense_key == ILLEGAL_REQUEST || sense_key == DATA_PROTECT) { /* No point in retrying after an illegal request or data protect error.*/ ide_dump_status (drive, "command error", stat); cdrom_end_request (0, drive); } else if ((err & ~ABRT_ERR) != 0) { /* Go to the default handler for other errors. */ ide_error (drive, "cdrom_decode_status", stat); return 1; } else if ((++rq->errors > ERROR_MAX)) { /* We've racked up too many retries. Abort. */ cdrom_end_request (0, drive); } /* If we got a CHECK_CONDITION status, queue a request sense command. */ if ((stat & ERR_STAT) != 0) cdrom_queue_request_sense (drive, NULL, NULL, NULL); } } /* Retry, or handle the next request. */ return 1;}/* Set up the device registers for transferring a packet command on DEV, expecting to later transfer XFERLEN bytes. HANDLER is the routine which actually transfers the command to the drive. If this is a drq_interrupt device, this routine will arrange for HANDLER to be called when the interrupt from the drive arrives. Otherwise, HANDLER will be called immediately after the drive is prepared for the transfer. */static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen, ide_handler_t *handler){ /* Wait for the controller to be idle. */ if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1; /* Set up the controller registers. */ OUT_BYTE (0, IDE_FEATURE_REG); OUT_BYTE (0, IDE_NSECTOR_REG); OUT_BYTE (0, IDE_SECTOR_REG); OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG); OUT_BYTE (xferlen >> 8 , IDE_HCYL_REG); OUT_BYTE (drive->ctl, IDE_CONTROL_REG); if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { ide_set_handler (drive, handler, WAIT_CMD); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ (*handler) (drive); } return 0;}/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN. The device registers must have already been prepared by cdrom_start_packet_command. HANDLER is the interrupt handler to call when the command completes or there's data ready. */static int cdrom_transfer_packet_command (ide_drive_t *drive, char *cmd_buf, int cmd_len, ide_handler_t *handler){ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { /* Here we should have been called after receiving an interrupt from the device. DRQ should how be set. */ int stat_dum; /* Check for errors. */ if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) return 1; } else { /* Otherwise, we must wait for DRQ to get set. */ if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) return 1; } /* Arm the interrupt handler. */ ide_set_handler (drive, handler, WAIT_CMD); /* Send the command to the device. */ cdrom_out_bytes (drive, cmd_buf, cmd_len); return 0;}/**************************************************************************** * Block read functions. *//* * Buffer up to SECTORS_TO_TRANSFER sectors from the drive in our sector * buffer. Once the first sector is added, any subsequent sectors are * assumed to be continuous (until the buffer is cleared). For the first * sector added, SECTOR is its sector number. (SECTOR is then ignored until * the buffer is cleared.) */static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector, int sectors_to_transfer){ struct cdrom_info *info = &drive->cdrom_info; /* Number of sectors to read into the buffer. */ int sectors_to_buffer = MIN (sectors_to_transfer, (SECTOR_BUFFER_SIZE >> SECTOR_BITS) - info->nsectors_buffered);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -