diff options
Diffstat (limited to 'drivers/scsi/qla4xxx/ql4_bsg.c')
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_bsg.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_bsg.c b/drivers/scsi/qla4xxx/ql4_bsg.c new file mode 100644 index 000000000000..daa2b0f8e309 --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_bsg.c @@ -0,0 +1,209 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2011 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#include "ql4_def.h" +#include "ql4_glbl.h" +#include "ql4_bsg.h" + +static int +qla4xxx_read_flash(struct bsg_job *bsg_job) +{ + struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); + struct scsi_qla_host *ha = to_qla_host(host); + struct iscsi_bsg_reply *bsg_reply = bsg_job->reply; + struct iscsi_bsg_request *bsg_req = bsg_job->request; + uint32_t sg_cnt; + uint32_t offset = 0; + uint32_t length = 0; + dma_addr_t flash_dma; + uint8_t *flash = NULL; + int rval = 0; + + bsg_reply->reply_payload_rcv_len = 0; + + if (unlikely(pci_channel_offline(ha->pdev))) + return -EINVAL; + + if (ha->flash_state != QLFLASH_WAITING) + return -EBUSY; + + /* TODO: Add check for adapter online, reset active?? */ + sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + + if (!sg_cnt) + return -ENOMEM; + + if (sg_cnt != bsg_job->reply_payload.sg_cnt) { + ql4_printk(KERN_ERR, ha, "dma mapping resulted in different" + " sg counts, sg_cnt: %x dma_sg_cnt: %x\n", + bsg_job->reply_payload.sg_cnt, sg_cnt); + rval = -EAGAIN; + goto unmap_sg; + } + + offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1]; + length = bsg_job->reply_payload.payload_len; + + flash = dma_alloc_coherent(&ha->pdev->dev, length, &flash_dma, + GFP_KERNEL); + if (!flash) { + ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for flash " + "data\n", __func__); + rval = -ENOMEM; + goto unmap_sg; + } + + ha->flash_state = QLFLASH_READING; + if (qla4xxx_get_flash(ha, flash_dma, offset, length)) + bsg_reply->result = (DID_ERROR << 16); + else { + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, + flash, length); + + bsg_reply->result = DID_OK; + bsg_reply->reply_payload_rcv_len = length; + } + + if (flash) + dma_free_coherent(&ha->pdev->dev, length, flash, flash_dma); + + ha->flash_state = QLFLASH_WAITING; +unmap_sg: + dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + if (!rval) + bsg_job_done(bsg_job, bsg_reply->result, + bsg_reply->reply_payload_rcv_len); + return rval; +} + +static int +qla4xxx_update_flash(struct bsg_job *bsg_job) +{ + struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); + struct scsi_qla_host *ha = to_qla_host(host); + struct iscsi_bsg_reply *bsg_reply = bsg_job->reply; + struct iscsi_bsg_request *bsg_req = bsg_job->request; + uint32_t sg_cnt; + uint32_t length = 0; + uint32_t offset = 0; + uint32_t options = 0; + dma_addr_t flash_dma; + uint8_t *flash = NULL; + int rval = 0; + + bsg_reply->reply_payload_rcv_len = 0; + + if (unlikely(pci_channel_offline(ha->pdev))) + return -EINVAL; + + if (ha->flash_state != QLFLASH_WAITING) + return -EBUSY; + + sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + + if (!sg_cnt) + return -ENOMEM; + + if (sg_cnt != bsg_job->request_payload.sg_cnt) { + ql4_printk(KERN_ERR, ha, "dma mapping resulted in different " + "sg counts request_sg_cnt: %x dma_request_sg_cnt: " + "%x\n", bsg_job->request_payload.sg_cnt, sg_cnt); + rval = -EAGAIN; + goto unmap_sg; + } + + length = bsg_job->request_payload.payload_len; + offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1]; + options = bsg_req->rqst_data.h_vendor.vendor_cmd[2]; + + flash = dma_alloc_coherent(&ha->pdev->dev, length, &flash_dma, + GFP_KERNEL); + if (!flash) { + ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for flash " + "data\n", __func__); + rval = -ENOMEM; + goto unmap_sg; + } + + ha->flash_state = QLFLASH_WRITING; + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, flash, length); + + if (qla4xxx_set_flash(ha, flash_dma, offset, length, options)) + bsg_reply->result = (DID_ERROR << 16); + else { + bsg_reply->result = DID_OK; + bsg_reply->reply_payload_rcv_len = length; + } + + if (flash) + dma_free_coherent(&ha->pdev->dev, length, flash, flash_dma); + ha->flash_state = QLFLASH_WAITING; +unmap_sg: + dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_TO_DEVICE); + + if (!rval) + bsg_job_done(bsg_job, bsg_reply->result, + bsg_reply->reply_payload_rcv_len); + return rval; +} + +/** + * qla4xxx_process_vendor_specific - handle vendor specific bsg request + * @job: iscsi_bsg_job to handle + **/ +int qla4xxx_process_vendor_specific(struct bsg_job *bsg_job) +{ + struct iscsi_bsg_reply *bsg_reply = bsg_job->reply; + struct iscsi_bsg_request *bsg_req = bsg_job->request; + struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); + struct scsi_qla_host *ha = to_qla_host(host); + + switch (bsg_req->rqst_data.h_vendor.vendor_cmd[0]) { + case QLISCSI_VND_READ_FLASH: + return qla4xxx_read_flash(bsg_job); + + case QLISCSI_VND_UPDATE_FLASH: + return qla4xxx_update_flash(bsg_job); + + default: + ql4_printk(KERN_ERR, ha, "%s: invalid BSG vendor command: " + "0x%x\n", __func__, bsg_req->msgcode); + bsg_reply->result = (DID_ERROR << 16); + bsg_reply->reply_payload_rcv_len = 0; + bsg_job_done(bsg_job, bsg_reply->result, + bsg_reply->reply_payload_rcv_len); + return -ENOSYS; + } +} + +/** + * qla4xxx_bsg_request - handle bsg request from ISCSI transport + * @job: iscsi_bsg_job to handle + */ +int qla4xxx_bsg_request(struct bsg_job *bsg_job) +{ + struct iscsi_bsg_request *bsg_req = bsg_job->request; + struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); + struct scsi_qla_host *ha = to_qla_host(host); + + switch (bsg_req->msgcode) { + case ISCSI_BSG_HST_VENDOR: + return qla4xxx_process_vendor_specific(bsg_job); + + default: + ql4_printk(KERN_ERR, ha, "%s: invalid BSG command: 0x%x\n", + __func__, bsg_req->msgcode); + } + + return -ENOSYS; +} |