📄 ide-cd.c
字号:
rq->buffer += SECTOR_SIZE; --rq->current_nr_sectors; --rq->nr_sectors; ++rq->sector; } /* If we've satisfied the current request, terminate it successfully. */ if (rq->nr_sectors == 0) { cdrom_end_request(drive, 1); return -1; } /* Move on to the next buffer if needed. */ if (rq->current_nr_sectors == 0) cdrom_end_request(drive, 1); /* If this condition does not hold, then the kluge i use to represent the number of sectors to skip at the start of a transfer will fail. I think that this will never happen, but let's be paranoid and check. */ if (rq->current_nr_sectors < bio_cur_sectors(rq->bio) && (rq->sector & (sectors_per_frame - 1))) { printk(KERN_ERR "%s: cdrom_read_from_buffer: buffer botch (%ld)\n", drive->name, (long)rq->sector); cdrom_end_request(drive, 0); return -1; } return 0;}/* * Routine to send a read packet command to the drive. * This is usually called directly from cdrom_start_read. * However, for drq_interrupt devices, it is called from an interrupt * when the drive is ready to accept the command. */static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive){ struct request *rq = HWGROUP(drive)->rq; unsigned short sectors_per_frame; int nskip; sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS; /* If the requested sector doesn't start on a cdrom block boundary, we must adjust the start of the transfer so that it does, and remember to skip the first few sectors. If the CURRENT_NR_SECTORS field is larger than the size of the buffer, it will mean that we're to skip a number of sectors equal to the amount by which CURRENT_NR_SECTORS is larger than the buffer size. */ nskip = rq->sector & (sectors_per_frame - 1); if (nskip > 0) { /* Sanity check... */ if (rq->current_nr_sectors != bio_cur_sectors(rq->bio) && (rq->sector & (sectors_per_frame - 1))) { printk(KERN_ERR "%s: cdrom_start_read_continuation: buffer botch (%u)\n", drive->name, rq->current_nr_sectors); cdrom_end_request(drive, 0); return ide_stopped; } rq->current_nr_sectors += nskip; } /* Set up the command */ rq->timeout = ATAPI_WAIT_PC; /* Send the command to the drive and return. */ return cdrom_transfer_packet_command(drive, rq, &cdrom_read_intr);}#define IDECD_SEEK_THRESHOLD (1000) /* 1000 blocks */#define IDECD_SEEK_TIMER (5 * WAIT_MIN_SLEEP) /* 100 ms */#define IDECD_SEEK_TIMEOUT (2 * WAIT_CMD) /* 20 sec */static ide_startstop_t cdrom_seek_intr (ide_drive_t *drive){ struct cdrom_info *info = drive->driver_data; int stat; static int retry = 10; if (cdrom_decode_status(drive, 0, &stat)) return ide_stopped; CDROM_CONFIG_FLAGS(drive)->seeking = 1; if (retry && time_after(jiffies, info->start_seek + IDECD_SEEK_TIMER)) { if (--retry == 0) { /* * this condition is far too common, to bother * users about it */ /* printk("%s: disabled DSC seek overlap\n", drive->name);*/ drive->dsc_overlap = 0; } } return ide_stopped;}static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive){ struct request *rq = HWGROUP(drive)->rq; sector_t frame = rq->sector; sector_div(frame, queue_hardsect_size(drive->queue) >> SECTOR_BITS); memset(rq->cmd, 0, sizeof(rq->cmd)); rq->cmd[0] = GPCMD_SEEK; put_unaligned(cpu_to_be32(frame), (unsigned int *) &rq->cmd[2]); rq->timeout = ATAPI_WAIT_PC; return cdrom_transfer_packet_command(drive, rq, &cdrom_seek_intr);}static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block){ struct cdrom_info *info = drive->driver_data; info->dma = 0; info->start_seek = jiffies; return cdrom_start_packet_command(drive, 0, cdrom_start_seek_continuation);}/* 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 != bio_data(rq->bio)) { sector_t n = (rq->buffer - (char *) bio_data(rq->bio)) / SECTOR_SIZE; rq->buffer = bio_data(rq->bio); rq->nr_sectors += n; rq->sector -= n; } rq->hard_cur_sectors = rq->current_nr_sectors = bio_cur_sectors(rq->bio); rq->hard_nr_sectors = rq->nr_sectors; rq->hard_sector = rq->sector; rq->q->prep_rq_fn(rq->q, rq);}/* * Start a read request from the CD-ROM. */static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block){ struct cdrom_info *info = drive->driver_data; struct request *rq = HWGROUP(drive)->rq; unsigned short sectors_per_frame; sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS; /* We may be retrying this request after an error. Fix up any weirdness which might be present in the request packet. */ restore_request(rq); /* Satisfy whatever we can of this request from our cached sector. */ if (cdrom_read_from_buffer(drive)) return ide_stopped; /* Clear the local sector buffer. */ info->nsectors_buffered = 0; /* use dma, if possible. */ info->dma = drive->using_dma; if ((rq->sector & (sectors_per_frame - 1)) || (rq->nr_sectors & (sectors_per_frame - 1))) info->dma = 0; /* Start sending the read request to the drive. */ return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation);}/**************************************************************************** * Execute all other packet commands. *//* Interrupt routine for packet command completion. */static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive){ int ireason, len, thislen; struct request *rq = HWGROUP(drive)->rq; u8 lowcyl = 0, highcyl = 0; int stat; /* Check for errors. */ if (cdrom_decode_status(drive, 0, &stat)) return ide_stopped; /* Read the interrupt reason and the transfer length. */ ireason = HWIF(drive)->INB(IDE_IREASON_REG) & 0x3; lowcyl = HWIF(drive)->INB(IDE_BCOUNTL_REG); highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG); len = lowcyl + (256 * highcyl); /* If DRQ is clear, the command has completed. Complain if we still have data left to transfer. */ if ((stat & DRQ_STAT) == 0) { /* Some of the trailing request sense fields are optional, and some drives don't send them. Sigh. */ if (rq->cmd[0] == GPCMD_REQUEST_SENSE && rq->data_len > 0 && rq->data_len <= 5) { while (rq->data_len > 0) { *(unsigned char *)rq->data++ = 0; --rq->data_len; } } if (rq->data_len == 0) cdrom_end_request(drive, 1); else { /* Comment this out, because this always happens right after a reset occurs, and it is annoying to always print expected stuff. */ /* printk ("%s: cdrom_pc_intr: data underrun %d\n", drive->name, pc->buflen); */ rq->cmd_flags |= REQ_FAILED; cdrom_end_request(drive, 0); } return ide_stopped; } /* Figure out how much data to transfer. */ thislen = rq->data_len; if (thislen > len) thislen = len; /* The drive wants to be written to. */ if (ireason == 0) { if (!rq->data) { blk_dump_rq_flags(rq, "cdrom_pc_intr, write"); goto confused; } /* Transfer the data. */ HWIF(drive)->atapi_output_bytes(drive, rq->data, thislen); /* If we haven't moved enough data to satisfy the drive, add some padding. */ while (len > thislen) { int dum = 0; HWIF(drive)->atapi_output_bytes(drive, &dum, sizeof(dum)); len -= sizeof(dum); } /* Keep count of how much data we've moved. */ rq->data += thislen; rq->data_len -= thislen; } /* Same drill for reading. */ else if (ireason == 2) { if (!rq->data) { blk_dump_rq_flags(rq, "cdrom_pc_intr, read"); goto confused; } /* Transfer the data. */ HWIF(drive)->atapi_input_bytes(drive, rq->data, thislen); /* If we haven't moved enough data to satisfy the drive, add some padding. */ while (len > thislen) { int dum = 0; HWIF(drive)->atapi_input_bytes(drive, &dum, sizeof(dum)); len -= sizeof(dum); } /* Keep count of how much data we've moved. */ rq->data += thislen; rq->data_len -= thislen; if (blk_sense_request(rq)) rq->sense_len += thislen; } else {confused: printk (KERN_ERR "%s: cdrom_pc_intr: The drive " "appears confused (ireason = 0x%02x). " "Trying to recover by ending request.\n", drive->name, ireason); rq->cmd_flags |= REQ_FAILED; cdrom_end_request(drive, 0); return ide_stopped; } /* Now we wait for another interrupt. */ ide_set_handler(drive, &cdrom_pc_intr, ATAPI_WAIT_PC, cdrom_timer_expiry); return ide_started;}static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive){ struct request *rq = HWGROUP(drive)->rq; if (!rq->timeout) rq->timeout = ATAPI_WAIT_PC; /* Send the command to the drive and return. */ return cdrom_transfer_packet_command(drive, rq, &cdrom_pc_intr);}static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive){ int len; struct request *rq = HWGROUP(drive)->rq; struct cdrom_info *info = drive->driver_data; info->dma = 0; rq->cmd_flags &= ~REQ_FAILED; len = rq->data_len; /* Start sending the command to the drive. */ return cdrom_start_packet_command(drive, len, cdrom_do_pc_continuation);}static int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq){ struct request_sense sense; int retries = 10; unsigned int flags = rq->cmd_flags; if (rq->sense == NULL) rq->sense = &sense; /* Start of retry loop. */ do { int error; unsigned long time = jiffies; rq->cmd_flags = flags; error = ide_do_drive_cmd(drive, rq, ide_wait); time = jiffies - time; /* FIXME: we should probably abort/retry or something * in case of failure */ if (rq->cmd_flags & REQ_FAILED) { /* The request failed. Retry if it was due to a unit attention status (usually means media was changed). */ struct request_sense *reqbuf = rq->sense; if (reqbuf->sense_key == UNIT_ATTENTION) cdrom_saw_media_change(drive); else if (reqbuf->sense_key == NOT_READY && reqbuf->asc == 4 && reqbuf->ascq != 4) { /* The drive is in the process of loading a disk. Retry, but wait a little to give the drive time to complete the load. */ ssleep(2); } else { /* Otherwise, don't retry. */ retries = 0; } --retries; } /* End of retry loop. */ } while ((rq->cmd_flags & REQ_FAILED) && retries >= 0); /* Return an error if the command failed. */ return (rq->cmd_flags & REQ_FAILED) ? -EIO : 0;}/* * Write handling */static int cdrom_write_check_ireason(ide_drive_t *drive, int len, int ireason){ /* Two notes about IDE interrupt reason here - 0 means that * the drive wants to receive data from us, 2 means that * the drive is expecting to transfer data to us. */ if (ireason == 0) return 0; else if (ireason == 2) { /* Whoops... The drive wants to send data. */ printk(KERN_ERR "%s: %s: wrong transfer direction!\n", drive->name, __FUNCTION__); while (len > 0) { int dum = 0; HWIF(drive)->atapi_input_bytes(drive, &dum, sizeof(dum)); len -= sizeof(dum); } } else { /* Drive wants a command packet, or invalid ireason... */ printk(KERN_ERR "%s: %s: bad interrupt reason 0x%02x\n", drive->name, __FUNCTION__, ireason); } cdrom_end_request(drive, 0); return 1;}typedef void (xfer_func_t)(ide_drive_t *, void *, u32);/* * best way to deal with dma that is not sector aligned right now... note * that in this path we are not using ->data or ->buffer at all. this irs * can replace cdrom_pc_intr, cdrom_read_intr, and cdrom_write_intr in the * future. */static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive){ struct cdrom_info *info = drive->driver_data; struct request *rq = HWGROUP(drive)->rq; int dma_error, dma, stat, ireason, len, thislen; u8 lowcyl, highcyl; xfer_func_t *xferfunc; unsigned long flags; /* Check for errors. */ dma_error = 0; dma = info->dma; if (dma) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -