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 47 static 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 59 static void clear_vda_request(struct esas2r_request *rq); 60 61 static 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. */ 65 bool 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 (a->flags & AF_DEGRADED_MODE) { 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 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(rq->vrq->mgt.mgt_func, &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(rq->vrq->cfg.sub_func, 229 &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, 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 269 static 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(rq->vrq->mgt.mgt_func, &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 306 cfg->data_length = 307 cpu_to_le32(sizeof(struct atto_vda_cfg_init)); 308 cfg->data.init.vda_version = 309 le32_to_cpu(rsp->vda_version); 310 cfg->data.init.fw_build = rsp->fw_build; 311 312 sprintf((char *)&cfg->data.init.fw_release, 313 "%1d.%02d", 314 (int)LOBYTE(le16_to_cpu(rsp->fw_release)), 315 (int)HIBYTE(le16_to_cpu(rsp->fw_release))); 316 317 if (LOWORD(LOBYTE(cfg->data.init.fw_build)) == 'A') 318 cfg->data.init.fw_version = 319 cfg->data.init.fw_build; 320 else 321 cfg->data.init.fw_version = 322 cfg->data.init.fw_release; 323 } else { 324 esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func, 325 &vi->cmd.cfg.data); 326 } 327 328 break; 329 330 case VDA_FUNC_CLI: 331 332 vi->cmd.cli.cmd_rsp_len = 333 le32_to_cpu(rq->func_rsp.cli_rsp.cmd_rsp_len); 334 break; 335 336 default: 337 338 break; 339 } 340 } 341 342 /* Build a flash VDA request. */ 343 void esas2r_build_flash_req(struct esas2r_adapter *a, 344 struct esas2r_request *rq, 345 u8 sub_func, 346 u8 cksum, 347 u32 addr, 348 u32 length) 349 { 350 struct atto_vda_flash_req *vrq = &rq->vrq->flash; 351 352 clear_vda_request(rq); 353 354 rq->vrq->scsi.function = VDA_FUNC_FLASH; 355 356 if (sub_func == VDA_FLASH_BEGINW 357 || sub_func == VDA_FLASH_WRITE 358 || sub_func == VDA_FLASH_READ) 359 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_flash_req, 360 data.sge); 361 362 vrq->length = cpu_to_le32(length); 363 vrq->flash_addr = cpu_to_le32(addr); 364 vrq->checksum = cksum; 365 vrq->sub_func = sub_func; 366 } 367 368 /* Build a VDA management request. */ 369 void esas2r_build_mgt_req(struct esas2r_adapter *a, 370 struct esas2r_request *rq, 371 u8 sub_func, 372 u8 scan_gen, 373 u16 dev_index, 374 u32 length, 375 void *data) 376 { 377 struct atto_vda_mgmt_req *vrq = &rq->vrq->mgt; 378 379 clear_vda_request(rq); 380 381 rq->vrq->scsi.function = VDA_FUNC_MGT; 382 383 vrq->mgt_func = sub_func; 384 vrq->scan_generation = scan_gen; 385 vrq->dev_index = cpu_to_le16(dev_index); 386 vrq->length = cpu_to_le32(length); 387 388 if (vrq->length) { 389 if (a->flags & AF_LEGACY_SGE_MODE) { 390 vrq->sg_list_offset = (u8)offsetof( 391 struct atto_vda_mgmt_req, sge); 392 393 vrq->sge[0].length = cpu_to_le32(SGE_LAST | length); 394 vrq->sge[0].address = cpu_to_le64( 395 rq->vrq_md->phys_addr + 396 sizeof(union atto_vda_req)); 397 } else { 398 vrq->sg_list_offset = (u8)offsetof( 399 struct atto_vda_mgmt_req, prde); 400 401 vrq->prde[0].ctl_len = cpu_to_le32(length); 402 vrq->prde[0].address = cpu_to_le64( 403 rq->vrq_md->phys_addr + 404 sizeof(union atto_vda_req)); 405 } 406 } 407 408 if (data) { 409 esas2r_nuxi_mgt_data(sub_func, data); 410 411 memcpy(&rq->vda_rsp_data->mgt_data.data.bytes[0], data, 412 length); 413 } 414 } 415 416 /* Build a VDA asyncronous event (AE) request. */ 417 void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq) 418 { 419 struct atto_vda_ae_req *vrq = &rq->vrq->ae; 420 421 clear_vda_request(rq); 422 423 rq->vrq->scsi.function = VDA_FUNC_AE; 424 425 vrq->length = cpu_to_le32(sizeof(struct atto_vda_ae_data)); 426 427 if (a->flags & AF_LEGACY_SGE_MODE) { 428 vrq->sg_list_offset = 429 (u8)offsetof(struct atto_vda_ae_req, sge); 430 vrq->sge[0].length = cpu_to_le32(SGE_LAST | vrq->length); 431 vrq->sge[0].address = cpu_to_le64( 432 rq->vrq_md->phys_addr + 433 sizeof(union atto_vda_req)); 434 } else { 435 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ae_req, 436 prde); 437 vrq->prde[0].ctl_len = cpu_to_le32(vrq->length); 438 vrq->prde[0].address = cpu_to_le64( 439 rq->vrq_md->phys_addr + 440 sizeof(union atto_vda_req)); 441 } 442 } 443 444 /* Build a VDA CLI request. */ 445 void esas2r_build_cli_req(struct esas2r_adapter *a, 446 struct esas2r_request *rq, 447 u32 length, 448 u32 cmd_rsp_len) 449 { 450 struct atto_vda_cli_req *vrq = &rq->vrq->cli; 451 452 clear_vda_request(rq); 453 454 rq->vrq->scsi.function = VDA_FUNC_CLI; 455 456 vrq->length = cpu_to_le32(length); 457 vrq->cmd_rsp_len = cpu_to_le32(cmd_rsp_len); 458 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_cli_req, sge); 459 } 460 461 /* Build a VDA IOCTL request. */ 462 void esas2r_build_ioctl_req(struct esas2r_adapter *a, 463 struct esas2r_request *rq, 464 u32 length, 465 u8 sub_func) 466 { 467 struct atto_vda_ioctl_req *vrq = &rq->vrq->ioctl; 468 469 clear_vda_request(rq); 470 471 rq->vrq->scsi.function = VDA_FUNC_IOCTL; 472 473 vrq->length = cpu_to_le32(length); 474 vrq->sub_func = sub_func; 475 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ioctl_req, sge); 476 } 477 478 /* Build a VDA configuration request. */ 479 void esas2r_build_cfg_req(struct esas2r_adapter *a, 480 struct esas2r_request *rq, 481 u8 sub_func, 482 u32 length, 483 void *data) 484 { 485 struct atto_vda_cfg_req *vrq = &rq->vrq->cfg; 486 487 clear_vda_request(rq); 488 489 rq->vrq->scsi.function = VDA_FUNC_CFG; 490 491 vrq->sub_func = sub_func; 492 vrq->length = cpu_to_le32(length); 493 494 if (data) { 495 esas2r_nuxi_cfg_data(sub_func, data); 496 497 memcpy(&vrq->data, data, length); 498 } 499 } 500 501 static void clear_vda_request(struct esas2r_request *rq) 502 { 503 u32 handle = rq->vrq->scsi.handle; 504 505 memset(rq->vrq, 0, sizeof(*rq->vrq)); 506 507 rq->vrq->scsi.handle = handle; 508 509 rq->req_stat = RS_PENDING; 510 511 /* since the data buffer is separate clear that too */ 512 513 memset(rq->data_buf, 0, ESAS2R_DATA_BUF_LEN); 514 515 /* 516 * Setup next and prev pointer in case the request is not going through 517 * esas2r_start_request(). 518 */ 519 520 INIT_LIST_HEAD(&rq->req_list); 521 } 522