/*
* 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 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);
}
Messung V0.5 in Prozent C=97 H=100 G=98
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland