Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *  linux/drivers/scsi/esas2r/esas2r_vda.c
0003  *      esas2r driver VDA firmware interface functions
0004  *
0005  *  Copyright (c) 2001-2013 ATTO Technology, Inc.
0006  *  (mailto:linuxdrivers@attotech.com)
0007  */
0008 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
0009 /*
0010  *  This program is free software; you can redistribute it and/or modify
0011  *  it under the terms of the GNU General Public License as published by
0012  *  the Free Software Foundation; version 2 of the License.
0013  *
0014  *  This program is distributed in the hope that it will be useful,
0015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0017  *  GNU General Public License for more details.
0018  *
0019  *  NO WARRANTY
0020  *  THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
0021  *  CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
0022  *  LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
0023  *  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
0024  *  solely responsible for determining the appropriateness of using and
0025  *  distributing the Program and assumes all risks associated with its
0026  *  exercise of rights under this Agreement, including but not limited to
0027  *  the risks and costs of program errors, damage to or loss of data,
0028  *  programs or equipment, and unavailability or interruption of operations.
0029  *
0030  *  DISCLAIMER OF LIABILITY
0031  *  NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
0032  *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0033  *  DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
0034  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
0035  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
0036  *  USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
0037  *  HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
0038  *
0039  *  You should have received a copy of the GNU General Public License
0040  *  along with this program; if not, write to the Free Software
0041  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0042  */
0043 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
0044 
0045 #include "esas2r.h"
0046 
0047 static u8 esas2r_vdaioctl_versions[] = {
0048     ATTO_VDA_VER_UNSUPPORTED,
0049     ATTO_VDA_FLASH_VER,
0050     ATTO_VDA_VER_UNSUPPORTED,
0051     ATTO_VDA_VER_UNSUPPORTED,
0052     ATTO_VDA_CLI_VER,
0053     ATTO_VDA_VER_UNSUPPORTED,
0054     ATTO_VDA_CFG_VER,
0055     ATTO_VDA_MGT_VER,
0056     ATTO_VDA_GSV_VER
0057 };
0058 
0059 static void clear_vda_request(struct esas2r_request *rq);
0060 
0061 static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
0062                       struct esas2r_request *rq);
0063 
0064 /* Prepare a VDA IOCTL request to be sent to the firmware. */
0065 bool esas2r_process_vda_ioctl(struct esas2r_adapter *a,
0066                   struct atto_ioctl_vda *vi,
0067                   struct esas2r_request *rq,
0068                   struct esas2r_sg_context *sgc)
0069 {
0070     u32 datalen = 0;
0071     struct atto_vda_sge *firstsg = NULL;
0072     u8 vercnt = (u8)ARRAY_SIZE(esas2r_vdaioctl_versions);
0073 
0074     vi->status = ATTO_STS_SUCCESS;
0075     vi->vda_status = RS_PENDING;
0076 
0077     if (vi->function >= vercnt) {
0078         vi->status = ATTO_STS_INV_FUNC;
0079         return false;
0080     }
0081 
0082     if (vi->version > esas2r_vdaioctl_versions[vi->function]) {
0083         vi->status = ATTO_STS_INV_VERSION;
0084         return false;
0085     }
0086 
0087     if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
0088         vi->status = ATTO_STS_DEGRADED;
0089         return false;
0090     }
0091 
0092     if (vi->function != VDA_FUNC_SCSI)
0093         clear_vda_request(rq);
0094 
0095     rq->vrq->scsi.function = vi->function;
0096     rq->interrupt_cb = esas2r_complete_vda_ioctl;
0097     rq->interrupt_cx = vi;
0098 
0099     switch (vi->function) {
0100     case VDA_FUNC_FLASH:
0101 
0102         if (vi->cmd.flash.sub_func != VDA_FLASH_FREAD
0103             && vi->cmd.flash.sub_func != VDA_FLASH_FWRITE
0104             && vi->cmd.flash.sub_func != VDA_FLASH_FINFO) {
0105             vi->status = ATTO_STS_INV_FUNC;
0106             return false;
0107         }
0108 
0109         if (vi->cmd.flash.sub_func != VDA_FLASH_FINFO)
0110             datalen = vi->data_length;
0111 
0112         rq->vrq->flash.length = cpu_to_le32(datalen);
0113         rq->vrq->flash.sub_func = vi->cmd.flash.sub_func;
0114 
0115         memcpy(rq->vrq->flash.data.file.file_name,
0116                vi->cmd.flash.data.file.file_name,
0117                sizeof(vi->cmd.flash.data.file.file_name));
0118 
0119         firstsg = rq->vrq->flash.data.file.sge;
0120         break;
0121 
0122     case VDA_FUNC_CLI:
0123 
0124         datalen = vi->data_length;
0125 
0126         rq->vrq->cli.cmd_rsp_len =
0127             cpu_to_le32(vi->cmd.cli.cmd_rsp_len);
0128         rq->vrq->cli.length = cpu_to_le32(datalen);
0129 
0130         firstsg = rq->vrq->cli.sge;
0131         break;
0132 
0133     case VDA_FUNC_MGT:
0134     {
0135         u8 *cmdcurr_offset = sgc->cur_offset
0136                      - offsetof(struct atto_ioctl_vda, data)
0137                      + offsetof(struct atto_ioctl_vda, cmd)
0138                      + offsetof(struct atto_ioctl_vda_mgt_cmd,
0139                         data);
0140         /*
0141          * build the data payload SGL here first since
0142          * esas2r_sgc_init() will modify the S/G list offset for the
0143          * management SGL (which is built below where the data SGL is
0144          * usually built).
0145          */
0146 
0147         if (vi->data_length) {
0148             u32 payldlen = 0;
0149 
0150             if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_HEALTH_REQ
0151                 || vi->cmd.mgt.mgt_func == VDAMGT_DEV_METRICS) {
0152                 rq->vrq->mgt.payld_sglst_offset =
0153                     (u8)offsetof(struct atto_vda_mgmt_req,
0154                              payld_sge);
0155 
0156                 payldlen = vi->data_length;
0157                 datalen = vi->cmd.mgt.data_length;
0158             } else if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_INFO2
0159                    || vi->cmd.mgt.mgt_func ==
0160                    VDAMGT_DEV_INFO2_BYADDR) {
0161                 datalen = vi->data_length;
0162                 cmdcurr_offset = sgc->cur_offset;
0163             } else {
0164                 vi->status = ATTO_STS_INV_PARAM;
0165                 return false;
0166             }
0167 
0168             /* Setup the length so building the payload SGL works */
0169             rq->vrq->mgt.length = cpu_to_le32(datalen);
0170 
0171             if (payldlen) {
0172                 rq->vrq->mgt.payld_length =
0173                     cpu_to_le32(payldlen);
0174 
0175                 esas2r_sgc_init(sgc, a, rq,
0176                         rq->vrq->mgt.payld_sge);
0177                 sgc->length = payldlen;
0178 
0179                 if (!esas2r_build_sg_list(a, rq, sgc)) {
0180                     vi->status = ATTO_STS_OUT_OF_RSRC;
0181                     return false;
0182                 }
0183             }
0184         } else {
0185             datalen = vi->cmd.mgt.data_length;
0186 
0187             rq->vrq->mgt.length = cpu_to_le32(datalen);
0188         }
0189 
0190         /*
0191          * Now that the payload SGL is built, if any, setup to build
0192          * the management SGL.
0193          */
0194         firstsg = rq->vrq->mgt.sge;
0195         sgc->cur_offset = cmdcurr_offset;
0196 
0197         /* Finish initializing the management request. */
0198         rq->vrq->mgt.mgt_func = vi->cmd.mgt.mgt_func;
0199         rq->vrq->mgt.scan_generation = vi->cmd.mgt.scan_generation;
0200         rq->vrq->mgt.dev_index =
0201             cpu_to_le32(vi->cmd.mgt.dev_index);
0202 
0203         esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);
0204         break;
0205     }
0206 
0207     case VDA_FUNC_CFG:
0208 
0209         if (vi->data_length
0210             || vi->cmd.cfg.data_length == 0) {
0211             vi->status = ATTO_STS_INV_PARAM;
0212             return false;
0213         }
0214 
0215         if (vi->cmd.cfg.cfg_func == VDA_CFG_INIT) {
0216             vi->status = ATTO_STS_INV_FUNC;
0217             return false;
0218         }
0219 
0220         rq->vrq->cfg.sub_func = vi->cmd.cfg.cfg_func;
0221         rq->vrq->cfg.length = cpu_to_le32(vi->cmd.cfg.data_length);
0222 
0223         if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
0224             memcpy(&rq->vrq->cfg.data,
0225                    &vi->cmd.cfg.data,
0226                    vi->cmd.cfg.data_length);
0227 
0228             esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
0229                          &rq->vrq->cfg.data);
0230         } else {
0231             vi->status = ATTO_STS_INV_FUNC;
0232 
0233             return false;
0234         }
0235 
0236         break;
0237 
0238     case VDA_FUNC_GSV:
0239 
0240         vi->cmd.gsv.rsp_len = vercnt;
0241 
0242         memcpy(vi->cmd.gsv.version_info, esas2r_vdaioctl_versions,
0243                vercnt);
0244 
0245         vi->vda_status = RS_SUCCESS;
0246         break;
0247 
0248     default:
0249 
0250         vi->status = ATTO_STS_INV_FUNC;
0251         return false;
0252     }
0253 
0254     if (datalen) {
0255         esas2r_sgc_init(sgc, a, rq, firstsg);
0256         sgc->length = datalen;
0257 
0258         if (!esas2r_build_sg_list(a, rq, sgc)) {
0259             vi->status = ATTO_STS_OUT_OF_RSRC;
0260             return false;
0261         }
0262     }
0263 
0264     esas2r_start_request(a, rq);
0265 
0266     return true;
0267 }
0268 
0269 static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
0270                       struct esas2r_request *rq)
0271 {
0272     struct atto_ioctl_vda *vi = (struct atto_ioctl_vda *)rq->interrupt_cx;
0273 
0274     vi->vda_status = rq->req_stat;
0275 
0276     switch (vi->function) {
0277     case VDA_FUNC_FLASH:
0278 
0279         if (vi->cmd.flash.sub_func == VDA_FLASH_FINFO
0280             || vi->cmd.flash.sub_func == VDA_FLASH_FREAD)
0281             vi->cmd.flash.data.file.file_size =
0282                 le32_to_cpu(rq->func_rsp.flash_rsp.file_size);
0283 
0284         break;
0285 
0286     case VDA_FUNC_MGT:
0287 
0288         vi->cmd.mgt.scan_generation =
0289             rq->func_rsp.mgt_rsp.scan_generation;
0290         vi->cmd.mgt.dev_index = le16_to_cpu(
0291             rq->func_rsp.mgt_rsp.dev_index);
0292 
0293         if (vi->data_length == 0)
0294             vi->cmd.mgt.data_length =
0295                 le32_to_cpu(rq->func_rsp.mgt_rsp.length);
0296 
0297         esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);
0298         break;
0299 
0300     case VDA_FUNC_CFG:
0301 
0302         if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
0303             struct atto_ioctl_vda_cfg_cmd *cfg = &vi->cmd.cfg;
0304             struct atto_vda_cfg_rsp *rsp = &rq->func_rsp.cfg_rsp;
0305             char buf[sizeof(cfg->data.init.fw_release) + 1];
0306 
0307             cfg->data_length =
0308                 cpu_to_le32(sizeof(struct atto_vda_cfg_init));
0309             cfg->data.init.vda_version =
0310                 le32_to_cpu(rsp->vda_version);
0311             cfg->data.init.fw_build = rsp->fw_build;
0312 
0313             snprintf(buf, sizeof(buf), "%1.1u.%2.2u",
0314                  (int)LOBYTE(le16_to_cpu(rsp->fw_release)),
0315                  (int)HIBYTE(le16_to_cpu(rsp->fw_release)));
0316 
0317             memcpy(&cfg->data.init.fw_release, buf,
0318                    sizeof(cfg->data.init.fw_release));
0319 
0320             if (LOWORD(LOBYTE(cfg->data.init.fw_build)) == 'A')
0321                 cfg->data.init.fw_version =
0322                     cfg->data.init.fw_build;
0323             else
0324                 cfg->data.init.fw_version =
0325                     cfg->data.init.fw_release;
0326         } else {
0327             esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
0328                          &vi->cmd.cfg.data);
0329         }
0330 
0331         break;
0332 
0333     case VDA_FUNC_CLI:
0334 
0335         vi->cmd.cli.cmd_rsp_len =
0336             le32_to_cpu(rq->func_rsp.cli_rsp.cmd_rsp_len);
0337         break;
0338 
0339     default:
0340 
0341         break;
0342     }
0343 }
0344 
0345 /* Build a flash VDA request. */
0346 void esas2r_build_flash_req(struct esas2r_adapter *a,
0347                 struct esas2r_request *rq,
0348                 u8 sub_func,
0349                 u8 cksum,
0350                 u32 addr,
0351                 u32 length)
0352 {
0353     struct atto_vda_flash_req *vrq = &rq->vrq->flash;
0354 
0355     clear_vda_request(rq);
0356 
0357     rq->vrq->scsi.function = VDA_FUNC_FLASH;
0358 
0359     if (sub_func == VDA_FLASH_BEGINW
0360         || sub_func == VDA_FLASH_WRITE
0361         || sub_func == VDA_FLASH_READ)
0362         vrq->sg_list_offset = (u8)offsetof(struct atto_vda_flash_req,
0363                            data.sge);
0364 
0365     vrq->length = cpu_to_le32(length);
0366     vrq->flash_addr = cpu_to_le32(addr);
0367     vrq->checksum = cksum;
0368     vrq->sub_func = sub_func;
0369 }
0370 
0371 /* Build a VDA management request. */
0372 void esas2r_build_mgt_req(struct esas2r_adapter *a,
0373               struct esas2r_request *rq,
0374               u8 sub_func,
0375               u8 scan_gen,
0376               u16 dev_index,
0377               u32 length,
0378               void *data)
0379 {
0380     struct atto_vda_mgmt_req *vrq = &rq->vrq->mgt;
0381 
0382     clear_vda_request(rq);
0383 
0384     rq->vrq->scsi.function = VDA_FUNC_MGT;
0385 
0386     vrq->mgt_func = sub_func;
0387     vrq->scan_generation = scan_gen;
0388     vrq->dev_index = cpu_to_le16(dev_index);
0389     vrq->length = cpu_to_le32(length);
0390 
0391     if (vrq->length) {
0392         if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
0393             vrq->sg_list_offset = (u8)offsetof(
0394                 struct atto_vda_mgmt_req, sge);
0395 
0396             vrq->sge[0].length = cpu_to_le32(SGE_LAST | length);
0397             vrq->sge[0].address = cpu_to_le64(
0398                 rq->vrq_md->phys_addr +
0399                 sizeof(union atto_vda_req));
0400         } else {
0401             vrq->sg_list_offset = (u8)offsetof(
0402                 struct atto_vda_mgmt_req, prde);
0403 
0404             vrq->prde[0].ctl_len = cpu_to_le32(length);
0405             vrq->prde[0].address = cpu_to_le64(
0406                 rq->vrq_md->phys_addr +
0407                 sizeof(union atto_vda_req));
0408         }
0409     }
0410 
0411     if (data) {
0412         esas2r_nuxi_mgt_data(sub_func, data);
0413 
0414         memcpy(&rq->vda_rsp_data->mgt_data.data.bytes[0], data,
0415                length);
0416     }
0417 }
0418 
0419 /* Build a VDA asyncronous event (AE) request. */
0420 void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq)
0421 {
0422     struct atto_vda_ae_req *vrq = &rq->vrq->ae;
0423 
0424     clear_vda_request(rq);
0425 
0426     rq->vrq->scsi.function = VDA_FUNC_AE;
0427 
0428     vrq->length = cpu_to_le32(sizeof(struct atto_vda_ae_data));
0429 
0430     if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
0431         vrq->sg_list_offset =
0432             (u8)offsetof(struct atto_vda_ae_req, sge);
0433         vrq->sge[0].length = cpu_to_le32(SGE_LAST | vrq->length);
0434         vrq->sge[0].address = cpu_to_le64(
0435             rq->vrq_md->phys_addr +
0436             sizeof(union atto_vda_req));
0437     } else {
0438         vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ae_req,
0439                            prde);
0440         vrq->prde[0].ctl_len = cpu_to_le32(vrq->length);
0441         vrq->prde[0].address = cpu_to_le64(
0442             rq->vrq_md->phys_addr +
0443             sizeof(union atto_vda_req));
0444     }
0445 }
0446 
0447 /* Build a VDA CLI request. */
0448 void esas2r_build_cli_req(struct esas2r_adapter *a,
0449               struct esas2r_request *rq,
0450               u32 length,
0451               u32 cmd_rsp_len)
0452 {
0453     struct atto_vda_cli_req *vrq = &rq->vrq->cli;
0454 
0455     clear_vda_request(rq);
0456 
0457     rq->vrq->scsi.function = VDA_FUNC_CLI;
0458 
0459     vrq->length = cpu_to_le32(length);
0460     vrq->cmd_rsp_len = cpu_to_le32(cmd_rsp_len);
0461     vrq->sg_list_offset = (u8)offsetof(struct atto_vda_cli_req, sge);
0462 }
0463 
0464 /* Build a VDA IOCTL request. */
0465 void esas2r_build_ioctl_req(struct esas2r_adapter *a,
0466                 struct esas2r_request *rq,
0467                 u32 length,
0468                 u8 sub_func)
0469 {
0470     struct atto_vda_ioctl_req *vrq = &rq->vrq->ioctl;
0471 
0472     clear_vda_request(rq);
0473 
0474     rq->vrq->scsi.function = VDA_FUNC_IOCTL;
0475 
0476     vrq->length = cpu_to_le32(length);
0477     vrq->sub_func = sub_func;
0478     vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ioctl_req, sge);
0479 }
0480 
0481 /* Build a VDA configuration request. */
0482 void esas2r_build_cfg_req(struct esas2r_adapter *a,
0483               struct esas2r_request *rq,
0484               u8 sub_func,
0485               u32 length,
0486               void *data)
0487 {
0488     struct atto_vda_cfg_req *vrq = &rq->vrq->cfg;
0489 
0490     clear_vda_request(rq);
0491 
0492     rq->vrq->scsi.function = VDA_FUNC_CFG;
0493 
0494     vrq->sub_func = sub_func;
0495     vrq->length = cpu_to_le32(length);
0496 
0497     if (data) {
0498         esas2r_nuxi_cfg_data(sub_func, data);
0499 
0500         memcpy(&vrq->data, data, length);
0501     }
0502 }
0503 
0504 static void clear_vda_request(struct esas2r_request *rq)
0505 {
0506     u32 handle = rq->vrq->scsi.handle;
0507 
0508     memset(rq->vrq, 0, sizeof(*rq->vrq));
0509 
0510     rq->vrq->scsi.handle = handle;
0511 
0512     rq->req_stat = RS_PENDING;
0513 
0514     /* since the data buffer is separate clear that too */
0515 
0516     memset(rq->data_buf, 0, ESAS2R_DATA_BUF_LEN);
0517 
0518     /*
0519      * Setup next and prev pointer in case the request is not going through
0520      * esas2r_start_request().
0521      */
0522 
0523     INIT_LIST_HEAD(&rq->req_list);
0524 }