[SCSI] libfc: add fc_fill_reply_hdr() and fc_fill_hdr()

Add functions to fill in an FC header given a request header.
These reduces code lines in fc_lport and fc_rport and works
without an exchange/sequence assigned.

fc_fill_reply_hdr() fills a header for a final reply frame.

fc_fill_hdr() which is similar but allows specifying the
f_ctl parameter.

Add defines for F_CTL values FC_FCTL_REQ and FC_FCTL_RESP.
These can be used for most request and response sequences.

v2 of patch adds a line to copy the frame encapsulation
info from the received frame.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index 39f4b6a..6a48c28 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -23,6 +23,7 @@
 #include <linux/crc32.h>
 
 #include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
 
 #include "fc_libfc.h"
 
@@ -132,3 +133,80 @@
 	}
 	return copy_len;
 }
+
+/**
+ * fc_fill_hdr() -  fill FC header fields based on request
+ * @fp: reply frame containing header to be filled in
+ * @in_fp: request frame containing header to use in filling in reply
+ * @r_ctl: R_CTL value for header
+ * @f_ctl: F_CTL value for header, with 0 pad
+ * @seq_cnt: sequence count for the header, ignored if frame has a sequence
+ * @parm_offset: parameter / offset value
+ */
+void fc_fill_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
+		 enum fc_rctl r_ctl, u32 f_ctl, u16 seq_cnt, u32 parm_offset)
+{
+	struct fc_frame_header *fh;
+	struct fc_frame_header *in_fh;
+	struct fc_seq *sp;
+	u32 fill;
+
+	fh = __fc_frame_header_get(fp);
+	in_fh = __fc_frame_header_get(in_fp);
+
+	if (f_ctl & FC_FC_END_SEQ) {
+		fill = -fr_len(fp) & 3;
+		if (fill) {
+			/* TODO, this may be a problem with fragmented skb */
+			memset(skb_put(fp_skb(fp), fill), 0, fill);
+			f_ctl |= fill;
+		}
+		fr_eof(fp) = FC_EOF_T;
+	} else {
+		WARN_ON(fr_len(fp) % 4 != 0);	/* no pad to non last frame */
+		fr_eof(fp) = FC_EOF_N;
+	}
+
+	fh->fh_r_ctl = r_ctl;
+	memcpy(fh->fh_d_id, in_fh->fh_s_id, sizeof(fh->fh_d_id));
+	memcpy(fh->fh_s_id, in_fh->fh_d_id, sizeof(fh->fh_s_id));
+	fh->fh_type = in_fh->fh_type;
+	hton24(fh->fh_f_ctl, f_ctl);
+	fh->fh_ox_id = in_fh->fh_ox_id;
+	fh->fh_rx_id = in_fh->fh_rx_id;
+	fh->fh_cs_ctl = 0;
+	fh->fh_df_ctl = 0;
+	fh->fh_parm_offset = htonl(parm_offset);
+
+	sp = fr_seq(in_fp);
+	if (sp) {
+		fr_seq(fp) = sp;
+		fh->fh_seq_id = sp->id;
+		seq_cnt = sp->cnt;
+	} else {
+		fh->fh_seq_id = 0;
+	}
+	fh->fh_seq_cnt = ntohs(seq_cnt);
+	fr_sof(fp) = seq_cnt ? FC_SOF_N3 : FC_SOF_I3;
+	fr_encaps(fp) = fr_encaps(in_fp);
+}
+EXPORT_SYMBOL(fc_fill_hdr);
+
+/**
+ * fc_fill_reply_hdr() -  fill FC reply header fields based on request
+ * @fp: reply frame containing header to be filled in
+ * @in_fp: request frame containing header to use in filling in reply
+ * @r_ctl: R_CTL value for reply
+ * @parm_offset: parameter / offset value
+ */
+void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
+		       enum fc_rctl r_ctl, u32 parm_offset)
+{
+	struct fc_seq *sp;
+
+	sp = fr_seq(in_fp);
+	if (sp)
+		fr_seq(fp) = fr_dev(in_fp)->tt.seq_start_next(sp);
+	fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset);
+}
+EXPORT_SYMBOL(fc_fill_reply_hdr);