123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- /*
- * linux/drivers/scsi/esas2r/esas2r_vda.c
- * esas2r driver VDA firmware interface functions
- *
- * Copyright (c) 2001-2013 ATTO Technology, Inc.
- * (mailto:linuxdrivers@attotech.com)
- */
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * NO WARRANTY
- * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
- * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
- * solely responsible for determining the appropriateness of using and
- * distributing the Program and assumes all risks associated with its
- * exercise of rights under this Agreement, including but not limited to
- * the risks and costs of program errors, damage to or loss of data,
- * programs or equipment, and unavailability or interruption of operations.
- *
- * DISCLAIMER OF LIABILITY
- * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
- * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- #include "esas2r.h"
- static u8 esas2r_vdaioctl_versions[] = {
- ATTO_VDA_VER_UNSUPPORTED,
- ATTO_VDA_FLASH_VER,
- ATTO_VDA_VER_UNSUPPORTED,
- ATTO_VDA_VER_UNSUPPORTED,
- ATTO_VDA_CLI_VER,
- ATTO_VDA_VER_UNSUPPORTED,
- ATTO_VDA_CFG_VER,
- ATTO_VDA_MGT_VER,
- ATTO_VDA_GSV_VER
- };
- static void clear_vda_request(struct esas2r_request *rq);
- static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
- struct esas2r_request *rq);
- /* Prepare a VDA IOCTL request to be sent to the firmware. */
- bool esas2r_process_vda_ioctl(struct esas2r_adapter *a,
- struct atto_ioctl_vda *vi,
- struct esas2r_request *rq,
- struct esas2r_sg_context *sgc)
- {
- u32 datalen = 0;
- struct atto_vda_sge *firstsg = NULL;
- u8 vercnt = (u8)ARRAY_SIZE(esas2r_vdaioctl_versions);
- vi->status = ATTO_STS_SUCCESS;
- vi->vda_status = RS_PENDING;
- if (vi->function >= vercnt) {
- vi->status = ATTO_STS_INV_FUNC;
- return false;
- }
- if (vi->version > esas2r_vdaioctl_versions[vi->function]) {
- vi->status = ATTO_STS_INV_VERSION;
- return false;
- }
- if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
- vi->status = ATTO_STS_DEGRADED;
- return false;
- }
- if (vi->function != VDA_FUNC_SCSI)
- clear_vda_request(rq);
- rq->vrq->scsi.function = vi->function;
- rq->interrupt_cb = esas2r_complete_vda_ioctl;
- rq->interrupt_cx = vi;
- switch (vi->function) {
- case VDA_FUNC_FLASH:
- if (vi->cmd.flash.sub_func != VDA_FLASH_FREAD
- && vi->cmd.flash.sub_func != VDA_FLASH_FWRITE
- && vi->cmd.flash.sub_func != VDA_FLASH_FINFO) {
- vi->status = ATTO_STS_INV_FUNC;
- return false;
- }
- if (vi->cmd.flash.sub_func != VDA_FLASH_FINFO)
- datalen = vi->data_length;
- rq->vrq->flash.length = cpu_to_le32(datalen);
- rq->vrq->flash.sub_func = vi->cmd.flash.sub_func;
- memcpy(rq->vrq->flash.data.file.file_name,
- vi->cmd.flash.data.file.file_name,
- sizeof(vi->cmd.flash.data.file.file_name));
- firstsg = rq->vrq->flash.data.file.sge;
- break;
- case VDA_FUNC_CLI:
- datalen = vi->data_length;
- rq->vrq->cli.cmd_rsp_len =
- cpu_to_le32(vi->cmd.cli.cmd_rsp_len);
- rq->vrq->cli.length = cpu_to_le32(datalen);
- firstsg = rq->vrq->cli.sge;
- break;
- case VDA_FUNC_MGT:
- {
- u8 *cmdcurr_offset = sgc->cur_offset
- - offsetof(struct atto_ioctl_vda, data)
- + offsetof(struct atto_ioctl_vda, cmd)
- + offsetof(struct atto_ioctl_vda_mgt_cmd,
- data);
- /*
- * build the data payload SGL here first since
- * esas2r_sgc_init() will modify the S/G list offset for the
- * management SGL (which is built below where the data SGL is
- * usually built).
- */
- if (vi->data_length) {
- u32 payldlen = 0;
- if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_HEALTH_REQ
- || vi->cmd.mgt.mgt_func == VDAMGT_DEV_METRICS) {
- rq->vrq->mgt.payld_sglst_offset =
- (u8)offsetof(struct atto_vda_mgmt_req,
- payld_sge);
- payldlen = vi->data_length;
- datalen = vi->cmd.mgt.data_length;
- } else if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_INFO2
- || vi->cmd.mgt.mgt_func ==
- VDAMGT_DEV_INFO2_BYADDR) {
- datalen = vi->data_length;
- cmdcurr_offset = sgc->cur_offset;
- } else {
- vi->status = ATTO_STS_INV_PARAM;
- return false;
- }
- /* Setup the length so building the payload SGL works */
- rq->vrq->mgt.length = cpu_to_le32(datalen);
- if (payldlen) {
- rq->vrq->mgt.payld_length =
- cpu_to_le32(payldlen);
- esas2r_sgc_init(sgc, a, rq,
- rq->vrq->mgt.payld_sge);
- sgc->length = payldlen;
- if (!esas2r_build_sg_list(a, rq, sgc)) {
- vi->status = ATTO_STS_OUT_OF_RSRC;
- return false;
- }
- }
- } else {
- datalen = vi->cmd.mgt.data_length;
- rq->vrq->mgt.length = cpu_to_le32(datalen);
- }
- /*
- * Now that the payload SGL is built, if any, setup to build
- * the management SGL.
- */
- firstsg = rq->vrq->mgt.sge;
- sgc->cur_offset = cmdcurr_offset;
- /* Finish initializing the management request. */
- rq->vrq->mgt.mgt_func = vi->cmd.mgt.mgt_func;
- rq->vrq->mgt.scan_generation = vi->cmd.mgt.scan_generation;
- rq->vrq->mgt.dev_index =
- cpu_to_le32(vi->cmd.mgt.dev_index);
- esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);
- break;
- }
- case VDA_FUNC_CFG:
- if (vi->data_length
- || vi->cmd.cfg.data_length == 0) {
- vi->status = ATTO_STS_INV_PARAM;
- return false;
- }
- if (vi->cmd.cfg.cfg_func == VDA_CFG_INIT) {
- vi->status = ATTO_STS_INV_FUNC;
- return false;
- }
- rq->vrq->cfg.sub_func = vi->cmd.cfg.cfg_func;
- rq->vrq->cfg.length = cpu_to_le32(vi->cmd.cfg.data_length);
- if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
- memcpy(&rq->vrq->cfg.data,
- &vi->cmd.cfg.data,
- vi->cmd.cfg.data_length);
- esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
- &rq->vrq->cfg.data);
- } else {
- vi->status = ATTO_STS_INV_FUNC;
- return false;
- }
- break;
- case VDA_FUNC_GSV:
- vi->cmd.gsv.rsp_len = vercnt;
- memcpy(vi->cmd.gsv.version_info, esas2r_vdaioctl_versions,
- vercnt);
- vi->vda_status = RS_SUCCESS;
- break;
- default:
- vi->status = ATTO_STS_INV_FUNC;
- return false;
- }
- if (datalen) {
- esas2r_sgc_init(sgc, a, rq, firstsg);
- sgc->length = datalen;
- if (!esas2r_build_sg_list(a, rq, sgc)) {
- vi->status = ATTO_STS_OUT_OF_RSRC;
- return false;
- }
- }
- esas2r_start_request(a, rq);
- return true;
- }
- static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
- struct esas2r_request *rq)
- {
- struct atto_ioctl_vda *vi = (struct atto_ioctl_vda *)rq->interrupt_cx;
- vi->vda_status = rq->req_stat;
- switch (vi->function) {
- case VDA_FUNC_FLASH:
- if (vi->cmd.flash.sub_func == VDA_FLASH_FINFO
- || vi->cmd.flash.sub_func == VDA_FLASH_FREAD)
- vi->cmd.flash.data.file.file_size =
- le32_to_cpu(rq->func_rsp.flash_rsp.file_size);
- break;
- case VDA_FUNC_MGT:
- vi->cmd.mgt.scan_generation =
- rq->func_rsp.mgt_rsp.scan_generation;
- vi->cmd.mgt.dev_index = le16_to_cpu(
- rq->func_rsp.mgt_rsp.dev_index);
- if (vi->data_length == 0)
- vi->cmd.mgt.data_length =
- le32_to_cpu(rq->func_rsp.mgt_rsp.length);
- esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);
- break;
- case VDA_FUNC_CFG:
- if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
- struct atto_ioctl_vda_cfg_cmd *cfg = &vi->cmd.cfg;
- struct atto_vda_cfg_rsp *rsp = &rq->func_rsp.cfg_rsp;
- char buf[sizeof(cfg->data.init.fw_release) + 1];
- cfg->data_length =
- cpu_to_le32(sizeof(struct atto_vda_cfg_init));
- cfg->data.init.vda_version =
- le32_to_cpu(rsp->vda_version);
- cfg->data.init.fw_build = rsp->fw_build;
- snprintf(buf, sizeof(buf), "%1.1u.%2.2u",
- (int)LOBYTE(le16_to_cpu(rsp->fw_release)),
- (int)HIBYTE(le16_to_cpu(rsp->fw_release)));
- memcpy(&cfg->data.init.fw_release, buf,
- sizeof(cfg->data.init.fw_release));
- if (LOWORD(LOBYTE(cfg->data.init.fw_build)) == 'A')
- cfg->data.init.fw_version =
- cfg->data.init.fw_build;
- else
- cfg->data.init.fw_version =
- cfg->data.init.fw_release;
- } else {
- esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
- &vi->cmd.cfg.data);
- }
- break;
- case VDA_FUNC_CLI:
- vi->cmd.cli.cmd_rsp_len =
- le32_to_cpu(rq->func_rsp.cli_rsp.cmd_rsp_len);
- break;
- default:
- break;
- }
- }
- /* Build a flash VDA request. */
- void esas2r_build_flash_req(struct esas2r_adapter *a,
- struct esas2r_request *rq,
- u8 sub_func,
- u8 cksum,
- u32 addr,
- u32 length)
- {
- struct atto_vda_flash_req *vrq = &rq->vrq->flash;
- clear_vda_request(rq);
- rq->vrq->scsi.function = VDA_FUNC_FLASH;
- if (sub_func == VDA_FLASH_BEGINW
- || sub_func == VDA_FLASH_WRITE
- || sub_func == VDA_FLASH_READ)
- vrq->sg_list_offset = (u8)offsetof(struct atto_vda_flash_req,
- data.sge);
- vrq->length = cpu_to_le32(length);
- vrq->flash_addr = cpu_to_le32(addr);
- vrq->checksum = cksum;
- vrq->sub_func = sub_func;
- }
- /* Build a VDA management request. */
- void esas2r_build_mgt_req(struct esas2r_adapter *a,
- struct esas2r_request *rq,
- u8 sub_func,
- u8 scan_gen,
- u16 dev_index,
- u32 length,
- void *data)
- {
- struct atto_vda_mgmt_req *vrq = &rq->vrq->mgt;
- clear_vda_request(rq);
- rq->vrq->scsi.function = VDA_FUNC_MGT;
- vrq->mgt_func = sub_func;
- vrq->scan_generation = scan_gen;
- vrq->dev_index = cpu_to_le16(dev_index);
- vrq->length = cpu_to_le32(length);
- if (vrq->length) {
- if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
- vrq->sg_list_offset = (u8)offsetof(
- struct atto_vda_mgmt_req, sge);
- vrq->sge[0].length = cpu_to_le32(SGE_LAST | length);
- vrq->sge[0].address = cpu_to_le64(
- rq->vrq_md->phys_addr +
- sizeof(union atto_vda_req));
- } else {
- vrq->sg_list_offset = (u8)offsetof(
- struct atto_vda_mgmt_req, prde);
- vrq->prde[0].ctl_len = cpu_to_le32(length);
- vrq->prde[0].address = cpu_to_le64(
- rq->vrq_md->phys_addr +
- sizeof(union atto_vda_req));
- }
- }
- if (data) {
- esas2r_nuxi_mgt_data(sub_func, data);
- memcpy(&rq->vda_rsp_data->mgt_data.data.bytes[0], data,
- length);
- }
- }
- /* Build a VDA asyncronous event (AE) request. */
- void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq)
- {
- struct atto_vda_ae_req *vrq = &rq->vrq->ae;
- clear_vda_request(rq);
- rq->vrq->scsi.function = VDA_FUNC_AE;
- vrq->length = cpu_to_le32(sizeof(struct atto_vda_ae_data));
- if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
- vrq->sg_list_offset =
- (u8)offsetof(struct atto_vda_ae_req, sge);
- vrq->sge[0].length = cpu_to_le32(SGE_LAST | vrq->length);
- vrq->sge[0].address = cpu_to_le64(
- rq->vrq_md->phys_addr +
- sizeof(union atto_vda_req));
- } else {
- vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ae_req,
- prde);
- vrq->prde[0].ctl_len = cpu_to_le32(vrq->length);
- vrq->prde[0].address = cpu_to_le64(
- rq->vrq_md->phys_addr +
- sizeof(union atto_vda_req));
- }
- }
- /* Build a VDA CLI request. */
- void esas2r_build_cli_req(struct esas2r_adapter *a,
- struct esas2r_request *rq,
- u32 length,
- u32 cmd_rsp_len)
- {
- struct atto_vda_cli_req *vrq = &rq->vrq->cli;
- clear_vda_request(rq);
- rq->vrq->scsi.function = VDA_FUNC_CLI;
- vrq->length = cpu_to_le32(length);
- vrq->cmd_rsp_len = cpu_to_le32(cmd_rsp_len);
- vrq->sg_list_offset = (u8)offsetof(struct atto_vda_cli_req, sge);
- }
- /* Build a VDA IOCTL request. */
- void esas2r_build_ioctl_req(struct esas2r_adapter *a,
- struct esas2r_request *rq,
- u32 length,
- u8 sub_func)
- {
- struct atto_vda_ioctl_req *vrq = &rq->vrq->ioctl;
- clear_vda_request(rq);
- rq->vrq->scsi.function = VDA_FUNC_IOCTL;
- vrq->length = cpu_to_le32(length);
- vrq->sub_func = sub_func;
- vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ioctl_req, sge);
- }
- /* Build a VDA configuration request. */
- void esas2r_build_cfg_req(struct esas2r_adapter *a,
- struct esas2r_request *rq,
- u8 sub_func,
- u32 length,
- void *data)
- {
- struct atto_vda_cfg_req *vrq = &rq->vrq->cfg;
- clear_vda_request(rq);
- rq->vrq->scsi.function = VDA_FUNC_CFG;
- vrq->sub_func = sub_func;
- vrq->length = cpu_to_le32(length);
- if (data) {
- esas2r_nuxi_cfg_data(sub_func, data);
- memcpy(&vrq->data, data, length);
- }
- }
- static void clear_vda_request(struct esas2r_request *rq)
- {
- u32 handle = rq->vrq->scsi.handle;
- memset(rq->vrq, 0, sizeof(*rq->vrq));
- rq->vrq->scsi.handle = handle;
- rq->req_stat = RS_PENDING;
- /* since the data buffer is separate clear that too */
- memset(rq->data_buf, 0, ESAS2R_DATA_BUF_LEN);
- /*
- * Setup next and prev pointer in case the request is not going through
- * esas2r_start_request().
- */
- INIT_LIST_HEAD(&rq->req_list);
- }
|