diff -uprN linux-2.6.9-vanilla/drivers/scsi/libata-core.c linux-2.6.9/drivers/scsi/libata-core.c --- linux-2.6.9-vanilla/drivers/scsi/libata-core.c 2004-10-19 05:53:06.000000000 +0800 +++ linux-2.6.9/drivers/scsi/libata-core.c 2004-12-14 14:08:00.202412224 +0800 @@ -3655,3 +3655,16 @@ EXPORT_SYMBOL_GPL(ata_scsi_release); EXPORT_SYMBOL_GPL(ata_host_intr); EXPORT_SYMBOL_GPL(ata_dev_classify); EXPORT_SYMBOL_GPL(ata_dev_id_string); +EXPORT_SYMBOL_GPL(ata_scsi_rbuf_fill); +EXPORT_SYMBOL_GPL(ata_scsiop_inq_00); +EXPORT_SYMBOL_GPL(ata_scsiop_noop); +EXPORT_SYMBOL_GPL(ata_scsi_badcmd); +EXPORT_SYMBOL_GPL(ata_scsiop_inq_80); +EXPORT_SYMBOL_GPL(ata_scsiop_inq_83); +EXPORT_SYMBOL_GPL(ata_scsiop_mode_sense); +EXPORT_SYMBOL_GPL(ata_scsiop_read_cap); +EXPORT_SYMBOL_GPL(ata_scsiop_inq_std); +EXPORT_SYMBOL_GPL(ata_to_sense_error); +EXPORT_SYMBOL_GPL(ata_scsiop_report_luns); +EXPORT_SYMBOL_GPL(ata_qc_new_init); +EXPORT_SYMBOL_GPL(ata_qc_issue); diff -uprN linux-2.6.9-vanilla/drivers/scsi/sata_uli.c linux-2.6.9/drivers/scsi/sata_uli.c --- linux-2.6.9-vanilla/drivers/scsi/sata_uli.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-2.6.9/drivers/scsi/sata_uli.c 2004-12-14 14:07:52.935516960 +0800 @@ -0,0 +1,1522 @@ +/* + * sata_uli.c - ULi Electronics SATA + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, in which case the provisions of + * the GPL are applicable instead of the above. If you wish to allow + * the use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under + * the OSL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the OSL or the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include + +#define DRV_NAME "sata_uli" +#define DRV_VERSION "0.11" + +//add by clear zhang 2004/12/13 +#define print_clear(_x_) printk _x_ +typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, u8 *scsicmd); +static u8 iftimeout=0; +//end add + +enum { + uli_5289 = 0, + uli_5287 = 1, + uli_5281 = 2, + + /* PCI configuration registers *///modiy by clear chung 2004/12/7 + //ULI_SCR_BASE = 0x90, /* sata0 phy SCR registers */ + //ULI_SATA1_OFS = 0x10, /* offset from sata0->sata1 phy regs */ + ULI5287_BASE = 0x90, /* sata0 phy SCR registers */ + ULI5287_OFFS = 0x10, /* offset from sata0->sata1 phy regs */ + ULI5281_BASE = 0x60, /* sata0 phy SCR registers */ + ULI5281_OFFS = 0x60, /* offset from sata0->sata1 phy regs */ + +}; + +static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg); +static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); + +//add by clear zhang 2004/12/13 +static struct ata_device *ata_scsi_find_dev_uli(struct ata_port *ap, struct scsi_device *scsidev); +static unsigned int ata_scsi_flush_xlat_uli(struct ata_queued_cmd *qc, u8 *scsicmd); +static unsigned int ata_scsi_verify_xlat_uli(struct ata_queued_cmd *qc, u8 *scsicmd); +static unsigned int ata_scsi_rw_xlat_uli(struct ata_queued_cmd *qc, u8 *scsicmd); +static inline ata_xlat_func_t ata_get_xlat_func_uli(struct ata_device *dev, u8 cmd); +struct ata_queued_cmd *ata_scsi_qc_new_uli(struct ata_port *ap, + struct ata_device *dev, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)); +static int ata_scsi_qc_complete_uli(struct ata_queued_cmd *qc, u8 drv_stat); +static void ata_scsi_translate_uli(struct ata_port *ap, struct ata_device *dev, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *), + ata_xlat_func_t xlat_func); +static void ata_scsi_simulate_uli(struct ata_port *ap, struct ata_device *dev, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)); +static unsigned int ata_scsi_rbuf_get_uli(struct scsi_cmnd *cmd, u8 **buf_out); +static inline void ata_scsi_rbuf_put_uli(struct scsi_cmnd *cmd); +static int atapi_qc_complete_uli(struct ata_queued_cmd *qc, u8 drv_stat); +static unsigned int atapi_xlat_uli(struct ata_queued_cmd *qc, u8 *scsicmd); +static int ata_scsi_queuecmd_uli(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)); + +static void ata_qc_timeout_uli(struct ata_queued_cmd *qc); +static void ata_eng_timeout_uli(struct ata_port *ap); + +///////////////////// +struct ata_scsi_args { + struct ata_port *ap; + struct ata_device *dev; + struct scsi_cmnd *cmd; + void (*done)(struct scsi_cmnd *); +}; +/* libata-core.c */ +extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap, struct ata_device *dev); +extern int ata_qc_issue(struct ata_queued_cmd *qc); + +/* libata-scsi.c */ +extern void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat); + +extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen); +extern unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen); +extern unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen); +extern unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen); +extern unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen); +extern unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen); +extern unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen); +extern unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen); +extern void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8 asc, u8 ascq); +extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args, unsigned int (*actor) (struct ata_scsi_args *args, + u8 *rbuf, unsigned int buflen)); + +static inline void ata_bad_scsiop(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)); +static inline void ata_bad_cdb(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)); +static inline void ata_bad_scsiop(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +{ + ata_scsi_badcmd(cmd, done, 0x20, 0x00); +} + +static inline void ata_bad_cdb(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +{ + ata_scsi_badcmd(cmd, done, 0x24, 0x00); +} + +static struct pci_device_id uli_pci_tbl[] = { + { PCI_VENDOR_ID_AL, 0x5289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5289 }, + { PCI_VENDOR_ID_AL, 0x5287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5287 }, + { PCI_VENDOR_ID_AL, 0x5281, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5281 }, + { } /* terminate list */ +}; + + +static struct pci_driver uli_pci_driver = { + .name = DRV_NAME, + .id_table = uli_pci_tbl, + .probe = uli_init_one, + .remove = ata_pci_remove_one, +}; + +static Scsi_Host_Template uli_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd_uli, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = ATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations uli_ops = { + .port_disable = ata_port_disable, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + .phy_reset = sata_phy_reset, + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout_uli, + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + .scr_read = uli_scr_read, + .scr_write = uli_scr_write, + .port_start = ata_port_start, + .port_stop = ata_port_stop, +}; + +static struct ata_port_info uli_port_info = { + .sht = &uli_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET | + ATA_FLAG_NO_LEGACY, + .pio_mask = 0x1f, + .mwdma_mask = 0x7, + .udma_mask = 0x7f, + .port_ops = &uli_ops, +}; + +MODULE_AUTHOR("Peer Chen"); +MODULE_DESCRIPTION("low-level driver for ULi Electronics SATA controller"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, uli_pci_tbl); + +static unsigned int get_scr_cfg_addr(struct ata_port *ap, unsigned int sc_reg) +{ + return ap->ioaddr.scr_addr + (4 * sc_reg); +} + +static u32 uli_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg) +{ + unsigned int cfg_addr = get_scr_cfg_addr(ap, sc_reg); + u32 val; + + pci_read_config_dword(ap->host_set->pdev, cfg_addr, &val); + return val; +} + +static void uli_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val) +{ + unsigned int cfg_addr = get_scr_cfg_addr(ap, scr); + + pci_write_config_dword(ap->host_set->pdev, cfg_addr, val); +} + +static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg) +{ + if (sc_reg > SCR_CONTROL) + return 0xffffffffU; + + return uli_scr_cfg_read(ap, sc_reg); +} + +static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +{ + if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0 + return; + + uli_scr_cfg_write(ap, sc_reg, val); +} + +/* move to PCI layer, integrate w/ MSI stuff */ +static void pci_enable_intx(struct pci_dev *pdev) +{ + u16 pci_command; + + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + if (pci_command & PCI_COMMAND_INTX_DISABLE) { + pci_command &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, pci_command); + } +} + +static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct ata_probe_ent *probe_ent = NULL; + int rc; + unsigned int board_idx = (unsigned int) ent->driver_data; + struct ata_port_info *ppi; + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + + ppi = &uli_port_info; + probe_ent = ata_pci_init_native_mode(pdev, &ppi); + + if (!probe_ent) { + rc = -ENOMEM; + goto err_out_regions; + } + + switch (board_idx) { + case uli_5287: + probe_ent->port[0].scr_addr = ULI5287_BASE; + probe_ent->port[1].scr_addr = ULI5287_BASE + ULI5287_OFFS; + probe_ent->n_ports = 4; + + probe_ent->port[2].cmd_addr = pci_resource_start(pdev, 0) + 8; + probe_ent->port[2].altstatus_addr = + probe_ent->port[2].ctl_addr = + (pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS) + 4; + probe_ent->port[2].bmdma_addr = pci_resource_start(pdev, 4) + 16; + probe_ent->port[2].scr_addr = ULI5287_BASE + ULI5287_OFFS*4; + + probe_ent->port[3].cmd_addr = pci_resource_start(pdev, 2) + 8; + probe_ent->port[3].altstatus_addr = + probe_ent->port[3].ctl_addr = + (pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS) + 4; + probe_ent->port[3].bmdma_addr = pci_resource_start(pdev, 4) + 24; + probe_ent->port[3].scr_addr = ULI5287_BASE + ULI5287_OFFS*5; + + ata_std_ports(&probe_ent->port[2]); + ata_std_ports(&probe_ent->port[3]); + break; + + case uli_5289: + probe_ent->port[0].scr_addr = ULI5287_BASE; + probe_ent->port[1].scr_addr = ULI5287_BASE + ULI5287_OFFS; + break; + + case uli_5281: + probe_ent->port[0].scr_addr = ULI5281_BASE; + probe_ent->port[1].scr_addr = ULI5281_BASE + ULI5281_OFFS; + break; + + default: + BUG(); + break; + } + pci_set_master(pdev); + pci_enable_intx(pdev); + + /* FIXME: check ata_device_add return value */ + ata_device_add(probe_ent); + kfree(probe_ent); + + return 0; + +err_out_regions: + pci_release_regions(pdev); + +err_out: + pci_disable_device(pdev); + return rc; + +} + +static int __init uli_init(void) +{ + return pci_module_init(&uli_pci_driver); +} + +static void __exit uli_exit(void) +{ + pci_unregister_driver(&uli_pci_driver); +} + +//add by clear zhang 2004/12/13 + +/** + * ata_scsi_find_dev_uli - lookup ata_device from scsi_cmnd + * @ap: ATA port to which the device is attached + * @cmd: SCSI command to be sent to the device + * + * Given various information provided in struct scsi_cmnd, + * map that onto an ATA bus, and using that mapping + * determine which ata_device is associated with the + * SCSI command to be sent. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Associated ATA device, or %NULL if not found. + */ + +static struct ata_device * +ata_scsi_find_dev_uli(struct ata_port *ap, struct scsi_device *scsidev) +{ + struct ata_device *dev; + + /* skip commands not addressed to targets we simulate */ + if (likely(scsidev->id < ATA_MAX_DEVICES)) + dev = &ap->device[scsidev->id]; + else + return NULL; + + if (unlikely((scsidev->channel != 0) || + (scsidev->lun != 0))) + return NULL; + + if (unlikely(!ata_dev_present(dev))) + return NULL; + +#ifndef ATA_ENABLE_ATAPI + if (unlikely(dev->class == ATA_DEV_ATAPI)) + return NULL; +#endif + + return dev; +} + +////////////////////////////////////////////////////////////////// +/** + * ata_scsi_flush_xlat_uli - Translate SCSI SYNCHRONIZE CACHE command + * @qc: Storage for translated ATA taskfile + * @scsicmd: SCSI command to translate (ignored) + * + * Sets up an ATA taskfile to issue FLUSH CACHE or + * FLUSH CACHE EXT. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Zero on success, non-zero on error. + */ + +static unsigned int ata_scsi_flush_xlat_uli(struct ata_queued_cmd *qc, u8 *scsicmd) +{ + struct ata_taskfile *tf = &qc->tf; + + tf->flags |= ATA_TFLAG_DEVICE; + tf->protocol = ATA_PROT_NODATA; + + if ((tf->flags & ATA_TFLAG_LBA48) && + (ata_id_has_flush_ext(qc->dev))) + tf->command = ATA_CMD_FLUSH_EXT; + else + tf->command = ATA_CMD_FLUSH; + + return 0; +} + +/** + * ata_scsi_verify_xlat_uli - Translate SCSI VERIFY command into an ATA one + * @qc: Storage for translated ATA taskfile + * @scsicmd: SCSI command to translate + * + * Converts SCSI VERIFY command to an ATA READ VERIFY command. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Zero on success, non-zero on error. + */ + +static unsigned int ata_scsi_verify_xlat_uli(struct ata_queued_cmd *qc, u8 *scsicmd) +{ + struct ata_taskfile *tf = &qc->tf; + unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48; + u64 dev_sectors = qc->dev->n_sectors; + u64 sect = 0; + u32 n_sect = 0; + + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf->protocol = ATA_PROT_NODATA; + tf->device |= ATA_LBA; + + if (scsicmd[0] == VERIFY) { + sect |= ((u64)scsicmd[2]) << 24; + sect |= ((u64)scsicmd[3]) << 16; + sect |= ((u64)scsicmd[4]) << 8; + sect |= ((u64)scsicmd[5]); + + n_sect |= ((u32)scsicmd[7]) << 8; + n_sect |= ((u32)scsicmd[8]); + } + + else if (scsicmd[0] == VERIFY_16) { + sect |= ((u64)scsicmd[2]) << 56; + sect |= ((u64)scsicmd[3]) << 48; + sect |= ((u64)scsicmd[4]) << 40; + sect |= ((u64)scsicmd[5]) << 32; + sect |= ((u64)scsicmd[6]) << 24; + sect |= ((u64)scsicmd[7]) << 16; + sect |= ((u64)scsicmd[8]) << 8; + sect |= ((u64)scsicmd[9]); + + n_sect |= ((u32)scsicmd[10]) << 24; + n_sect |= ((u32)scsicmd[11]) << 16; + n_sect |= ((u32)scsicmd[12]) << 8; + n_sect |= ((u32)scsicmd[13]); + } + + else + return 1; + + if (!n_sect) + return 1; + if (sect >= dev_sectors) + return 1; + if ((sect + n_sect) > dev_sectors) + return 1; + if (lba48) { + if (n_sect > (64 * 1024)) + return 1; + } else { + if (n_sect > 256) + return 1; + } + + if (lba48) { + tf->hob_nsect = (n_sect >> 8) & 0xff; + + tf->hob_lbah = (sect >> 40) & 0xff; + tf->hob_lbam = (sect >> 32) & 0xff; + tf->hob_lbal = (sect >> 24) & 0xff; + } else + tf->device |= (sect >> 24) & 0xf; + + tf->nsect = n_sect & 0xff; + + tf->hob_lbah = (sect >> 16) & 0xff; + tf->hob_lbam = (sect >> 8) & 0xff; + tf->hob_lbal = sect & 0xff; + + return 0; +} + +/** + * ata_scsi_rw_xlat_uli - Translate SCSI r/w command into an ATA one + * @qc: Storage for translated ATA taskfile + * @scsicmd: SCSI command to translate + * + * Converts any of six SCSI read/write commands into the + * ATA counterpart, including starting sector (LBA), + * sector count, and taking into account the device's LBA48 + * support. + * + * Commands %READ_6, %READ_10, %READ_16, %WRITE_6, %WRITE_10, and + * %WRITE_16 are currently supported. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Zero on success, non-zero on error. + */ + +static unsigned int ata_scsi_rw_xlat_uli(struct ata_queued_cmd *qc, u8 *scsicmd) +{ + struct ata_taskfile *tf = &qc->tf; + unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48; + + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf->protocol = qc->dev->xfer_protocol; + tf->device |= ATA_LBA; + + if (scsicmd[0] == READ_10 || scsicmd[0] == READ_6 || + scsicmd[0] == READ_16) { + tf->command = qc->dev->read_cmd; + } else { + tf->command = qc->dev->write_cmd; + tf->flags |= ATA_TFLAG_WRITE; + } + + if (scsicmd[0] == READ_10 || scsicmd[0] == WRITE_10) { + if (lba48) { + tf->hob_nsect = scsicmd[7]; + tf->hob_lbal = scsicmd[2]; + + qc->nsect = ((unsigned int)scsicmd[7] << 8) | + scsicmd[8]; + } else { + /* if we don't support LBA48 addressing, the request + * -may- be too large. */ + if ((scsicmd[2] & 0xf0) || scsicmd[7]) + return 1; + + /* stores LBA27:24 in lower 4 bits of device reg */ + tf->device |= scsicmd[2]; + + qc->nsect = scsicmd[8]; + } + + tf->nsect = scsicmd[8]; + tf->lbal = scsicmd[5]; + tf->lbam = scsicmd[4]; + tf->lbah = scsicmd[3]; + + VPRINTK("ten-byte command\n"); + return 0; + } + + if (scsicmd[0] == READ_6 || scsicmd[0] == WRITE_6) { + qc->nsect = tf->nsect = scsicmd[4]; + tf->lbal = scsicmd[3]; + tf->lbam = scsicmd[2]; + tf->lbah = scsicmd[1] & 0x1f; /* mask out reserved bits */ + + VPRINTK("six-byte command\n"); + return 0; + } + + if (scsicmd[0] == READ_16 || scsicmd[0] == WRITE_16) { + /* rule out impossible LBAs and sector counts */ + if (scsicmd[2] || scsicmd[3] || scsicmd[10] || scsicmd[11]) + return 1; + + if (lba48) { + tf->hob_nsect = scsicmd[12]; + tf->hob_lbal = scsicmd[6]; + tf->hob_lbam = scsicmd[5]; + tf->hob_lbah = scsicmd[4]; + + qc->nsect = ((unsigned int)scsicmd[12] << 8) | + scsicmd[13]; + } else { + /* once again, filter out impossible non-zero values */ + if (scsicmd[4] || scsicmd[5] || scsicmd[12] || + (scsicmd[6] & 0xf0)) + return 1; + + /* stores LBA27:24 in lower 4 bits of device reg */ + tf->device |= scsicmd[2]; + + qc->nsect = scsicmd[13]; + } + + tf->nsect = scsicmd[13]; + tf->lbal = scsicmd[9]; + tf->lbam = scsicmd[8]; + tf->lbah = scsicmd[7]; + + VPRINTK("sixteen-byte command\n"); + return 0; + } + + DPRINTK("no-byte command\n"); + return 1; +} + +/** + * ata_get_xlat_func_uli - check if SCSI to ATA translation is possible + * @dev: ATA device + * @cmd: SCSI command opcode to consider + * + * Look up the SCSI command given, and determine whether the + * SCSI command is to be translated or simulated. + * + * RETURNS: + * Pointer to translation function if possible, %NULL if not. + */ + +static inline ata_xlat_func_t ata_get_xlat_func_uli(struct ata_device *dev, u8 cmd) +{ + switch (cmd) { + case READ_6: + case READ_10: + case READ_16: + + case WRITE_6: + case WRITE_10: + case WRITE_16: + return ata_scsi_rw_xlat_uli; + + case SYNCHRONIZE_CACHE: + if (ata_try_flush_cache(dev)) + return ata_scsi_flush_xlat_uli; + break; + + case VERIFY: + case VERIFY_16: + return ata_scsi_verify_xlat_uli; + } + + return NULL; +} +/////////////////////////////////////////////////////////////// + +/** + * ata_scsi_qc_new_uli - acquire new ata_queued_cmd reference + * @ap: ATA port to which the new command is attached + * @dev: ATA device to which the new command is attached + * @cmd: SCSI command that originated this ATA command + * @done: SCSI command completion function + * + * Obtain a reference to an unused ata_queued_cmd structure, + * which is the basic libata structure representing a single + * ATA command sent to the hardware. + * + * If a command was available, fill in the SCSI-specific + * portions of the structure with information on the + * current command. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Command allocated, or %NULL if none available. + */ +struct ata_queued_cmd *ata_scsi_qc_new_uli(struct ata_port *ap, + struct ata_device *dev, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)) +{ + struct ata_queued_cmd *qc; + + qc = ata_qc_new_init(ap, dev); + if (qc) { + qc->scsicmd = cmd; + qc->scsidone = done; + + if (cmd->use_sg) { + qc->sg = (struct scatterlist *) cmd->request_buffer; + qc->n_elem = cmd->use_sg; + } else { + qc->sg = &qc->sgent; + qc->n_elem = 1; + } + } else { + cmd->result = (DID_OK << 16) | (QUEUE_FULL << 1); + done(cmd); + } + + return qc; +} +///////////////////////////////////////////////////////////////////// + +static int ata_scsi_qc_complete_uli(struct ata_queued_cmd *qc, u8 drv_stat) +{ + struct scsi_cmnd *cmd = qc->scsicmd; + + // add by clear cheung 2004/12/7 for ULi M5281 workaroud + if(qc->ap->host_set->pdev->vendor==0x10b9 && + qc->ap->host_set->pdev->device==0x5281) + { + + struct pci_dev *pdev_m5281=qc->ap->host_set->pdev; + u8 m5281_revision; + + cmd->result = SAM_STAT_GOOD; + + pci_read_config_byte(pdev_m5281, PCI_REVISION_ID, &m5281_revision); + //printk("m5281_revision=0x%x\n",m5281_revision); + //version 0xa0,0xa1,0xb1 need workaroud + if(m5281_revision==0xa0 ||m5281_revision==0xa1 ||m5281_revision==0xb1 ) + { + u16 error_m5281; + unsigned long scr_addr_m5281 = qc->ap->ioaddr.scr_addr; + + pci_read_config_word(pdev_m5281,scr_addr_m5281+6, &error_m5281); + + if ( unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ)) && iftimeout==0/*express not timeout*/) + ata_to_sense_error(qc, drv_stat); + //time out or drive status is ok + else if ((error_m5281 & 0x3f8)!=0 || iftimeout==1) //the src errore present + { + u32 scr4_m5281; + u8 reset_m5281, stus, scr0_m5281; + + u8 kk, jj; + u8 comreset=1, softreset=1;//success=0, fail=1 + + printk("ULi sata error:0x%x\n",error_m5281); + //reset + // dma have been closed + //__save_flags(flags_m5281); + //__cli(); + //com reset + for(kk=0;kk<20;kk++) + { + pci_read_config_byte(pdev_m5281,scr_addr_m5281+8,&reset_m5281); + reset_m5281 = reset_m5281 & 0xfe; + pci_write_config_byte(pdev_m5281,scr_addr_m5281+8,reset_m5281); + reset_m5281 = reset_m5281 | 0x01; + pci_write_config_byte(pdev_m5281,scr_addr_m5281+8,reset_m5281); + + //delay two ms + mdelay(2); + + //reallert and clear erro bit + reset_m5281 = reset_m5281 & 0xfe; + pci_write_config_byte(pdev_m5281,scr_addr_m5281+8,reset_m5281); + mdelay(2); + + pci_read_config_byte(pdev_m5281,scr_addr_m5281,&scr0_m5281); + pci_read_config_dword(pdev_m5281,scr_addr_m5281+4,&scr4_m5281); + if(scr0_m5281!=0x13 ||(scr4_m5281 & 0x50000)!= 0x00050000 ) + { + print_clear(("phy reset failed\n")); + print_clear(("scr0=0x%x\n", scr0_m5281)); + print_clear(("scr4=0x%x\n", scr4_m5281)); + continue; + } + else + { + //print_clear(("com reset success\n")); + //clear error bits + pci_write_config_dword(pdev_m5281,scr_addr_m5281+4,scr4_m5281); + + comreset=0;//success + break; + } + + } + + if(comreset==0) + { + //stus= inb(qc->ap->ioaddr.status_addr);// get drive status + jj=0; + do + { + mdelay(2); + stus= inb(qc->ap->ioaddr.status_addr);// get drive status + jj++; + if(jj>=150) + { + break; + } + }while (stus!=0x50); + + //print_clear(("jj=0x%x\n", jj)); + + if(stus!=0x50 /*&& iftimeout==1*/) + { + //print_clear(("soft reset\n")); + for(kk=0;kk<5;kk++) + { + outb(0x04,qc->ap->ioaddr.ctl_addr); // set SRST + mdelay(5); // more than enough time + outb(0,qc->ap->ioaddr.ctl_addr); // clear + jj=0; + do + { + mdelay(1); + stus= inb(qc->ap->ioaddr.status_addr);// get drive status + jj++; + if(jj>=100) + { + break; + } + }while (stus!=0x50); + + if(stus==0x50) + { + softreset=0;//sucess + break; + } + + } + + } + if(stus==0x50) + { + softreset=0; + } + print_clear(("ULi drive status=0x%x\n",stus)); + + } + iftimeout=0; + //__restore_flags(flags_m5281); + + + //set the command + //trnslate error can be done accoring to ata_to_sense_error + + if(comreset==1) + { + ata_to_sense_error(qc, 0x20); + } + else + { + //print_clear(("to sys\n")); + ata_to_sense_error(qc, 0x1); + } + } + else + { + cmd->result = SAM_STAT_GOOD; + } + + } + else + { + if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) + ata_to_sense_error(qc, drv_stat); + else + cmd->result = SAM_STAT_GOOD; + } + } + //end add by clear cheung 2004/12/7 + else + { + + if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) + ata_to_sense_error(qc, drv_stat); + else + cmd->result = SAM_STAT_GOOD; + } + + qc->scsidone(cmd); + + return 0; +} + +/** + * ata_scsi_translate_uli - Translate then issue SCSI command to ATA device + * @ap: ATA port to which the command is addressed + * @dev: ATA device to which the command is addressed + * @cmd: SCSI command to execute + * @done: SCSI command completion function + * @xlat_func: Actor which translates @cmd to an ATA taskfile + * + * Our ->queuecommand() function has decided that the SCSI + * command issued can be directly translated into an ATA + * command, rather than handled internally. + * + * This function sets up an ata_queued_cmd structure for the + * SCSI command, and sends that ata_queued_cmd to the hardware. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +static void ata_scsi_translate_uli(struct ata_port *ap, struct ata_device *dev, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *), + ata_xlat_func_t xlat_func) +{ + struct ata_queued_cmd *qc; + u8 *scsicmd = cmd->cmnd; + + VPRINTK("ENTER\n"); + + qc = ata_scsi_qc_new_uli(ap, dev, cmd, done); + if (!qc) + return; + + /* data is present; dma-map it */ + if (cmd->sc_data_direction == SCSI_DATA_READ || + cmd->sc_data_direction == SCSI_DATA_WRITE) { + if (unlikely(cmd->request_bufflen < 1)) { + printk(KERN_WARNING "ata%u(%u): WARNING: zero len r/w req\n", + ap->id, dev->devno); + goto err_out; + } + + if (cmd->use_sg) + ata_sg_init(qc, cmd->request_buffer, cmd->use_sg); + else + ata_sg_init_one(qc, cmd->request_buffer, + cmd->request_bufflen); + + qc->pci_dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); + } + + qc->complete_fn = ata_scsi_qc_complete_uli; + + if (xlat_func(qc, scsicmd)) + goto err_out; + + /* select device, send command to hardware */ + if (ata_qc_issue(qc)) + goto err_out; + + VPRINTK("EXIT\n"); + return; + +err_out: + ata_bad_cdb(cmd, done); + DPRINTK("EXIT - badcmd\n"); +} +/////////////////////////////////////////////////////////////////////////////// + +/** + * ata_scsi_simulate_uli - simulate SCSI command on ATA device + * @ap: Port to which ATA device is attached. + * @dev: Target device for CDB. + * @cmd: SCSI command being sent to device. + * @done: SCSI command completion function. + * + * Interprets and directly executes a select list of SCSI commands + * that can be handled internally. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +static void ata_scsi_simulate_uli(struct ata_port *ap, struct ata_device *dev, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)) +{ + struct ata_scsi_args args; + u8 *scsicmd = cmd->cmnd; + + args.ap = ap; + args.dev = dev; + args.cmd = cmd; + args.done = done; + + switch(scsicmd[0]) { + /* no-op's, complete with success */ + case SYNCHRONIZE_CACHE: + case REZERO_UNIT: + case SEEK_6: + case SEEK_10: + case TEST_UNIT_READY: + case FORMAT_UNIT: /* FIXME: correct? */ + case SEND_DIAGNOSTIC: /* FIXME: correct? */ + ata_scsi_rbuf_fill(&args, ata_scsiop_noop); + break; + + case INQUIRY: + if (scsicmd[1] & 2) /* is CmdDt set? */ + ata_bad_cdb(cmd, done); + else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */ + ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std); + else if (scsicmd[2] == 0x00) + ata_scsi_rbuf_fill(&args, ata_scsiop_inq_00); + else if (scsicmd[2] == 0x80) + ata_scsi_rbuf_fill(&args, ata_scsiop_inq_80); + else if (scsicmd[2] == 0x83) + ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83); + else + ata_bad_cdb(cmd, done); + break; + + case MODE_SENSE: + case MODE_SENSE_10: + ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense); + break; + + case MODE_SELECT: /* unconditionally return */ + case MODE_SELECT_10: /* bad-field-in-cdb */ + ata_bad_cdb(cmd, done); + break; + + case READ_CAPACITY: + ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); + break; + + case SERVICE_ACTION_IN: + if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16) + ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); + else + ata_bad_cdb(cmd, done); + break; + + case REPORT_LUNS: + ata_scsi_rbuf_fill(&args, ata_scsiop_report_luns); + break; + + /* mandantory commands we haven't implemented yet */ + case REQUEST_SENSE: + + /* all other commands */ + default: + ata_bad_scsiop(cmd, done); + break; + } +} +////////////////////////////////////////////////////////////////////// + +/** + * ata_scsi_rbuf_get_uli - Map response buffer. + * @cmd: SCSI command containing buffer to be mapped. + * @buf_out: Pointer to mapped area. + * + * Maps buffer contained within SCSI command @cmd. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Length of response buffer. + */ + +static unsigned int ata_scsi_rbuf_get_uli(struct scsi_cmnd *cmd, u8 **buf_out) +{ + u8 *buf; + unsigned int buflen; + + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *) cmd->request_buffer; + buf = kmap_atomic(sg->page, KM_USER0) + sg->offset; + buflen = sg->length; + } else { + buf = cmd->request_buffer; + buflen = cmd->request_bufflen; + } + + memset(buf, 0, buflen); + *buf_out = buf; + return buflen; +} + +/** + * ata_scsi_rbuf_put_uli - Unmap response buffer. + * @cmd: SCSI command containing buffer to be unmapped. + * + * Unmaps response buffer contained within @cmd. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +static inline void ata_scsi_rbuf_put_uli(struct scsi_cmnd *cmd) +{ + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *) cmd->request_buffer; + kunmap_atomic(sg->page, KM_USER0); + } +} +static int atapi_qc_complete_uli(struct ata_queued_cmd *qc, u8 drv_stat) +{ + struct scsi_cmnd *cmd = qc->scsicmd; + + // add by clear cheung 2004/12/7 for ULi M5281 workaroud + if(qc->ap->host_set->pdev->vendor==0x10b9 && + qc->ap->host_set->pdev->device==0x5281) + { + + struct pci_dev *pdev_m5281=qc->ap->host_set->pdev; + u8 m5281_revision; + + cmd->result = SAM_STAT_GOOD; + + pci_read_config_byte(pdev_m5281, PCI_REVISION_ID, &m5281_revision); + //printk("m5281_revision=0x%x\n",m5281_revision); + //version 0xa0,0xa1,0xb1 need workaroud + if(m5281_revision==0xa0 ||m5281_revision==0xa1 ||m5281_revision==0xb1 ) + { + u16 error_m5281; + unsigned long scr_addr_m5281 = qc->ap->ioaddr.scr_addr; + + pci_read_config_word(pdev_m5281,scr_addr_m5281+6, &error_m5281); + + if ( unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ)) && iftimeout==0/*express not timeout*/) + cmd->result = SAM_STAT_CHECK_CONDITION; + //time out or drive status is ok + else if ((error_m5281 & 0x3f8)!=0 || iftimeout==1) //the src errore present + { + u32 scr4_m5281; + u8 reset_m5281, stus, scr0_m5281; + + u8 kk, jj; + u8 comreset=1, softreset=1;//success=0, fail=1 + + printk("ULi sata error:0x%x\n",error_m5281); + //reset + // dma have been closed + //__save_flags(flags_m5281); + //__cli(); + //com reset + for(kk=0;kk<20;kk++) + { + pci_read_config_byte(pdev_m5281,scr_addr_m5281+8,&reset_m5281); + reset_m5281 = reset_m5281 & 0xfe; + pci_write_config_byte(pdev_m5281,scr_addr_m5281+8,reset_m5281); + reset_m5281 = reset_m5281 | 0x01; + pci_write_config_byte(pdev_m5281,scr_addr_m5281+8,reset_m5281); + + //delay two ms + mdelay(2); + + //reallert and clear erro bit + reset_m5281 = reset_m5281 & 0xfe; + pci_write_config_byte(pdev_m5281,scr_addr_m5281+8,reset_m5281); + mdelay(2); + + pci_read_config_byte(pdev_m5281,scr_addr_m5281,&scr0_m5281); + pci_read_config_dword(pdev_m5281,scr_addr_m5281+4,&scr4_m5281); + if(scr0_m5281!=0x13 ||(scr4_m5281 & 0x50000)!= 0x00050000 ) + { + print_clear(("phy reset failed\n")); + print_clear(("scr0=0x%x\n", scr0_m5281)); + print_clear(("scr4=0x%x\n", scr4_m5281)); + continue; + } + else + { + //print_clear(("com reset success\n")); + //clear error bits + pci_write_config_dword(pdev_m5281,scr_addr_m5281+4,scr4_m5281); + + comreset=0;//success + break; + } + + } + + if(comreset==0) + { + //stus= inb(qc->ap->ioaddr.status_addr);// get drive status + jj=0; + do + { + mdelay(2); + stus= inb(qc->ap->ioaddr.status_addr);// get drive status + jj++; + if(jj>=150) + { + break; + } + }while (stus!=0x50); + + //print_clear(("jj=0x%x\n", jj)); + + if(stus!=0x50 /*&& iftimeout==1*/) + { + //print_clear(("soft reset\n")); + for(kk=0;kk<5;kk++) + { + outb(0x04,qc->ap->ioaddr.ctl_addr); // set SRST + mdelay(5); // more than enough time + outb(0,qc->ap->ioaddr.ctl_addr); // clear + jj=0; + do + { + mdelay(1); + stus= inb(qc->ap->ioaddr.status_addr);// get drive status + jj++; + if(jj>=100) + { + break; + } + }while (stus!=0x50); + + if(stus==0x50) + { + softreset=0;//sucess + break; + } + + } + + } + if(stus==0x50) + { + softreset=0; + } + print_clear(("ULi drive status=0x%x\n",stus)); + + } + iftimeout=0; + //__restore_flags(flags_m5281); + + + //set the command + //trnslate error can be done accoring to ata_to_sense_error + cmd->result = SAM_STAT_CHECK_CONDITION; + /*if(comreset==1) + { + ata_to_sense_error(qc, 0x20); + } + else + { + //print_clear(("to sys\n")); + ata_to_sense_error(qc, 0x1); + }*/ + } + else + { + u8 *scsicmd = cmd->cmnd; + + if (scsicmd[0] == INQUIRY) { + u8 *buf = NULL; + unsigned int buflen; + + buflen = ata_scsi_rbuf_get_uli(cmd, &buf); + buf[2] = 0x5; + buf[3] = (buf[3] & 0xf0) | 2; + ata_scsi_rbuf_put_uli(cmd); + } + cmd->result = SAM_STAT_GOOD; + } + + } + else + { + if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) + cmd->result = SAM_STAT_CHECK_CONDITION; + else { + u8 *scsicmd = cmd->cmnd; + + if (scsicmd[0] == INQUIRY) { + u8 *buf = NULL; + unsigned int buflen; + + buflen = ata_scsi_rbuf_get_uli(cmd, &buf); + buf[2] = 0x5; + buf[3] = (buf[3] & 0xf0) | 2; + ata_scsi_rbuf_put_uli(cmd); + } + cmd->result = SAM_STAT_GOOD; + } + } + } + //end add by clear cheung 2004/12/7 + + else + { + + if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) + cmd->result = SAM_STAT_CHECK_CONDITION; + else { + u8 *scsicmd = cmd->cmnd; + + if (scsicmd[0] == INQUIRY) { + u8 *buf = NULL; + unsigned int buflen; + + buflen = ata_scsi_rbuf_get_uli(cmd, &buf); + buf[2] = 0x5; + buf[3] = (buf[3] & 0xf0) | 2; + ata_scsi_rbuf_put_uli(cmd); + } + cmd->result = SAM_STAT_GOOD; + } + } + + qc->scsidone(cmd); + + return 0; +} + +/** + * atapi_xlat_uli - Initialize PACKET taskfile + * @qc: command structure to be initialized + * @scsicmd: SCSI CDB associated with this PACKET command + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Zero on success, non-zero on failure. + */ + +static unsigned int atapi_xlat_uli(struct ata_queued_cmd *qc, u8 *scsicmd) +{ + struct scsi_cmnd *cmd = qc->scsicmd; + struct ata_device *dev = qc->dev; + int using_pio = (dev->flags & ATA_DFLAG_PIO); + int nodata = (cmd->sc_data_direction == SCSI_DATA_NONE); + + memcpy(&qc->cdb, scsicmd, qc->ap->cdb_len); + + qc->complete_fn = atapi_qc_complete_uli; + + qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + if (cmd->sc_data_direction == SCSI_DATA_WRITE) { + qc->tf.flags |= ATA_TFLAG_WRITE; + DPRINTK("direction: write\n"); + } + + qc->tf.command = ATA_CMD_PACKET; + + /* no data, or PIO data xfer */ + if (using_pio || nodata) { + if (nodata) + qc->tf.protocol = ATA_PROT_ATAPI_NODATA; + else + qc->tf.protocol = ATA_PROT_ATAPI; + qc->tf.lbam = (8 * 1024) & 0xff; + qc->tf.lbah = (8 * 1024) >> 8; + } + + /* DMA data xfer */ + else { + qc->tf.protocol = ATA_PROT_ATAPI_DMA; + qc->tf.feature |= ATAPI_PKT_DMA; + +#ifdef ATAPI_ENABLE_DMADIR + /* some SATA bridges need us to indicate data xfer direction */ + if (cmd->sc_data_direction != SCSI_DATA_WRITE) + qc->tf.feature |= ATAPI_DMADIR; +#endif + } + + return 0; +} + +///////////////////////////////////////////////////////////// +/** + * ata_scsi_queuecmd_uli - Issue SCSI cdb to libata-managed device + * @cmd: SCSI command to be sent + * @done: Completion function, called when command is complete + * + * In some cases, this function translates SCSI commands into + * ATA taskfiles, and queues the taskfiles to be sent to + * hardware. In other cases, this function simulates a + * SCSI device by evaluating and responding to certain + * SCSI commands. This creates the overall effect of + * ATA and ATAPI devices appearing as SCSI devices. + * + * LOCKING: + * Releases scsi-layer-held lock, and obtains host_set lock. + * + * RETURNS: + * Zero. + */ + +static int ata_scsi_queuecmd_uli(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +{ + struct ata_port *ap; + struct ata_device *dev; + struct scsi_device *scsidev = cmd->device; + + ap = (struct ata_port *) &scsidev->host->hostdata[0]; + + //ata_scsi_dump_cdb(ap, cmd); + + dev = ata_scsi_find_dev_uli(ap, scsidev); + if (unlikely(!dev)) { + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + goto out_unlock; + } + + if (dev->class == ATA_DEV_ATA) { + ata_xlat_func_t xlat_func = ata_get_xlat_func_uli(dev, + cmd->cmnd[0]); + + if (xlat_func) + ata_scsi_translate_uli(ap, dev, cmd, done, xlat_func); + else + ata_scsi_simulate_uli(ap, dev, cmd, done); + } else + ata_scsi_translate_uli(ap, dev, cmd, done, atapi_xlat_uli); + +out_unlock: + return 0; +} +////////////////////////////////////////////////////////////////////// + +/** + * ata_qc_timeout_uli - Handle timeout of queued command + * @qc: Command that timed out + * + * Some part of the kernel (currently, only the SCSI layer) + * has noticed that the active command on port @ap has not + * completed after a specified length of time. Handle this + * condition by disabling DMA (if necessary) and completing + * transactions, with error if necessary. + * + * This also handles the case of the "lost interrupt", where + * for some reason (possibly hardware bug, possibly driver bug) + * an interrupt was not delivered to the driver, even though the + * transaction completed successfully. + * + * LOCKING: + */ + +static void ata_qc_timeout_uli(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + u8 host_stat = 0, drv_stat; + + DPRINTK("ENTER\n"); + + /* 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 + * any kernel, so the default scsi_done() assumes it is + * not being called from the SCSI EH. + */ + qc->scsidone = scsi_finish_command; + + switch (qc->tf.protocol) { + + case ATA_PROT_DMA: + case ATA_PROT_ATAPI_DMA: + host_stat = ata_bmdma_status(ap); + + /* before we do anything else, clear DMA-Start bit */ + ata_bmdma_stop(ap); + + /* fall through */ + + default: + ata_altstatus(ap); + drv_stat = ata_chk_status(ap); + + /* ack bmdma irq events */ + ata_bmdma_ack_irq(ap); + + printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x host_stat 0x%x\n", + ap->id, qc->tf.command, drv_stat, host_stat); + + /* complete taskfile transaction */ + ata_qc_complete(qc, drv_stat); + break; + } + + DPRINTK("EXIT\n"); +} + +/** + * ata_eng_timeout_uli - Handle timeout of queued command + * @ap: Port on which timed-out command is active + * + * Some part of the kernel (currently, only the SCSI layer) + * has noticed that the active command on port @ap has not + * completed after a specified length of time. Handle this + * condition by disabling DMA (if necessary) and completing + * transactions, with error if necessary. + * + * This also handles the case of the "lost interrupt", where + * for some reason (possibly hardware bug, possibly driver bug) + * an interrupt was not delivered to the driver, even though the + * transaction completed successfully. + * + * LOCKING: + * Inherited from SCSI layer (none, can sleep) + */ + +static void ata_eng_timeout_uli(struct ata_port *ap) +{ + struct ata_queued_cmd *qc; + + DPRINTK("ENTER\n"); + + //add by clear cheung 2004/12/7 + iftimeout=1; + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (!qc) { + printk(KERN_ERR "ata%u: BUG: timeout without command\n", + ap->id); + goto out; + } + + ata_qc_timeout_uli(qc); + +out: + DPRINTK("EXIT\n"); +} + +//////////////////////////////////////////////////////////////////////////////////// +module_init(uli_init); +module_exit(uli_exit);