diff options
Diffstat (limited to 'drivers/ide/ide-atapi.c')
-rw-r--r-- | drivers/ide/ide-atapi.c | 116 |
1 files changed, 86 insertions, 30 deletions
diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index e9d042dba0e..2fb5d28a9be 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -149,7 +149,10 @@ static void ide_queue_pc_head(ide_drive_t *drive, struct gendisk *disk, memcpy(rq->cmd, pc->c, 12); if (drive->media == ide_tape) rq->cmd[13] = REQ_IDETAPE_PC1; - ide_do_drive_cmd(drive, rq); + + drive->hwif->rq = NULL; + + elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 0); } /* @@ -297,6 +300,21 @@ int ide_cd_get_xferlen(struct request *rq) } EXPORT_SYMBOL_GPL(ide_cd_get_xferlen); +void ide_read_bcount_and_ireason(ide_drive_t *drive, u16 *bcount, u8 *ireason) +{ + struct ide_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.tf_flags = IDE_TFLAG_IN_LBAH | IDE_TFLAG_IN_LBAM | + IDE_TFLAG_IN_NSECT; + + drive->hwif->tp_ops->tf_read(drive, &cmd); + + *bcount = (cmd.tf.lbah << 8) | cmd.tf.lbam; + *ireason = cmd.tf.nsect & 3; +} +EXPORT_SYMBOL_GPL(ide_read_bcount_and_ireason); + /* * This is the usual interrupt handler which will be called during a packet * command. We will transfer some of the data (as requested by the drive) @@ -318,11 +336,6 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD : WAIT_TAPE_CMD; - if (pc->flags & PC_FLAG_TIMEDOUT) { - drive->pc_callback(drive, 0); - return ide_stopped; - } - /* Clear the interrupt */ stat = tp_ops->read_status(hwif); @@ -344,6 +357,8 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) /* No more interrupts */ if ((stat & ATA_DRQ) == 0) { + int uptodate; + debug_log("Packet command completed, %d bytes transferred\n", pc->xferred); @@ -382,7 +397,22 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) dsc = 1; /* Command finished - Call the callback function */ - drive->pc_callback(drive, dsc); + uptodate = drive->pc_callback(drive, dsc); + + if (uptodate == 0) + drive->failed_pc = NULL; + + if (blk_special_request(rq)) { + rq->errors = 0; + ide_complete_rq(drive, 0, blk_rq_bytes(rq)); + } 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)); + } return ide_stopped; } @@ -440,7 +470,8 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) /* FIXME: don't do partial completions */ if (drive->media == ide_floppy) - ide_end_request(drive, 1, done >> 9); + ide_complete_rq(drive, 0, + done ? done : ide_rq_bytes(rq)); } else xferfunc(drive, NULL, pc->cur_pos, bcount); @@ -452,20 +483,32 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) rq->cmd[0], bcount); next_irq: /* And set the interrupt handler again */ - ide_set_handler(drive, ide_pc_intr, timeout, NULL); + ide_set_handler(drive, ide_pc_intr, timeout); return ide_started; } +static void ide_init_packet_cmd(struct ide_cmd *cmd, u32 tf_flags, + u16 bcount, u8 dma) +{ + cmd->protocol = dma ? ATAPI_PROT_DMA : ATAPI_PROT_PIO; + cmd->tf_flags |= IDE_TFLAG_OUT_LBAH | IDE_TFLAG_OUT_LBAM | + IDE_TFLAG_OUT_FEATURE | tf_flags; + cmd->tf.command = ATA_CMD_PACKET; + cmd->tf.feature = dma; /* Use PIO/DMA */ + cmd->tf.lbam = bcount & 0xff; + cmd->tf.lbah = (bcount >> 8) & 0xff; +} + static u8 ide_read_ireason(ide_drive_t *drive) { - ide_task_t task; + struct ide_cmd cmd; - memset(&task, 0, sizeof(task)); - task.tf_flags = IDE_TFLAG_IN_NSECT; + memset(&cmd, 0, sizeof(cmd)); + cmd.tf_flags = IDE_TFLAG_IN_NSECT; - drive->hwif->tp_ops->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &cmd); - return task.tf.nsect & 3; + return cmd.tf.nsect & 3; } static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason) @@ -560,11 +603,13 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) } } + hwif->expiry = expiry; + /* Set the interrupt routine */ ide_set_handler(drive, (dev_is_idecd(drive) ? drive->irq_handler : ide_pc_intr), - timeout, expiry); + timeout); /* Begin DMA, if necessary */ if (dev_is_idecd(drive)) { @@ -584,23 +629,30 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) return ide_started; } -ide_startstop_t ide_issue_pc(ide_drive_t *drive) +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; u32 tf_flags; u16 bcount; + u8 drq_int = !!(drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT); if (dev_is_idecd(drive)) { tf_flags = IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL; - bcount = ide_cd_get_xferlen(hwif->rq); + bcount = ide_cd_get_xferlen(rq); expiry = ide_cd_expiry; timeout = ATAPI_WAIT_PC; - if (drive->dma) - drive->dma = !hwif->dma_ops->dma_setup(drive); + if (drive->dma) { + if (ide_build_sglist(drive, cmd)) + drive->dma = !dma_ops->dma_setup(drive, cmd); + else + drive->dma = 0; + } } else { pc = drive->pc; @@ -619,8 +671,12 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive) } if ((pc->flags & PC_FLAG_DMA_OK) && - (drive->dev_flags & IDE_DFLAG_USING_DMA)) - drive->dma = !hwif->dma_ops->dma_setup(drive); + (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 (!drive->dma) pc->flags &= ~PC_FLAG_DMA_OK; @@ -629,18 +685,18 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive) : WAIT_TAPE_CMD; } - ide_pktcmd_tf_load(drive, tf_flags, bcount, drive->dma); + ide_init_packet_cmd(cmd, tf_flags, bcount, drive->dma); - /* Issue the packet command */ - if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { + (void)do_rw_taskfile(drive, cmd); + + if (drq_int) { if (drive->dma) drive->waiting_for_dma = 0; - ide_execute_command(drive, ATA_CMD_PACKET, ide_transfer_pc, - timeout, expiry); - return ide_started; - } else { - ide_execute_pkt_cmd(drive); - return ide_transfer_pc(drive); + hwif->expiry = expiry; } + + ide_execute_command(drive, cmd, ide_transfer_pc, timeout); + + return drq_int ? ide_started : ide_transfer_pc(drive); } EXPORT_SYMBOL_GPL(ide_issue_pc); |