📄 libata-scsi.c
字号:
* termination. * * LOCKING: * spin_lock_irqsave(host lock) * * RETURNS: * 0 on success, SCSI_ML_QUEUE_DEVICE_BUSY if the command * needs to be deferred. */static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), ata_xlat_func_t xlat_func){ struct ata_port *ap = dev->link->ap; struct ata_queued_cmd *qc; int rc; VPRINTK("ENTER\n"); qc = ata_scsi_qc_new(dev, cmd, done); if (!qc) goto err_mem; /* data is present; dma-map it */ if (cmd->sc_data_direction == DMA_FROM_DEVICE || cmd->sc_data_direction == DMA_TO_DEVICE) { if (unlikely(scsi_bufflen(cmd) < 1)) { ata_dev_printk(dev, KERN_WARNING, "WARNING: zero len r/w req\n"); goto err_did; } ata_sg_init(qc, scsi_sglist(cmd), scsi_sg_count(cmd)); qc->dma_dir = cmd->sc_data_direction; } qc->complete_fn = ata_scsi_qc_complete; if (xlat_func(qc)) goto early_finish; if (ap->ops->qc_defer) { if ((rc = ap->ops->qc_defer(qc))) goto defer; } /* select device, send command to hardware */ ata_qc_issue(qc); VPRINTK("EXIT\n"); return 0;early_finish: ata_qc_free(qc); qc->scsidone(cmd); DPRINTK("EXIT - early finish (good or error)\n"); return 0;err_did: ata_qc_free(qc); cmd->result = (DID_ERROR << 16); qc->scsidone(cmd);err_mem: DPRINTK("EXIT - internal\n"); return 0;defer: ata_qc_free(qc); DPRINTK("EXIT - defer\n"); if (rc == ATA_DEFER_LINK) return SCSI_MLQUEUE_DEVICE_BUSY; else return SCSI_MLQUEUE_HOST_BUSY;}/** * 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 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; struct scatterlist *sg = scsi_sglist(cmd); if (sg) { buf = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset; buflen = sg->length; } else { buf = NULL; buflen = 0; } *buf_out = buf; return buflen;}/** * ata_scsi_rbuf_put - Unmap response buffer. * @cmd: SCSI command containing buffer to be unmapped. * @buf: buffer to unmap * * Unmaps response buffer contained within @cmd. * * LOCKING: * spin_lock_irqsave(host lock) */static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd, u8 *buf){ struct scatterlist *sg = scsi_sglist(cmd); if (sg) kunmap_atomic(buf - sg->offset, KM_IRQ0);}/** * ata_scsi_rbuf_fill - wrapper for SCSI command simulators * @args: device IDENTIFY data / 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 (0), or not (in which case cmd->result * and sense buffer are assumed to be set). * * LOCKING: * spin_lock_irqsave(host 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); memset(rbuf, 0, buflen); rc = actor(args, rbuf, buflen); ata_scsi_rbuf_put(cmd, rbuf); if (rc == 0) cmd->result = SAM_STAT_GOOD; args->done(cmd);}/** * ATA_SCSI_RBUF_SET - helper to set values in SCSI response buffer * @idx: byte index into SCSI response buffer * @val: value to set * * To be used by SCSI command simulator functions. This macros * expects two local variables, u8 *rbuf and unsigned int buflen, * are in scope. * * LOCKING: * None. */#define ATA_SCSI_RBUF_SET(idx, val) do { \ if ((idx) < buflen) rbuf[(idx)] = (u8)(val); \ } while (0)/** * ata_scsiop_inq_std - Simulate INQUIRY command * @args: device IDENTIFY data / 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-VPD INQUIRY command output. * * LOCKING: * spin_lock_irqsave(host lock) */unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ 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(args->id)) hdr[1] |= (1 << 7); VPRINTK("ENTER\n"); memcpy(rbuf, hdr, sizeof(hdr)); if (buflen > 35) { memcpy(&rbuf[8], "ATA ", 8); ata_id_string(args->id, &rbuf[16], ATA_ID_PROD, 16); ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV, 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 VPD page 0, list of pages * @args: device IDENTIFY data / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Returns list of inquiry VPD pages available. * * LOCKING: * spin_lock_irqsave(host 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 VPD pages */ if (buflen > 6) memcpy(rbuf + 4, pages, sizeof(pages)); return 0;}/** * ata_scsiop_inq_80 - Simulate INQUIRY VPD page 80, device serial number * @args: device IDENTIFY data / 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 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_ID_SERNO_LEN, /* page len */ }; memcpy(rbuf, hdr, sizeof(hdr)); if (buflen > (ATA_ID_SERNO_LEN + 4 - 1)) ata_id_string(args->id, (unsigned char *) &rbuf[4], ATA_ID_SERNO, ATA_ID_SERNO_LEN); return 0;}/** * ata_scsiop_inq_83 - Simulate INQUIRY VPD page 83, device identity * @args: device IDENTIFY data / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Yields two logical unit device identification designators: * - vendor specific ASCII containing the ATA serial number * - SAT defined "t10 vendor id based" containing ASCII vendor * name ("ATA "), model and serial numbers. * * LOCKING: * spin_lock_irqsave(host lock) */unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ int num; const int sat_model_serial_desc_len = 68; rbuf[1] = 0x83; /* this page code */ num = 4; if (buflen > (ATA_ID_SERNO_LEN + num + 3)) { /* piv=0, assoc=lu, code_set=ACSII, designator=vendor */ rbuf[num + 0] = 2; rbuf[num + 3] = ATA_ID_SERNO_LEN; num += 4; ata_id_string(args->id, (unsigned char *) rbuf + num, ATA_ID_SERNO, ATA_ID_SERNO_LEN); num += ATA_ID_SERNO_LEN; } if (buflen > (sat_model_serial_desc_len + num + 3)) { /* SAT defined lu model and serial numbers descriptor */ /* piv=0, assoc=lu, code_set=ACSII, designator=t10 vendor id */ rbuf[num + 0] = 2; rbuf[num + 1] = 1; rbuf[num + 3] = sat_model_serial_desc_len; num += 4; memcpy(rbuf + num, "ATA ", 8); num += 8; ata_id_string(args->id, (unsigned char *) rbuf + num, ATA_ID_PROD, ATA_ID_PROD_LEN); num += ATA_ID_PROD_LEN; ata_id_string(args->id, (unsigned char *) rbuf + num, ATA_ID_SERNO, ATA_ID_SERNO_LEN); num += ATA_ID_SERNO_LEN; } rbuf[3] = num - 4; /* page len (assume less than 256 bytes) */ return 0;}/** * ata_scsiop_inq_89 - Simulate INQUIRY VPD page 89, ATA info * @args: device IDENTIFY data / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Yields SAT-specified ATA VPD page. * * LOCKING: * spin_lock_irqsave(host lock) */unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ u8 pbuf[60]; struct ata_taskfile tf; unsigned int i; if (!buflen) return 0; memset(&pbuf, 0, sizeof(pbuf)); memset(&tf, 0, sizeof(tf)); pbuf[1] = 0x89; /* our page code */ pbuf[2] = (0x238 >> 8); /* page size fixed at 238h */ pbuf[3] = (0x238 & 0xff); memcpy(&pbuf[8], "linux ", 8); memcpy(&pbuf[16], "libata ", 16); memcpy(&pbuf[32], DRV_VERSION, 4); ata_id_string(args->id, &pbuf[32], ATA_ID_FW_REV, 4); /* we don't store the ATA device signature, so we fake it */ tf.command = ATA_DRDY; /* really, this is Status reg */ tf.lbal = 0x1; tf.nsect = 0x1; ata_tf_to_fis(&tf, 0, 1, &pbuf[36]); /* TODO: PMP? */ pbuf[36] = 0x34; /* force D2H Reg FIS (34h) */ pbuf[56] = ATA_CMD_ID_ATA; i = min(buflen, 60U); memcpy(rbuf, &pbuf[0], i); buflen -= i; if (!buflen) return 0; memcpy(&rbuf[60], &args->id[0], min(buflen, 512U)); return 0;}/** * ata_scsiop_noop - Command handler that simply returns success. * @args: device IDENTIFY data / 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 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 * @id: device IDENTIFY data * @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(u16 *id, u8 **ptr_io, const u8 *last){ u8 page[CACHE_MPAGE_LEN]; memcpy(page, def_cache_mpage, sizeof(page)); if (ata_id_wcache_enabled(id)) page[2] |= (1 << 2); /* write cache enable */ if (!ata_id_rahead_enabled(id)) 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){ ata_msense_push(ptr_io, last, def_control_mpage, sizeof(def_control_mpage)); return sizeof(def_control_mpage);}/** * ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page * @dev: Device associated with this MODE SENSE command
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -