libata-scsi.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,571 行 · 第 1/3 页
C
1,571 行
* RETURNS: * Zero on success, non-zero on error. */static unsigned int ata_scsi_rw_xlat(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;}static int ata_scsi_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))) ata_to_sense_error(qc, drv_stat); else cmd->result = SAM_STAT_GOOD; qc->scsidone(cmd); return 0;}/** * ata_scsi_translate - 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(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(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; 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_rbuf_get - 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(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 - 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(struct scsi_cmnd *cmd){ if (cmd->use_sg) { struct scatterlist *sg; sg = (struct scatterlist *) cmd->request_buffer; kunmap_atomic(sg->page, KM_USER0); }}/** * ata_scsi_rbuf_fill - wrapper for SCSI command simulators * @args: Port / device / SCSI command of interest. * @actor: Callback hook for desired SCSI command simulator * * Takes care of the hard work of simulating a SCSI command... * Mapping the response buffer, calling the command's handler, * and handling the handler's return value. This return value * indicates whether the handler wishes the SCSI command to be * completed successfully, or not. * * LOCKING: * spin_lock_irqsave(host_set lock) */void ata_scsi_rbuf_fill(struct ata_scsi_args *args, unsigned int (*actor) (struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen)){ u8 *rbuf; unsigned int buflen, rc; struct scsi_cmnd *cmd = args->cmd; buflen = ata_scsi_rbuf_get(cmd, &rbuf); rc = actor(args, rbuf, buflen); ata_scsi_rbuf_put(cmd); if (rc) ata_bad_cdb(cmd, args->done); else { cmd->result = SAM_STAT_GOOD; args->done(cmd); }}/** * ata_scsiop_inq_std - Simulate INQUIRY command * @args: Port / device / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Returns standard device identification data associated * with non-EVPD INQUIRY command output. * * LOCKING: * spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ struct ata_device *dev = args->dev; u8 hdr[] = { TYPE_DISK, 0, 0x5, /* claim SPC-3 version compatibility */ 2, 95 - 4 }; /* set scsi removeable (RMB) bit per ata bit */ if (ata_id_removeable(dev)) hdr[1] |= (1 << 7); VPRINTK("ENTER\n"); memcpy(rbuf, hdr, sizeof(hdr)); if (buflen > 35) { memcpy(&rbuf[8], "ATA ", 8); ata_dev_id_string(dev, &rbuf[16], ATA_ID_PROD_OFS, 16); ata_dev_id_string(dev, &rbuf[32], ATA_ID_FW_REV_OFS, 4); if (rbuf[32] == 0 || rbuf[32] == ' ') memcpy(&rbuf[32], "n/a ", 4); } if (buflen > 63) { const u8 versions[] = { 0x60, /* SAM-3 (no version claimed) */ 0x03, 0x20, /* SBC-2 (no version claimed) */ 0x02, 0x60 /* SPC-3 (no version claimed) */ }; memcpy(rbuf + 59, versions, sizeof(versions)); } return 0;}/** * ata_scsiop_inq_00 - Simulate INQUIRY EVPD page 0, list of pages * @args: Port / device / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Returns list of inquiry EVPD pages available. * * LOCKING: * spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ const u8 pages[] = { 0x00, /* page 0x00, this page */ 0x80, /* page 0x80, unit serial no page */ 0x83 /* page 0x83, device ident page */ }; rbuf[3] = sizeof(pages); /* number of supported EVPD pages */ if (buflen > 6) memcpy(rbuf + 4, pages, sizeof(pages)); return 0;}/** * ata_scsiop_inq_80 - Simulate INQUIRY EVPD page 80, device serial number * @args: Port / device / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Returns ATA device serial number. * * LOCKING: * spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ const u8 hdr[] = { 0, 0x80, /* this page code */ 0, ATA_SERNO_LEN, /* page len */ }; memcpy(rbuf, hdr, sizeof(hdr)); if (buflen > (ATA_SERNO_LEN + 4)) ata_dev_id_string(args->dev, (unsigned char *) &rbuf[4], ATA_ID_SERNO_OFS, ATA_SERNO_LEN); return 0;}static const char *inq_83_str = "Linux ATA-SCSI simulator";/** * ata_scsiop_inq_83 - Simulate INQUIRY EVPD page 83, device identity * @args: Port / device / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Returns device identification. Currently hardcoded to * return "Linux ATA-SCSI simulator". * * LOCKING: * spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ rbuf[1] = 0x83; /* this page code */ rbuf[3] = 4 + strlen(inq_83_str); /* page len */ /* our one and only identification descriptor (vendor-specific) */ if (buflen > (strlen(inq_83_str) + 4 + 4)) { rbuf[4 + 0] = 2; /* code set: ASCII */ rbuf[4 + 3] = strlen(inq_83_str); memcpy(rbuf + 4 + 4, inq_83_str, strlen(inq_83_str)); } return 0;}/** * ata_scsiop_noop - * @args: Port / device / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * No operation. Simply returns success to caller, to indicate * that the caller should successfully complete this SCSI command. * * LOCKING: * spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ VPRINTK("ENTER\n"); return 0;}/** * ata_msense_push - Push data onto MODE SENSE data output buffer * @ptr_io: (input/output) Location to store more output data * @last: End of output data buffer * @buf: Pointer to BLOB being added to output buffer * @buflen: Length of BLOB * * Store MODE SENSE data on an output buffer. * * LOCKING: * None. */static void ata_msense_push(u8 **ptr_io, const u8 *last, const u8 *buf, unsigned int buflen){ u8 *ptr = *ptr_io; if ((ptr + buflen - 1) > last) return; memcpy(ptr, buf, buflen); ptr += buflen; *ptr_io = ptr;}/** * ata_msense_caching - Simulate MODE SENSE caching info page * @dev: Device associated with this MODE SENSE command * @ptr_io: (input/output) Location to store more output data * @last: End of output data buffer * * Generate a caching info page, which conditionally indicates * write caching to the SCSI layer, depending on device * capabilities. * * LOCKING: * None. */static unsigned int ata_msense_caching(struct ata_device *dev, u8 **ptr_io, const u8 *last){ u8 page[] = { 0x8, /* page code */ 0x12, /* page length */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 zeroes */ 0, 0, 0, 0, 0, 0, 0, 0 /* 8 zeroes */ }; if (ata_id_wcache_enabled(dev)) page[2] |= (1 << 2); /* write cache enable */ if (!ata_id_rahead_enabled(dev)) page[12] |= (1 << 5); /* disable read ahead */ ata_msense_push(ptr_io, last, page, sizeof(page)); return sizeof(page);}/** * ata_msense_ctl_mode - Simulate MODE SENSE control mode page * @dev: Device associated with this MODE SENSE command * @ptr_io: (input/output) Location to store more output data * @last: End of output data buffer * * Generate a generic MODE SENSE control mode page. * * LOCKING: * None. */static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last){ const u8 page[] = {0xa, 0xa, 2, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 30}; ata_msense_push(ptr_io, last, page, sizeof(page)); return sizeof(page);}/** * ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page * @dev: Device associated with this MODE SENSE command * @ptr_io: (input/output) Location to store more output data * @last: End of output data buffer *
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?