diff options
| author | Jeff Garzik <jgarzik@pobox.com> | 2004-10-19 04:54:44 -0400 |
|---|---|---|
| committer | Jeff Garzik <jgarzik@pobox.com> | 2004-10-19 04:54:44 -0400 |
| commit | a034f8d6b2d8960421a2633f12b0b658b3bea667 (patch) | |
| tree | 5fe5a9642bad0f97efb48ad3a1d955213ee27010 | |
| parent | e2e875d9807f7f1caa066ede93c943311a21229a (diff) | |
| parent | afa386d79e960dff6430ee8ffcfec4bff2a464fa (diff) | |
Merge pobox.com:/spare/repo/libata-dev/atapi
into pobox.com:/spare/repo/libata-2.6
| -rw-r--r-- | drivers/scsi/libata-core.c | 204 | ||||
| -rw-r--r-- | drivers/scsi/libata-scsi.c | 12 | ||||
| -rw-r--r-- | include/linux/libata.h | 4 |
3 files changed, 177 insertions, 43 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 6e8042f94336..cc18aa14c71b 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -39,6 +39,7 @@ #include <linux/workqueue.h> #include <scsi/scsi.h> #include "scsi.h" +#include "scsi_priv.h" #include <scsi/scsi_host.h> #include <linux/libata.h> #include <asm/io.h> @@ -58,6 +59,7 @@ static int ata_choose_xfer_mode(struct ata_port *ap, u8 *xfer_mode_out, unsigned int *xfer_shift_out); static int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat); +static void __ata_qc_complete(struct ata_queued_cmd *qc); static unsigned int ata_unique_id = 1; static struct workqueue_struct *ata_wq; @@ -2193,11 +2195,50 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) kunmap(page); } -static void atapi_pio_sector(struct ata_queued_cmd *qc) +static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes) +{ + int do_write = (qc->tf.flags & ATA_TFLAG_WRITE); + struct scatterlist *sg = qc->sg; + struct ata_port *ap = qc->ap; + struct page *page; + unsigned char *buf; + unsigned int count; + + if (qc->curbytes == qc->nbytes - bytes) + ap->pio_task_state = PIO_ST_LAST; + +next_sg: + sg = &qc->sg[qc->cursg]; + page = sg->page; + + count = min(sg_dma_len(sg) - qc->cursg_ofs, bytes); + buf = kmap(page) + sg->offset + qc->cursg_ofs; + + bytes -= count; + qc->curbytes += count; + qc->cursg_ofs += count; + + if (qc->cursg_ofs == sg_dma_len(sg)) { + qc->cursg++; + qc->cursg_ofs = 0; + } + + DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read"); + + /* do the actual data transfer */ + ata_data_xfer(ap, buf, count, do_write); + + kunmap(page); + + if (bytes) + goto next_sg; +} + +static void atapi_pio_bytes(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; struct ata_device *dev = qc->dev; - unsigned int i, ireason, bc_lo, bc_hi, bytes; + unsigned int ireason, bc_lo, bc_hi, bytes; int i_write, do_write = (qc->tf.flags & ATA_TFLAG_WRITE) ? 1 : 0; ap->ops->tf_read(ap, &qc->tf); @@ -2215,16 +2256,7 @@ static void atapi_pio_sector(struct ata_queued_cmd *qc) if (do_write != i_write) goto err_out; - /* make sure byte count is multiple of sector size; not - * required by standard (warning! warning!), but IDE driver - * does this to simplify things a bit. We are lazy, and - * follow suit. - */ - if (bytes & (ATA_SECT_SIZE - 1)) - goto err_out; - - for (i = 0; i < (bytes >> 9); i++) - ata_pio_sector(qc); + __atapi_pio_bytes(qc, bytes); return; @@ -2265,19 +2297,30 @@ static void ata_pio_block(struct ata_port *ap) } } - /* handle BSY=0, DRQ=0 as error */ - if ((status & ATA_DRQ) == 0) { - ap->pio_task_state = PIO_ST_ERR; - return; - } - qc = ata_qc_from_tag(ap, ap->active_tag); assert(qc != NULL); - if (is_atapi_taskfile(&qc->tf)) - atapi_pio_sector(qc); - else + if (is_atapi_taskfile(&qc->tf)) { + /* no more data to transfer or unsupported ATAPI command */ + if ((status & ATA_DRQ) == 0) { + ap->pio_task_state = PIO_ST_IDLE; + + ata_irq_on(ap); + + ata_qc_complete(qc, status); + return; + } + + atapi_pio_bytes(qc); + } else { + /* handle BSY=0, DRQ=0 as error */ + if ((status & ATA_DRQ) == 0) { + ap->pio_task_state = PIO_ST_ERR; + return; + } + ata_pio_sector(qc); + } } static void ata_pio_error(struct ata_port *ap) @@ -2335,6 +2378,59 @@ static void ata_pio_task(void *_data) } } +static void atapi_request_sense(struct ata_port *ap, struct ata_device *dev, + struct scsi_cmnd *cmd) +{ + DECLARE_COMPLETION(wait); + struct ata_queued_cmd *qc; + unsigned long flags; + int using_pio = dev->flags & ATA_DFLAG_PIO; + int rc; + + DPRINTK("ATAPI request sense\n"); + + qc = ata_qc_new_init(ap, dev); + BUG_ON(qc == NULL); + + /* FIXME: is this needed? */ + memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); + + ata_sg_init_one(qc, cmd->sense_buffer, sizeof(cmd->sense_buffer)); + qc->pci_dma_dir = PCI_DMA_FROMDEVICE; + + memset(&qc->cdb, 0, sizeof(ap->cdb_len)); + qc->cdb[0] = REQUEST_SENSE; + qc->cdb[4] = SCSI_SENSE_BUFFERSIZE; + + qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + qc->tf.command = ATA_CMD_PACKET; + + if (using_pio) { + qc->tf.protocol = ATA_PROT_ATAPI; + qc->tf.lbam = (8 * 1024) & 0xff; + qc->tf.lbah = (8 * 1024) >> 8; + + qc->nbytes = SCSI_SENSE_BUFFERSIZE; + } else { + qc->tf.protocol = ATA_PROT_ATAPI_DMA; + qc->tf.feature |= ATAPI_PKT_DMA; + } + + qc->waiting = &wait; + qc->complete_fn = ata_qc_complete_noop; + + spin_lock_irqsave(&ap->host_set->lock, flags); + rc = ata_qc_issue(qc); + spin_unlock_irqrestore(&ap->host_set->lock, flags); + + if (rc) + ata_port_disable(ap); + else + wait_for_completion(&wait); + + DPRINTK("EXIT\n"); +} + /** * ata_qc_timeout - Handle timeout of queued command * @qc: Command that timed out @@ -2356,10 +2452,29 @@ static void ata_pio_task(void *_data) static void ata_qc_timeout(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; + struct ata_device *dev = qc->dev; u8 host_stat = 0, drv_stat; DPRINTK("ENTER\n"); + /* FIXME: doesn't this conflict with timeout handling? */ + if (qc->dev->class == ATA_DEV_ATAPI && qc->scsicmd) { + struct scsi_cmnd *cmd = qc->scsicmd; + + if (!scsi_eh_eflags_chk(cmd, SCSI_EH_CANCEL_CMD)) { + + /* finish completing original command */ + __ata_qc_complete(qc); + + atapi_request_sense(ap, dev, cmd); + + cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16); + scsi_finish_command(cmd); + + goto out; + } + } + /* hack alert! We cannot use the supplied completion * function from inside the ->eh_strategy_handler() thread. * libata is the only user of ->eh_strategy_handler() in @@ -2393,7 +2508,7 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc) ata_qc_complete(qc, drv_stat); break; } - +out: DPRINTK("EXIT\n"); } @@ -2482,6 +2597,7 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap, qc->dev = dev; qc->cursect = qc->cursg = qc->cursg_ofs = 0; qc->nsect = 0; + qc->nbytes = qc->curbytes = 0; ata_tf_init(ap, &qc->tf, dev->devno); @@ -2497,6 +2613,30 @@ static int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat) return 0; } +static void __ata_qc_complete(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + unsigned int tag, do_clear = 0; + + qc->flags = 0; + tag = qc->tag; + if (likely(ata_tag_valid(tag))) { + if (tag == ap->active_tag) + ap->active_tag = ATA_TAG_POISON; + qc->tag = ATA_TAG_POISON; + do_clear = 1; + } + + if (qc->waiting) { + struct completion *waiting = qc->waiting; + qc->waiting = NULL; + complete(waiting); + } + + if (likely(do_clear)) + clear_bit(tag, &ap->qactive); +} + /** * ata_qc_complete - Complete an active ATA command * @qc: Command to complete @@ -2508,8 +2648,6 @@ static int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat) void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) { - struct ata_port *ap = qc->ap; - unsigned int tag, do_clear = 0; int rc; assert(qc != NULL); /* ata_qc_from_tag _might_ return NULL */ @@ -2527,23 +2665,7 @@ void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) if (rc != 0) return; - qc->flags = 0; - tag = qc->tag; - if (likely(ata_tag_valid(tag))) { - if (tag == ap->active_tag) - ap->active_tag = ATA_TAG_POISON; - qc->tag = ATA_TAG_POISON; - do_clear = 1; - } - - if (qc->waiting) { - struct completion *waiting = qc->waiting; - qc->waiting = NULL; - complete(waiting); - } - - if (likely(do_clear)) - clear_bit(tag, &ap->qactive); + __ata_qc_complete(qc); VPRINTK("EXIT\n"); } diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index a086d9f01adc..eb94fe94285e 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -1248,9 +1248,15 @@ static int atapi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) { struct scsi_cmnd *cmd = qc->scsicmd; - if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) + if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) { + DPRINTK("request check condition\n"); + cmd->result = SAM_STAT_CHECK_CONDITION; - else { + + qc->scsidone(cmd); + + return 1; + } else { u8 *scsicmd = cmd->cmnd; if (scsicmd[0] == INQUIRY) { @@ -1322,6 +1328,8 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) #endif } + qc->nbytes = cmd->bufflen; + return 0; } diff --git a/include/linux/libata.h b/include/linux/libata.h index 407b4adc06d1..06b42245ee99 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -230,6 +230,10 @@ struct ata_queued_cmd { unsigned int nsect; unsigned int cursect; + + unsigned int nbytes; + unsigned int curbytes; + unsigned int cursg; unsigned int cursg_ofs; |
