Files
linux-kernel-test/drivers/scsi/esas2r/esas2r_vda.c
Bradley Grove 9588d24e36 [SCSI] esas2r: Directly call kernel functions for atomic bit operations
Previously the code embedded the kernel's test_bit/clear_bit
functions in wrappers that accepted u32 parameters.  The
wrapper cast these parameters to longs before passing them
to the kernel's bit functions.   This did not work properly
on platforms with 64-bit longs.

Signed-off-by: Bradley Grove <bgrove@attotech.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
2013-10-25 09:58:59 +01:00

525 lines
14 KiB
C

/*
* 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), "%1d.%02d",
(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);
}