mirror of https://gitee.com/openkylin/linux.git
141 lines
3.0 KiB
C
141 lines
3.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright(c) 2008 Intel Corporation. All rights reserved.
|
|
*
|
|
* Maintained at www.Open-FCoE.org
|
|
*/
|
|
|
|
/*
|
|
* Provide interface to send ELS/CT FC frames
|
|
*/
|
|
|
|
#include <linux/export.h>
|
|
#include <asm/unaligned.h>
|
|
#include <scsi/fc/fc_gs.h>
|
|
#include <scsi/fc/fc_ns.h>
|
|
#include <scsi/fc/fc_els.h>
|
|
#include <scsi/libfc.h>
|
|
#include <scsi/fc_encode.h>
|
|
#include "fc_libfc.h"
|
|
|
|
/**
|
|
* fc_elsct_send() - Send an ELS or CT frame
|
|
* @lport: The local port to send the frame on
|
|
* @did: The destination ID for the frame
|
|
* @fp: The frame to be sent
|
|
* @op: The operational code
|
|
* @resp: The callback routine when the response is received
|
|
* @arg: The argument to pass to the response callback routine
|
|
* @timer_msec: The timeout period for the frame (in msecs)
|
|
*/
|
|
struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did,
|
|
struct fc_frame *fp, unsigned int op,
|
|
void (*resp)(struct fc_seq *,
|
|
struct fc_frame *,
|
|
void *),
|
|
void *arg, u32 timer_msec)
|
|
{
|
|
enum fc_rctl r_ctl;
|
|
enum fc_fh_type fh_type;
|
|
int rc;
|
|
|
|
/* ELS requests */
|
|
if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS))
|
|
rc = fc_els_fill(lport, did, fp, op, &r_ctl, &fh_type);
|
|
else {
|
|
/* CT requests */
|
|
rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type, &did);
|
|
}
|
|
|
|
if (rc) {
|
|
fc_frame_free(fp);
|
|
return NULL;
|
|
}
|
|
|
|
fc_fill_fc_hdr(fp, r_ctl, did, lport->port_id, fh_type,
|
|
FC_FCTL_REQ, 0);
|
|
|
|
return fc_exch_seq_send(lport, fp, resp, NULL, arg, timer_msec);
|
|
}
|
|
EXPORT_SYMBOL(fc_elsct_send);
|
|
|
|
/**
|
|
* fc_elsct_init() - Initialize the ELS/CT layer
|
|
* @lport: The local port to initialize the ELS/CT layer for
|
|
*/
|
|
int fc_elsct_init(struct fc_lport *lport)
|
|
{
|
|
if (!lport->tt.elsct_send)
|
|
lport->tt.elsct_send = fc_elsct_send;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(fc_elsct_init);
|
|
|
|
/**
|
|
* fc_els_resp_type() - Return a string describing the ELS response
|
|
* @fp: The frame pointer or possible error code
|
|
*/
|
|
const char *fc_els_resp_type(struct fc_frame *fp)
|
|
{
|
|
const char *msg;
|
|
struct fc_frame_header *fh;
|
|
struct fc_ct_hdr *ct;
|
|
|
|
if (IS_ERR(fp)) {
|
|
switch (-PTR_ERR(fp)) {
|
|
case FC_NO_ERR:
|
|
msg = "response no error";
|
|
break;
|
|
case FC_EX_TIMEOUT:
|
|
msg = "response timeout";
|
|
break;
|
|
case FC_EX_CLOSED:
|
|
msg = "response closed";
|
|
break;
|
|
default:
|
|
msg = "response unknown error";
|
|
break;
|
|
}
|
|
} else {
|
|
fh = fc_frame_header_get(fp);
|
|
switch (fh->fh_type) {
|
|
case FC_TYPE_ELS:
|
|
switch (fc_frame_payload_op(fp)) {
|
|
case ELS_LS_ACC:
|
|
msg = "accept";
|
|
break;
|
|
case ELS_LS_RJT:
|
|
msg = "reject";
|
|
break;
|
|
default:
|
|
msg = "response unknown ELS";
|
|
break;
|
|
}
|
|
break;
|
|
case FC_TYPE_CT:
|
|
ct = fc_frame_payload_get(fp, sizeof(*ct));
|
|
if (ct) {
|
|
switch (ntohs(ct->ct_cmd)) {
|
|
case FC_FS_ACC:
|
|
msg = "CT accept";
|
|
break;
|
|
case FC_FS_RJT:
|
|
msg = "CT reject";
|
|
break;
|
|
default:
|
|
msg = "response unknown CT";
|
|
break;
|
|
}
|
|
} else {
|
|
msg = "short CT response";
|
|
}
|
|
break;
|
|
default:
|
|
msg = "response not ELS or CT";
|
|
break;
|
|
}
|
|
}
|
|
return msg;
|
|
}
|