1/*
2 * linux/drivers/scsi/esas2r/esas2r_vda.c
3 * esas2r driver VDA firmware interface functions
4 *
5 * Copyright (c) 2001-2013 ATTO Technology, Inc.
6 * (mailto:linuxdrivers@attotech.com)
7 */
8/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
9/*
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * NO WARRANTY
20 * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
21 * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
22 * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
23 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
24 * solely responsible for determining the appropriateness of using and
25 * distributing the Program and assumes all risks associated with its
26 * exercise of rights under this Agreement, including but not limited to
27 * the risks and costs of program errors, damage to or loss of data,
28 * programs or equipment, and unavailability or interruption of operations.
29 *
30 * DISCLAIMER OF LIABILITY
31 * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
32 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
34 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
35 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
36 * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
37 * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
38 *
39 * You should have received a copy of the GNU General Public License
40 * along with this program; if not, write to the Free Software
41 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
42 */
43/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
44
45#include "esas2r.h"
46
47static u8 esas2r_vdaioctl_versions[] = {
48 ATTO_VDA_VER_UNSUPPORTED,
49 ATTO_VDA_FLASH_VER,
50 ATTO_VDA_VER_UNSUPPORTED,
51 ATTO_VDA_VER_UNSUPPORTED,
52 ATTO_VDA_CLI_VER,
53 ATTO_VDA_VER_UNSUPPORTED,
54 ATTO_VDA_CFG_VER,
55 ATTO_VDA_MGT_VER,
56 ATTO_VDA_GSV_VER
57};
58
59static void clear_vda_request(struct esas2r_request *rq);
60
61static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
62 struct esas2r_request *rq);
63
64/* Prepare a VDA IOCTL request to be sent to the firmware. */
65bool esas2r_process_vda_ioctl(struct esas2r_adapter *a,
66 struct atto_ioctl_vda *vi,
67 struct esas2r_request *rq,
68 struct esas2r_sg_context *sgc)
69{
70 u32 datalen = 0;
71 struct atto_vda_sge *firstsg = NULL;
72 u8 vercnt = (u8)ARRAY_SIZE(esas2r_vdaioctl_versions);
73
74 vi->status = ATTO_STS_SUCCESS;
75 vi->vda_status = RS_PENDING;
76
77 if (vi->function >= vercnt) {
78 vi->status = ATTO_STS_INV_FUNC;
79 return false;
80 }
81
82 if (vi->version > esas2r_vdaioctl_versions[vi->function]) {
83 vi->status = ATTO_STS_INV_VERSION;
84 return false;
85 }
86
87 if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
88 vi->status = ATTO_STS_DEGRADED;
89 return false;
90 }
91
92 if (vi->function != VDA_FUNC_SCSI)
93 clear_vda_request(rq);
94
95 rq->vrq->scsi.function = vi->function;
96 rq->interrupt_cb = esas2r_complete_vda_ioctl;
97 rq->interrupt_cx = vi;
98
99 switch (vi->function) {
100 case VDA_FUNC_FLASH:
101
102 if (vi->cmd.flash.sub_func != VDA_FLASH_FREAD
103 && vi->cmd.flash.sub_func != VDA_FLASH_FWRITE
104 && vi->cmd.flash.sub_func != VDA_FLASH_FINFO) {
105 vi->status = ATTO_STS_INV_FUNC;
106 return false;
107 }
108
109 if (vi->cmd.flash.sub_func != VDA_FLASH_FINFO)
110 datalen = vi->data_length;
111
112 rq->vrq->flash.length = cpu_to_le32(datalen);
113 rq->vrq->flash.sub_func = vi->cmd.flash.sub_func;
114
115 memcpy(rq->vrq->flash.data.file.file_name,
116 vi->cmd.flash.data.file.file_name,
117 sizeof(vi->cmd.flash.data.file.file_name));
118
119 firstsg = rq->vrq->flash.data.file.sge;
120 break;
121
122 case VDA_FUNC_CLI:
123
124 datalen = vi->data_length;
125
126 rq->vrq->cli.cmd_rsp_len =
127 cpu_to_le32(vi->cmd.cli.cmd_rsp_len);
128 rq->vrq->cli.length = cpu_to_le32(datalen);
129
130 firstsg = rq->vrq->cli.sge;
131 break;
132
133 case VDA_FUNC_MGT:
134 {
135 u8 *cmdcurr_offset = sgc->cur_offset
136 - offsetof(struct atto_ioctl_vda, data)
137 + offsetof(struct atto_ioctl_vda, cmd)
138 + offsetof(struct atto_ioctl_vda_mgt_cmd,
139 data);
140 /*
141 * build the data payload SGL here first since
142 * esas2r_sgc_init() will modify the S/G list offset for the
143 * management SGL (which is built below where the data SGL is
144 * usually built).
145 */
146
147 if (vi->data_length) {
148 u32 payldlen = 0;
149
150 if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_HEALTH_REQ
151 || vi->cmd.mgt.mgt_func == VDAMGT_DEV_METRICS) {
152 rq->vrq->mgt.payld_sglst_offset =
153 (u8)offsetof(struct atto_vda_mgmt_req,
154 payld_sge);
155
156 payldlen = vi->data_length;
157 datalen = vi->cmd.mgt.data_length;
158 } else if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_INFO2
159 || vi->cmd.mgt.mgt_func ==
160 VDAMGT_DEV_INFO2_BYADDR) {
161 datalen = vi->data_length;
162 cmdcurr_offset = sgc->cur_offset;
163 } else {
164 vi->status = ATTO_STS_INV_PARAM;
165 return false;
166 }
167
168 /* Setup the length so building the payload SGL works */
169 rq->vrq->mgt.length = cpu_to_le32(datalen);
170
171 if (payldlen) {
172 rq->vrq->mgt.payld_length =
173 cpu_to_le32(payldlen);
174
175 esas2r_sgc_init(sgc, a, rq,
176 first: rq->vrq->mgt.payld_sge);
177 sgc->length = payldlen;
178
179 if (!esas2r_build_sg_list(a, rq, sgc)) {
180 vi->status = ATTO_STS_OUT_OF_RSRC;
181 return false;
182 }
183 }
184 } else {
185 datalen = vi->cmd.mgt.data_length;
186
187 rq->vrq->mgt.length = cpu_to_le32(datalen);
188 }
189
190 /*
191 * Now that the payload SGL is built, if any, setup to build
192 * the management SGL.
193 */
194 firstsg = rq->vrq->mgt.sge;
195 sgc->cur_offset = cmdcurr_offset;
196
197 /* Finish initializing the management request. */
198 rq->vrq->mgt.mgt_func = vi->cmd.mgt.mgt_func;
199 rq->vrq->mgt.scan_generation = vi->cmd.mgt.scan_generation;
200 rq->vrq->mgt.dev_index =
201 cpu_to_le32(vi->cmd.mgt.dev_index);
202
203 esas2r_nuxi_mgt_data(function: rq->vrq->mgt.mgt_func, data: &vi->cmd.mgt.data);
204 break;
205 }
206
207 case VDA_FUNC_CFG:
208
209 if (vi->data_length
210 || vi->cmd.cfg.data_length == 0) {
211 vi->status = ATTO_STS_INV_PARAM;
212 return false;
213 }
214
215 if (vi->cmd.cfg.cfg_func == VDA_CFG_INIT) {
216 vi->status = ATTO_STS_INV_FUNC;
217 return false;
218 }
219
220 rq->vrq->cfg.sub_func = vi->cmd.cfg.cfg_func;
221 rq->vrq->cfg.length = cpu_to_le32(vi->cmd.cfg.data_length);
222
223 if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
224 memcpy(&rq->vrq->cfg.data,
225 &vi->cmd.cfg.data,
226 vi->cmd.cfg.data_length);
227
228 esas2r_nuxi_cfg_data(function: rq->vrq->cfg.sub_func,
229 data: &rq->vrq->cfg.data);
230 } else {
231 vi->status = ATTO_STS_INV_FUNC;
232
233 return false;
234 }
235
236 break;
237
238 case VDA_FUNC_GSV:
239
240 vi->cmd.gsv.rsp_len = vercnt;
241
242 memcpy(vi->cmd.gsv.version_info, esas2r_vdaioctl_versions,
243 vercnt);
244
245 vi->vda_status = RS_SUCCESS;
246 break;
247
248 default:
249
250 vi->status = ATTO_STS_INV_FUNC;
251 return false;
252 }
253
254 if (datalen) {
255 esas2r_sgc_init(sgc, a, rq, first: firstsg);
256 sgc->length = datalen;
257
258 if (!esas2r_build_sg_list(a, rq, sgc)) {
259 vi->status = ATTO_STS_OUT_OF_RSRC;
260 return false;
261 }
262 }
263
264 esas2r_start_request(a, rq);
265
266 return true;
267}
268
269static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
270 struct esas2r_request *rq)
271{
272 struct atto_ioctl_vda *vi = (struct atto_ioctl_vda *)rq->interrupt_cx;
273
274 vi->vda_status = rq->req_stat;
275
276 switch (vi->function) {
277 case VDA_FUNC_FLASH:
278
279 if (vi->cmd.flash.sub_func == VDA_FLASH_FINFO
280 || vi->cmd.flash.sub_func == VDA_FLASH_FREAD)
281 vi->cmd.flash.data.file.file_size =
282 le32_to_cpu(rq->func_rsp.flash_rsp.file_size);
283
284 break;
285
286 case VDA_FUNC_MGT:
287
288 vi->cmd.mgt.scan_generation =
289 rq->func_rsp.mgt_rsp.scan_generation;
290 vi->cmd.mgt.dev_index = le16_to_cpu(
291 rq->func_rsp.mgt_rsp.dev_index);
292
293 if (vi->data_length == 0)
294 vi->cmd.mgt.data_length =
295 le32_to_cpu(rq->func_rsp.mgt_rsp.length);
296
297 esas2r_nuxi_mgt_data(function: rq->vrq->mgt.mgt_func, data: &vi->cmd.mgt.data);
298 break;
299
300 case VDA_FUNC_CFG:
301
302 if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
303 struct atto_ioctl_vda_cfg_cmd *cfg = &vi->cmd.cfg;
304 struct atto_vda_cfg_rsp *rsp = &rq->func_rsp.cfg_rsp;
305 char buf[sizeof(cfg->data.init.fw_release) + 1];
306
307 cfg->data_length =
308 cpu_to_le32(sizeof(struct atto_vda_cfg_init));
309 cfg->data.init.vda_version =
310 le32_to_cpu(rsp->vda_version);
311 cfg->data.init.fw_build = rsp->fw_build;
312
313 snprintf(buf, size: sizeof(buf), fmt: "%1.1u.%2.2u",
314 (int)LOBYTE(le16_to_cpu(rsp->fw_release)),
315 (int)HIBYTE(le16_to_cpu(rsp->fw_release)));
316
317 memcpy(&cfg->data.init.fw_release, buf,
318 sizeof(cfg->data.init.fw_release));
319
320 if (LOWORD(LOBYTE(cfg->data.init.fw_build)) == 'A')
321 cfg->data.init.fw_version =
322 cfg->data.init.fw_build;
323 else
324 cfg->data.init.fw_version =
325 cfg->data.init.fw_release;
326 } else {
327 esas2r_nuxi_cfg_data(function: rq->vrq->cfg.sub_func,
328 data: &vi->cmd.cfg.data);
329 }
330
331 break;
332
333 case VDA_FUNC_CLI:
334
335 vi->cmd.cli.cmd_rsp_len =
336 le32_to_cpu(rq->func_rsp.cli_rsp.cmd_rsp_len);
337 break;
338
339 default:
340
341 break;
342 }
343}
344
345/* Build a flash VDA request. */
346void esas2r_build_flash_req(struct esas2r_adapter *a,
347 struct esas2r_request *rq,
348 u8 sub_func,
349 u8 cksum,
350 u32 addr,
351 u32 length)
352{
353 struct atto_vda_flash_req *vrq = &rq->vrq->flash;
354
355 clear_vda_request(rq);
356
357 rq->vrq->scsi.function = VDA_FUNC_FLASH;
358
359 if (sub_func == VDA_FLASH_BEGINW
360 || sub_func == VDA_FLASH_WRITE
361 || sub_func == VDA_FLASH_READ)
362 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_flash_req,
363 data.sge);
364
365 vrq->length = cpu_to_le32(length);
366 vrq->flash_addr = cpu_to_le32(addr);
367 vrq->checksum = cksum;
368 vrq->sub_func = sub_func;
369}
370
371/* Build a VDA management request. */
372void esas2r_build_mgt_req(struct esas2r_adapter *a,
373 struct esas2r_request *rq,
374 u8 sub_func,
375 u8 scan_gen,
376 u16 dev_index,
377 u32 length,
378 void *data)
379{
380 struct atto_vda_mgmt_req *vrq = &rq->vrq->mgt;
381
382 clear_vda_request(rq);
383
384 rq->vrq->scsi.function = VDA_FUNC_MGT;
385
386 vrq->mgt_func = sub_func;
387 vrq->scan_generation = scan_gen;
388 vrq->dev_index = cpu_to_le16(dev_index);
389 vrq->length = cpu_to_le32(length);
390
391 if (vrq->length) {
392 if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
393 vrq->sg_list_offset = (u8)offsetof(
394 struct atto_vda_mgmt_req, sge);
395
396 vrq->sge[0].length = cpu_to_le32(SGE_LAST | length);
397 vrq->sge[0].address = cpu_to_le64(
398 rq->vrq_md->phys_addr +
399 sizeof(union atto_vda_req));
400 } else {
401 vrq->sg_list_offset = (u8)offsetof(
402 struct atto_vda_mgmt_req, prde);
403
404 vrq->prde[0].ctl_len = cpu_to_le32(length);
405 vrq->prde[0].address = cpu_to_le64(
406 rq->vrq_md->phys_addr +
407 sizeof(union atto_vda_req));
408 }
409 }
410
411 if (data) {
412 esas2r_nuxi_mgt_data(function: sub_func, data);
413
414 memcpy(&rq->vda_rsp_data->mgt_data.data.bytes[0], data,
415 length);
416 }
417}
418
419/* Build a VDA asyncronous event (AE) request. */
420void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq)
421{
422 struct atto_vda_ae_req *vrq = &rq->vrq->ae;
423
424 clear_vda_request(rq);
425
426 rq->vrq->scsi.function = VDA_FUNC_AE;
427
428 vrq->length = cpu_to_le32(sizeof(struct atto_vda_ae_data));
429
430 if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
431 vrq->sg_list_offset =
432 (u8)offsetof(struct atto_vda_ae_req, sge);
433 vrq->sge[0].length = cpu_to_le32(SGE_LAST | vrq->length);
434 vrq->sge[0].address = cpu_to_le64(
435 rq->vrq_md->phys_addr +
436 sizeof(union atto_vda_req));
437 } else {
438 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ae_req,
439 prde);
440 vrq->prde[0].ctl_len = cpu_to_le32(vrq->length);
441 vrq->prde[0].address = cpu_to_le64(
442 rq->vrq_md->phys_addr +
443 sizeof(union atto_vda_req));
444 }
445}
446
447/* Build a VDA CLI request. */
448void esas2r_build_cli_req(struct esas2r_adapter *a,
449 struct esas2r_request *rq,
450 u32 length,
451 u32 cmd_rsp_len)
452{
453 struct atto_vda_cli_req *vrq = &rq->vrq->cli;
454
455 clear_vda_request(rq);
456
457 rq->vrq->scsi.function = VDA_FUNC_CLI;
458
459 vrq->length = cpu_to_le32(length);
460 vrq->cmd_rsp_len = cpu_to_le32(cmd_rsp_len);
461 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_cli_req, sge);
462}
463
464/* Build a VDA IOCTL request. */
465void esas2r_build_ioctl_req(struct esas2r_adapter *a,
466 struct esas2r_request *rq,
467 u32 length,
468 u8 sub_func)
469{
470 struct atto_vda_ioctl_req *vrq = &rq->vrq->ioctl;
471
472 clear_vda_request(rq);
473
474 rq->vrq->scsi.function = VDA_FUNC_IOCTL;
475
476 vrq->length = cpu_to_le32(length);
477 vrq->sub_func = sub_func;
478 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ioctl_req, sge);
479}
480
481/* Build a VDA configuration request. */
482void esas2r_build_cfg_req(struct esas2r_adapter *a,
483 struct esas2r_request *rq,
484 u8 sub_func,
485 u32 length,
486 void *data)
487{
488 struct atto_vda_cfg_req *vrq = &rq->vrq->cfg;
489
490 clear_vda_request(rq);
491
492 rq->vrq->scsi.function = VDA_FUNC_CFG;
493
494 vrq->sub_func = sub_func;
495 vrq->length = cpu_to_le32(length);
496
497 if (data) {
498 esas2r_nuxi_cfg_data(function: sub_func, data);
499
500 memcpy(&vrq->data, data, length);
501 }
502}
503
504static void clear_vda_request(struct esas2r_request *rq)
505{
506 u32 handle = rq->vrq->scsi.handle;
507
508 memset(rq->vrq, 0, sizeof(*rq->vrq));
509
510 rq->vrq->scsi.handle = handle;
511
512 rq->req_stat = RS_PENDING;
513
514 /* since the data buffer is separate clear that too */
515
516 memset(rq->data_buf, 0, ESAS2R_DATA_BUF_LEN);
517
518 /*
519 * Setup next and prev pointer in case the request is not going through
520 * esas2r_start_request().
521 */
522
523 INIT_LIST_HEAD(list: &rq->req_list);
524}
525

source code of linux/drivers/scsi/esas2r/esas2r_vda.c