diff options
Diffstat (limited to 'drivers/ide/ide-atapi.c')
-rw-r--r-- | drivers/ide/ide-atapi.c | 168 |
1 files changed, 55 insertions, 113 deletions
diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 2fb5d28a9be..3e43b889dd6 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -6,6 +6,8 @@ #include <linux/cdrom.h> #include <linux/delay.h> #include <linux/ide.h> +#include <linux/scatterlist.h> + #include <scsi/scsi.h> #ifdef DEBUG @@ -69,56 +71,6 @@ int ide_check_atapi_device(ide_drive_t *drive, const char *s) } EXPORT_SYMBOL_GPL(ide_check_atapi_device); -/* PIO data transfer routine using the scatter gather table. */ -int ide_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, - unsigned int bcount, int write) -{ - ide_hwif_t *hwif = drive->hwif; - const struct ide_tp_ops *tp_ops = hwif->tp_ops; - xfer_func_t *xf = write ? tp_ops->output_data : tp_ops->input_data; - struct scatterlist *sg = pc->sg; - char *buf; - int count, done = 0; - - while (bcount) { - count = min(sg->length - pc->b_count, bcount); - - if (PageHighMem(sg_page(sg))) { - unsigned long flags; - - local_irq_save(flags); - buf = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset; - xf(drive, NULL, buf + pc->b_count, count); - kunmap_atomic(buf - sg->offset, KM_IRQ0); - local_irq_restore(flags); - } else { - buf = sg_virt(sg); - xf(drive, NULL, buf + pc->b_count, count); - } - - bcount -= count; - pc->b_count += count; - done += count; - - if (pc->b_count == sg->length) { - if (!--pc->sg_cnt) - break; - pc->sg = sg = sg_next(sg); - pc->b_count = 0; - } - } - - if (bcount) { - printk(KERN_ERR "%s: %d leftover bytes, %s\n", drive->name, - bcount, write ? "padding with zeros" - : "discarding data"); - ide_pad_transfer(drive, write, bcount); - } - - return done; -} -EXPORT_SYMBOL_GPL(ide_io_buffers); - void ide_init_pc(struct ide_atapi_pc *pc) { memset(pc, 0, sizeof(*pc)); @@ -324,12 +276,14 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) { struct ide_atapi_pc *pc = drive->pc; ide_hwif_t *hwif = drive->hwif; + struct ide_cmd *cmd = &hwif->cmd; struct request *rq = hwif->rq; const struct ide_tp_ops *tp_ops = hwif->tp_ops; xfer_func_t *xferfunc; - unsigned int timeout, temp; + unsigned int timeout, done; u16 bcount; u8 stat, ireason, dsc = 0; + u8 write = !!(pc->flags & PC_FLAG_WRITING); debug_log("Enter %s - interrupt handler\n", __func__); @@ -340,8 +294,13 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) stat = tp_ops->read_status(hwif); if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { - if (hwif->dma_ops->dma_end(drive) || - (drive->media == ide_tape && (stat & ATA_ERR))) { + int rc; + + drive->waiting_for_dma = 0; + rc = hwif->dma_ops->dma_end(drive); + ide_dma_unmap_sg(drive, cmd); + + if (rc || (drive->media == ide_tape && (stat & ATA_ERR))) { if (drive->media == ide_floppy) printk(KERN_ERR "%s: DMA %s error\n", drive->name, rq_data_dir(pc->rq) @@ -357,7 +316,8 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) /* No more interrupts */ if ((stat & ATA_DRQ) == 0) { - int uptodate; + int uptodate, error; + unsigned int done; debug_log("Packet command completed, %d bytes transferred\n", pc->xferred); @@ -404,16 +364,24 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) if (blk_special_request(rq)) { rq->errors = 0; - ide_complete_rq(drive, 0, blk_rq_bytes(rq)); + done = blk_rq_bytes(rq); + error = 0; } else { + if (blk_fs_request(rq) == 0 && uptodate <= 0) { if (rq->errors == 0) rq->errors = -EIO; } - ide_complete_rq(drive, uptodate ? 0 : -EIO, - ide_rq_bytes(rq)); + + if (drive->media == ide_tape) + done = ide_rq_bytes(rq); /* FIXME */ + else + done = blk_rq_bytes(rq); + + error = uptodate ? 0 : -EIO; } + ide_complete_rq(drive, error, done); return ide_stopped; } @@ -433,8 +401,7 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) return ide_do_reset(drive); } - if (((ireason & ATAPI_IO) == ATAPI_IO) == - !!(pc->flags & PC_FLAG_WRITING)) { + if (((ireason & ATAPI_IO) == ATAPI_IO) == write) { /* Hopefully, we will never get here */ printk(KERN_ERR "%s: We wanted to %s, but the device wants us " "to %s!\n", drive->name, @@ -443,45 +410,30 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) return ide_do_reset(drive); } - if (!(pc->flags & PC_FLAG_WRITING)) { - /* Reading - Check that we have enough space */ - temp = pc->xferred + bcount; - if (temp > pc->req_xfer) { - if (temp > pc->buf_size) { - printk(KERN_ERR "%s: The device wants to send " - "us more data than expected - " - "discarding data\n", - drive->name); - - ide_pad_transfer(drive, 0, bcount); - goto next_irq; - } - debug_log("The device wants to send us more data than " - "expected - allowing transfer\n"); - } - xferfunc = tp_ops->input_data; - } else - xferfunc = tp_ops->output_data; - - if ((drive->media == ide_floppy && !pc->buf) || - (drive->media == ide_tape && pc->bh)) { - int done = drive->pc_io_buffers(drive, pc, bcount, - !!(pc->flags & PC_FLAG_WRITING)); - - /* FIXME: don't do partial completions */ - if (drive->media == ide_floppy) - ide_complete_rq(drive, 0, - done ? done : ide_rq_bytes(rq)); - } else - xferfunc(drive, NULL, pc->cur_pos, bcount); + xferfunc = write ? tp_ops->output_data : tp_ops->input_data; + + if (drive->media == ide_floppy && pc->buf == NULL) { + done = min_t(unsigned int, bcount, cmd->nleft); + ide_pio_bytes(drive, cmd, write, done); + } else if (drive->media == ide_tape && pc->bh) { + done = drive->pc_io_buffers(drive, pc, bcount, write); + } else { + done = min_t(unsigned int, bcount, pc->req_xfer - pc->xferred); + xferfunc(drive, NULL, pc->cur_pos, done); + } /* Update the current position */ - pc->xferred += bcount; - pc->cur_pos += bcount; + pc->xferred += done; + pc->cur_pos += done; + + bcount -= done; + + if (bcount) + ide_pad_transfer(drive, write, bcount); + + debug_log("[cmd %x] transferred %d bytes, padded %d bytes\n", + rq->cmd[0], done, bcount); - debug_log("[cmd %x] transferred %d bytes on that intr.\n", - rq->cmd[0], bcount); -next_irq: /* And set the interrupt handler again */ ide_set_handler(drive, ide_pc_intr, timeout); return ide_started; @@ -611,6 +563,10 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) : ide_pc_intr), timeout); + /* Send the actual packet */ + if ((drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) == 0) + hwif->tp_ops->output_data(drive, NULL, rq->cmd, cmd_len); + /* Begin DMA, if necessary */ if (dev_is_idecd(drive)) { if (drive->dma) @@ -622,10 +578,6 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) } } - /* Send the actual packet */ - if ((drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) == 0) - hwif->tp_ops->output_data(drive, NULL, rq->cmd, cmd_len); - return ide_started; } @@ -633,7 +585,6 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_cmd *cmd) { struct ide_atapi_pc *pc; ide_hwif_t *hwif = drive->hwif; - const struct ide_dma_ops *dma_ops = hwif->dma_ops; ide_expiry_t *expiry = NULL; struct request *rq = hwif->rq; unsigned int timeout; @@ -647,12 +598,8 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_cmd *cmd) expiry = ide_cd_expiry; timeout = ATAPI_WAIT_PC; - if (drive->dma) { - if (ide_build_sglist(drive, cmd)) - drive->dma = !dma_ops->dma_setup(drive, cmd); - else - drive->dma = 0; - } + if (drive->dma) + drive->dma = !ide_dma_prepare(drive, cmd); } else { pc = drive->pc; @@ -670,13 +617,8 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_cmd *cmd) ide_dma_off(drive); } - if ((pc->flags & PC_FLAG_DMA_OK) && - (drive->dev_flags & IDE_DFLAG_USING_DMA)) { - if (ide_build_sglist(drive, cmd)) - drive->dma = !dma_ops->dma_setup(drive, cmd); - else - drive->dma = 0; - } + if (pc->flags & PC_FLAG_DMA_OK) + drive->dma = !ide_dma_prepare(drive, cmd); if (!drive->dma) pc->flags &= ~PC_FLAG_DMA_OK; |