diff options
author | Vikas Chaudhary <vikas.chaudhary@qlogic.com> | 2011-07-25 14:48:51 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2011-08-27 10:36:22 -0400 |
commit | a355943ca847ca3a264d468e408217562234d019 (patch) | |
tree | 28aa660563b27a86427112137de818913d96a724 /drivers/scsi/qla4xxx/ql4_bsg.c | |
parent | 90eeb01a038e5fec0efdea4df008f3c18f67b82c (diff) |
[SCSI] qla4xxx: add bsg support
This patch adds bsg support to qla4xxx.
Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com>
Signed-off-by: Harish Zunjarrao <harish.zunjarrao@qlogic.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
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 @@ | |||
1 | /* | ||
2 | * QLogic iSCSI HBA Driver | ||
3 | * Copyright (c) 2011 QLogic Corporation | ||
4 | * | ||
5 | * See LICENSE.qla4xxx for copyright and licensing details. | ||
6 | */ | ||
7 | |||
8 | #include "ql4_def.h" | ||
9 | #include "ql4_glbl.h" | ||
10 | #include "ql4_bsg.h" | ||
11 | |||
12 | static int | ||
13 | qla4xxx_read_flash(struct bsg_job *bsg_job) | ||
14 | { | ||
15 | struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); | ||
16 | struct scsi_qla_host *ha = to_qla_host(host); | ||
17 | struct iscsi_bsg_reply *bsg_reply = bsg_job->reply; | ||
18 | struct iscsi_bsg_request *bsg_req = bsg_job->request; | ||
19 | uint32_t sg_cnt; | ||
20 | uint32_t offset = 0; | ||
21 | uint32_t length = 0; | ||
22 | dma_addr_t flash_dma; | ||
23 | uint8_t *flash = NULL; | ||
24 | int rval = 0; | ||
25 | |||
26 | bsg_reply->reply_payload_rcv_len = 0; | ||
27 | |||
28 | if (unlikely(pci_channel_offline(ha->pdev))) | ||
29 | return -EINVAL; | ||
30 | |||
31 | if (ha->flash_state != QLFLASH_WAITING) | ||
32 | return -EBUSY; | ||
33 | |||
34 | /* TODO: Add check for adapter online, reset active?? */ | ||
35 | sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, | ||
36 | bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
37 | |||
38 | if (!sg_cnt) | ||
39 | return -ENOMEM; | ||
40 | |||
41 | if (sg_cnt != bsg_job->reply_payload.sg_cnt) { | ||
42 | ql4_printk(KERN_ERR, ha, "dma mapping resulted in different" | ||
43 | " sg counts, sg_cnt: %x dma_sg_cnt: %x\n", | ||
44 | bsg_job->reply_payload.sg_cnt, sg_cnt); | ||
45 | rval = -EAGAIN; | ||
46 | goto unmap_sg; | ||
47 | } | ||
48 | |||
49 | offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1]; | ||
50 | length = bsg_job->reply_payload.payload_len; | ||
51 | |||
52 | flash = dma_alloc_coherent(&ha->pdev->dev, length, &flash_dma, | ||
53 | GFP_KERNEL); | ||
54 | if (!flash) { | ||
55 | ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for flash " | ||
56 | "data\n", __func__); | ||
57 | rval = -ENOMEM; | ||
58 | goto unmap_sg; | ||
59 | } | ||
60 | |||
61 | ha->flash_state = QLFLASH_READING; | ||
62 | if (qla4xxx_get_flash(ha, flash_dma, offset, length)) | ||
63 | bsg_reply->result = (DID_ERROR << 16); | ||
64 | else { | ||
65 | sg_copy_from_buffer(bsg_job->reply_payload.sg_list, | ||
66 | bsg_job->reply_payload.sg_cnt, | ||
67 | flash, length); | ||
68 | |||
69 | bsg_reply->result = DID_OK; | ||
70 | bsg_reply->reply_payload_rcv_len = length; | ||
71 | } | ||
72 | |||
73 | if (flash) | ||
74 | dma_free_coherent(&ha->pdev->dev, length, flash, flash_dma); | ||
75 | |||
76 | ha->flash_state = QLFLASH_WAITING; | ||
77 | unmap_sg: | ||
78 | dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, | ||
79 | bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
80 | if (!rval) | ||
81 | bsg_job_done(bsg_job, bsg_reply->result, | ||
82 | bsg_reply->reply_payload_rcv_len); | ||
83 | return rval; | ||
84 | } | ||
85 | |||
86 | static int | ||
87 | qla4xxx_update_flash(struct bsg_job *bsg_job) | ||
88 | { | ||
89 | struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); | ||
90 | struct scsi_qla_host *ha = to_qla_host(host); | ||
91 | struct iscsi_bsg_reply *bsg_reply = bsg_job->reply; | ||
92 | struct iscsi_bsg_request *bsg_req = bsg_job->request; | ||
93 | uint32_t sg_cnt; | ||
94 | uint32_t length = 0; | ||
95 | uint32_t offset = 0; | ||
96 | uint32_t options = 0; | ||
97 | dma_addr_t flash_dma; | ||
98 | uint8_t *flash = NULL; | ||
99 | int rval = 0; | ||
100 | |||
101 | bsg_reply->reply_payload_rcv_len = 0; | ||
102 | |||
103 | if (unlikely(pci_channel_offline(ha->pdev))) | ||
104 | return -EINVAL; | ||
105 | |||
106 | if (ha->flash_state != QLFLASH_WAITING) | ||
107 | return -EBUSY; | ||
108 | |||
109 | sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, | ||
110 | bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
111 | |||
112 | if (!sg_cnt) | ||
113 | return -ENOMEM; | ||
114 | |||
115 | if (sg_cnt != bsg_job->request_payload.sg_cnt) { | ||
116 | ql4_printk(KERN_ERR, ha, "dma mapping resulted in different " | ||
117 | "sg counts request_sg_cnt: %x dma_request_sg_cnt: " | ||
118 | "%x\n", bsg_job->request_payload.sg_cnt, sg_cnt); | ||
119 | rval = -EAGAIN; | ||
120 | goto unmap_sg; | ||
121 | } | ||
122 | |||
123 | length = bsg_job->request_payload.payload_len; | ||
124 | offset = bsg_req->rqst_data.h_vendor.vendor_cmd[1]; | ||
125 | options = bsg_req->rqst_data.h_vendor.vendor_cmd[2]; | ||
126 | |||
127 | flash = dma_alloc_coherent(&ha->pdev->dev, length, &flash_dma, | ||
128 | GFP_KERNEL); | ||
129 | if (!flash) { | ||
130 | ql4_printk(KERN_ERR, ha, "%s: dma alloc failed for flash " | ||
131 | "data\n", __func__); | ||
132 | rval = -ENOMEM; | ||
133 | goto unmap_sg; | ||
134 | } | ||
135 | |||
136 | ha->flash_state = QLFLASH_WRITING; | ||
137 | sg_copy_to_buffer(bsg_job->request_payload.sg_list, | ||
138 | bsg_job->request_payload.sg_cnt, flash, length); | ||
139 | |||
140 | if (qla4xxx_set_flash(ha, flash_dma, offset, length, options)) | ||
141 | bsg_reply->result = (DID_ERROR << 16); | ||
142 | else { | ||
143 | bsg_reply->result = DID_OK; | ||
144 | bsg_reply->reply_payload_rcv_len = length; | ||
145 | } | ||
146 | |||
147 | if (flash) | ||
148 | dma_free_coherent(&ha->pdev->dev, length, flash, flash_dma); | ||
149 | ha->flash_state = QLFLASH_WAITING; | ||
150 | unmap_sg: | ||
151 | dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, | ||
152 | bsg_job->reply_payload.sg_cnt, DMA_TO_DEVICE); | ||
153 | |||
154 | if (!rval) | ||
155 | bsg_job_done(bsg_job, bsg_reply->result, | ||
156 | bsg_reply->reply_payload_rcv_len); | ||
157 | return rval; | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * qla4xxx_process_vendor_specific - handle vendor specific bsg request | ||
162 | * @job: iscsi_bsg_job to handle | ||
163 | **/ | ||
164 | int qla4xxx_process_vendor_specific(struct bsg_job *bsg_job) | ||
165 | { | ||
166 | struct iscsi_bsg_reply *bsg_reply = bsg_job->reply; | ||
167 | struct iscsi_bsg_request *bsg_req = bsg_job->request; | ||
168 | struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); | ||
169 | struct scsi_qla_host *ha = to_qla_host(host); | ||
170 | |||
171 | switch (bsg_req->rqst_data.h_vendor.vendor_cmd[0]) { | ||
172 | case QLISCSI_VND_READ_FLASH: | ||
173 | return qla4xxx_read_flash(bsg_job); | ||
174 | |||
175 | case QLISCSI_VND_UPDATE_FLASH: | ||
176 | return qla4xxx_update_flash(bsg_job); | ||
177 | |||
178 | default: | ||
179 | ql4_printk(KERN_ERR, ha, "%s: invalid BSG vendor command: " | ||
180 | "0x%x\n", __func__, bsg_req->msgcode); | ||
181 | bsg_reply->result = (DID_ERROR << 16); | ||
182 | bsg_reply->reply_payload_rcv_len = 0; | ||
183 | bsg_job_done(bsg_job, bsg_reply->result, | ||
184 | bsg_reply->reply_payload_rcv_len); | ||
185 | return -ENOSYS; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * qla4xxx_bsg_request - handle bsg request from ISCSI transport | ||
191 | * @job: iscsi_bsg_job to handle | ||
192 | */ | ||
193 | int qla4xxx_bsg_request(struct bsg_job *bsg_job) | ||
194 | { | ||
195 | struct iscsi_bsg_request *bsg_req = bsg_job->request; | ||
196 | struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); | ||
197 | struct scsi_qla_host *ha = to_qla_host(host); | ||
198 | |||
199 | switch (bsg_req->msgcode) { | ||
200 | case ISCSI_BSG_HST_VENDOR: | ||
201 | return qla4xxx_process_vendor_specific(bsg_job); | ||
202 | |||
203 | default: | ||
204 | ql4_printk(KERN_ERR, ha, "%s: invalid BSG command: 0x%x\n", | ||
205 | __func__, bsg_req->msgcode); | ||
206 | } | ||
207 | |||
208 | return -ENOSYS; | ||
209 | } | ||