/* Copyright  2010 - 2013 UNISYS CORPORATION */
/* All rights reserved. */
#ifndef __IOCHANNEL_H__
#define __IOCHANNEL_H__

/*
* Everything needed for IOPart-GuestPart communication is define in
* this file.  Note: Everything is OS-independent because this file is
* used by Windows, Linux and possible EFI drivers.  */


/*
* Communication flow between the IOPart and GuestPart uses the channel headers
* channel state.  The following states are currently being used:
*       UNINIT(All Zeroes), CHANNEL_ATTACHING, CHANNEL_ATTACHED, CHANNEL_OPENED
*
* additional states will be used later.  No locking is needed to switch between
* states due to the following rules:
*
*      1.  IOPart is only the only partition allowed to change from UNIT
*      2.  IOPart is only the only partition allowed to change from
*		CHANNEL_ATTACHING
*      3.  GuestPart is only the only partition allowed to change from
*		CHANNEL_ATTACHED
*
* The state changes are the following: IOPart sees the channel is in UNINIT,
*        UNINIT -> CHANNEL_ATTACHING (performed only by IOPart)
*        CHANNEL_ATTACHING -> CHANNEL_ATTACHED (performed only by IOPart)
*        CHANNEL_ATTACHED -> CHANNEL_OPENED (performed only by GuestPart)
*/

#include "commontypes.h"
#include "vmcallinterface.h"

#define _ULTRA_CONTROLVM_CHANNEL_INLINE_
#include <linux/dma-direction.h>
#include "controlvmchannel.h"
#include "vbuschannel.h"
#undef _ULTRA_CONTROLVM_CHANNEL_INLINE_
#include "channel.h"

/*
 * CHANNEL Guids
 */

#include "channel_guid.h"

#define ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE
#define ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE
#define ULTRA_VSWITCH_CHANNEL_PROTOCOL_SIGNATURE \
	ULTRA_CHANNEL_PROTOCOL_SIGNATURE

/* Must increment these whenever you insert or delete fields within this channel
* struct.  Also increment whenever you change the meaning of fields within this
* channel struct so as to break pre-existing software.  Note that you can
* usually add fields to the END of the channel struct withOUT needing to
* increment this. */
#define ULTRA_VHBA_CHANNEL_PROTOCOL_VERSIONID 2
#define ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID 2
#define ULTRA_VSWITCH_CHANNEL_PROTOCOL_VERSIONID 1

#define ULTRA_VHBA_CHANNEL_OK_CLIENT(pChannel, logCtx)			\
	(ULTRA_check_channel_client(pChannel, UltraVhbaChannelProtocolGuid, \
				    "vhba", MIN_IO_CHANNEL_SIZE,	\
				    ULTRA_VHBA_CHANNEL_PROTOCOL_VERSIONID, \
				    ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE, \
				    __FILE__, __LINE__, logCtx))
#define ULTRA_VHBA_CHANNEL_OK_SERVER(actualBytes, logCtx)		\
	(ULTRA_check_channel_server(UltraVhbaChannelProtocolGuid,	\
				    "vhba", MIN_IO_CHANNEL_SIZE, actualBytes, \
				    __FILE__, __LINE__, logCtx))
#define ULTRA_VNIC_CHANNEL_OK_CLIENT(pChannel, logCtx)			\
	(ULTRA_check_channel_client(pChannel, UltraVnicChannelProtocolGuid, \
				    "vnic", MIN_IO_CHANNEL_SIZE,	\
				    ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID, \
				    ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE, \
				    __FILE__, __LINE__, logCtx))
#define ULTRA_VNIC_CHANNEL_OK_SERVER(actualBytes, logCtx)		\
	(ULTRA_check_channel_server(UltraVnicChannelProtocolGuid,	\
				    "vnic", MIN_IO_CHANNEL_SIZE, actualBytes, \
				    __FILE__, __LINE__, logCtx))
#define ULTRA_VSWITCH_CHANNEL_OK_CLIENT(pChannel, logCtx)		\
	(ULTRA_check_channel_client(pChannel, UltraVswitchChannelProtocolGuid, \
				    "vswitch", MIN_IO_CHANNEL_SIZE,	\
				    ULTRA_VSWITCH_CHANNEL_PROTOCOL_VERSIONID, \
				    ULTRA_VSWITCH_CHANNEL_PROTOCOL_SIGNATURE, \
				    __FILE__, __LINE__, logCtx))
#define ULTRA_VSWITCH_CHANNEL_OK_SERVER(actualBytes, logCtx)          \
	(ULTRA_check_channel_server(UltraVswitchChannelProtocolGuid,	\
				    "vswitch", MIN_IO_CHANNEL_SIZE,	\
				    actualBytes,		    \
				    __FILE__, __LINE__, logCtx))
/*
* Everything necessary to handle SCSI & NIC traffic between Guest Partition and
* IO Partition is defined below.  */


/*
* Defines and enums.
*/

#define MINNUM(a, b) (((a) < (b)) ? (a) : (b))
#define MAXNUM(a, b) (((a) > (b)) ? (a) : (b))

/* these define the two queues per data channel between iopart and
 * ioguestparts */
#define IOCHAN_TO_IOPART 0 /* used by ioguestpart to 'insert' signals to
			    * iopart */
#define IOCHAN_FROM_GUESTPART 0 /* used by iopart to 'remove' signals from
				 * ioguestpart - same queue as previous queue */

#define IOCHAN_TO_GUESTPART 1 /* used by iopart to 'insert' signals to
			       * ioguestpart */
#define IOCHAN_FROM_IOPART 1 /* used by ioguestpart to 'remove' signals from
			      * iopart - same queue as previous queue */

/* these define the two queues per control channel between controlpart and "its"
 * guests, which includes the iopart  */
#define CTRLCHAN_TO_CTRLGUESTPART 0 /* used by ctrlguestpart to 'insert' signals
				     * to ctrlpart */
#define CTLRCHAN_FROM_CTRLPART 0 /* used by ctrlpart to 'remove' signals from
				  * ctrlquestpart - same queue as previous
				  * queue */

#define CTRLCHAN_TO_CTRLPART 1 /* used by ctrlpart to 'insert' signals to
				* ctrlguestpart */
#define CTRLCHAN_FROM_CTRLGUESTPART 1 /* used by ctrguestpart to 'remove'
				       * signals from ctrlpart - same queue as
				       * previous queue */

/* these define the Event & Ack queues per control channel Events are generated
* by CTRLGUESTPART and sent to CTRLPART; Acks are generated by CTRLPART and sent
* to CTRLGUESTPART. */
#define CTRLCHAN_EVENT_TO_CTRLPART 2 /* used by ctrlguestpart to 'insert' Events
				      * to ctrlpart */
#define CTRLCHAN_EVENT_FROM_CTRLGUESTPART 2 /* used by ctrlpart to 'remove'
					     * Events from ctrlguestpart */

#define CTRLCHAN_ACK_TO_CTRLGUESTPART 3	/* used by ctrlpart to 'insert' Acks to
					 * ctrlguestpart */
#define CTRLCHAN_ACK_FROM_CTRLPART 3 /* used by ctrlguestpart to 'remove' Events
				      * from ctrlpart */

/* size of cdb - i.e., scsi cmnd */
#define MAX_CMND_SIZE 16

#define MAX_SENSE_SIZE 64

#define MAX_PHYS_INFO 64

/* Because GuestToGuestCopy is limited to 4KiB segments, and we have limited the
* Emulex Driver to 256 scatter list segments via the lpfc_sg_seg_cnt parameter
* to 256, the maximum I/O size is limited to 256 * 4 KiB = 1 MB */
#define MAX_IO_SIZE   (1024*1024)	/* 1 MB */

/* NOTE 1: lpfc defines its support for segments in
* #define LPFC_SG_SEG_CNT 64
*
* NOTE 2: In Linux, frags array in skb is currently allocated to be
* MAX_SKB_FRAGS size, which is 18 which is smaller than MAX_PHYS_INFO for
* now.  */

#ifndef MAX_SERIAL_NUM
#define MAX_SERIAL_NUM		32
#endif				/* MAX_SERIAL_NUM */

#define MAX_SCSI_BUSES		1
#define MAX_SCSI_TARGETS	8
#define MAX_SCSI_LUNS		16
#define MAX_SCSI_FROM_HOST	0xFFFFFFFF	/* Indicator to use Physical HBA
						 * SCSI Host value */

/* various types of network packets that can be sent in cmdrsp */
typedef enum { NET_RCV_POST = 0,	/* submit buffer to hold receiving
					 * incoming packet */
	/* virtnic -> uisnic */
	NET_RCV,		/* incoming packet received */
	/* uisnic -> virtpci */
	NET_XMIT,		/* for outgoing net packets      */
	/* virtnic -> uisnic */
	NET_XMIT_DONE,		/* outgoing packet xmitted */
	/* uisnic -> virtpci */
	NET_RCV_ENBDIS,		/* enable/disable packet reception */
	/* virtnic -> uisnic */
	NET_RCV_ENBDIS_ACK,	/* acknowledge enable/disable packet
				 * reception */
	/* uisnic -> virtnic */
	NET_RCV_PROMISC,	/* enable/disable promiscuous mode */
	/* virtnic -> uisnic */
	NET_CONNECT_STATUS,	/* indicate the loss or restoration of a network
				 * connection */
	/* uisnic -> virtnic */
	NET_MACADDR,		/* indicates the client has requested to update
				 * its MAC addr */
	NET_MACADDR_ACK,	/* Mac addres  */

} NET_TYPES;

#define		ETH_HEADER_SIZE 14	/* size of ethernet header */

#define		ETH_MIN_DATA_SIZE 46	/* minimum eth data size */
#define		ETH_MIN_PACKET_SIZE (ETH_HEADER_SIZE + ETH_MIN_DATA_SIZE)

#define     ETH_DEF_DATA_SIZE 1500	/* default data size */
#define     ETH_DEF_PACKET_SIZE (ETH_HEADER_SIZE + ETH_DEF_DATA_SIZE)

#define		ETH_MAX_MTU 16384	/* maximum data size */

#ifndef MAX_MACADDR_LEN
#define MAX_MACADDR_LEN 6	/* number of bytes in MAC address */
#endif				/* MAX_MACADDR_LEN */

#define ETH_IS_LOCALLY_ADMINISTERED(Address) \
	(((U8 *) (Address))[0] & ((U8) 0x02))
#define NIC_VENDOR_ID 0x0008000B

/* various types of scsi task mgmt commands  */
typedef enum { TASK_MGMT_ABORT_TASK =
	    1, TASK_MGMT_BUS_RESET, TASK_MGMT_LUN_RESET,
	    TASK_MGMT_TARGET_RESET,
} TASK_MGMT_TYPES;

/* various types of vdisk mgmt commands  */
typedef enum { VDISK_MGMT_ACQUIRE = 1, VDISK_MGMT_RELEASE,
} VDISK_MGMT_TYPES;

/* this is used in the vdest field  */
#define VDEST_ALL 0xFFFF

#define MIN_NUMSIGNALS 64
#define MAX_NUMSIGNALS 4096

/* MAX_NET_RCV_BUF specifies the number of rcv buffers that are created by each
* guest's virtnic and posted to uisnic.  Uisnic, for each channel, keeps the rcv
* buffers posted and uses them to receive data on behalf of the guest's virtnic.
* NOTE: the num_rcv_bufs is configurable for each VNIC. So the following is
* simply an upperlimit on what each VNIC can provide.  Setting it to half of the
* NUMSIGNALS to prevent queue full deadlocks */
#define MAX_NET_RCV_BUFS (MIN_NUMSIGNALS / 2)

/*
 * structs with pragma pack  */


/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */
/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */

#pragma pack(push, 1)

struct guest_phys_info {
	U64 address;
	U64 length;
};

#define GPI_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(struct guest_phys_info))

struct uisscsi_dest {
	U32 channel;		/* channel == bus number */
	U32 id;			/* id == target number */
	U32 lun;		/* lun == logical unit number */
};

struct vhba_wwnn {
	U32 wwnn1;
	U32 wwnn2;
};

/* WARNING: Values stired in this structure must contain maximum counts (not
 * maximum values). */
struct vhba_config_max {	/* 20 bytes */
	U32 max_channel;	/* maximum channel for devices attached to this
				 * bus */
	U32 max_id;		/* maximum SCSI ID for devices attached to this
				 * bus */
	U32 max_lun;		/* maximum SCSI LUN for devices attached to this
				 * bus */
	U32 cmd_per_lun;	/* maximum number of outstanding commands per
				 * lun that are allowed at one time */
	U32 max_io_size;	/* maximum io size for devices attached to this
				 * bus */
	/* max io size is often determined by the resource of the hba. e.g */
	/* max scatter gather list length * page size / sector size */
};

struct uiscmdrsp_scsi {
	void *scsicmd;		/* the handle to the cmd that was received -
				 * send it back as is in the rsp packet.  */
	U8 cmnd[MAX_CMND_SIZE];	/* the cdb for the command */
	U32 bufflen;		/* length of data to be transferred out or in */
	U16 guest_phys_entries;	/* Number of entries in scatter-gather (sg)
				 * list */
	struct guest_phys_info gpi_list[MAX_PHYS_INFO];	/* physical address
							 * information for each
							 * fragment */
	enum dma_data_direction  data_dir; /* direction of the data, if any */
	struct uisscsi_dest vdest;	/* identifies the virtual hba, id,
					 * channel, lun to which cmd was sent */

	    /* the following fields are needed to queue the rsp back to cmd
	     * originator */
	int linuxstat;		/* the original Linux status - for use by linux
				 * vdisk code */
	U8 scsistat;		/* the scsi status */
	U8 addlstat;		/* non-scsi status - covers cases like timeout
				 * needed by windows guests */
#define ADDL_RESET		1
#define ADDL_TIMEOUT		2
#define ADDL_INTERNAL_ERROR	3
#define ADDL_SEL_TIMEOUT	4
#define ADDL_CMD_TIMEOUT	5
#define ADDL_BAD_TARGET		6
#define ADDL_RETRY		7

	/* the following fields are need to determine the result of command */
	 U8 sensebuf[MAX_SENSE_SIZE];	/* sense info in case cmd failed; */
	/* it holds the sense_data struct; */
	/* see that struct for details. */
	void *vdisk; /* contains pointer to the vdisk so that we can clean up
		      * when the IO completes. */
	int no_disk_result;	/* used to return no disk inquiry result */
	/* when no_disk_result is set to 1,  */
	/* scsi.scsistat is SAM_STAT_GOOD */
	/* scsi.addlstat is 0 */
	/* scsi.linuxstat is SAM_STAT_GOOD */
	/* That is, there is NO error. */
};

/*
* Defines to support sending correct inquiry result when no disk is
* configured.  */

/* From SCSI SPC2 -
 *
 * If the target is not capable of supporting a device on this logical unit, the
 * device server shall set this field to 7Fh (PERIPHERAL QUALIFIER set to 011b
 * and PERIPHERAL DEVICE TYPE set to 1Fh).
 *
 *The device server is capable of supporting the specified peripheral device
 *type on this logical unit. However, the physical device is not currently
 *connected to this logical unit.
 */

#define DEV_NOT_PRESENT 0x7f	/* old name - compatibility */
#define DEV_NOT_CAPABLE 0x7f	/* peripheral qualifier of 0x3  */
    /* peripheral type of 0x1f */
    /* specifies no device but target present */

#define DEV_DISK_CAPABLE_NOT_PRESENT 0x20 /* peripheral qualifier of 0x1 */
    /* peripheral type of 0 - disk */
    /* specifies device capable, but not present */

#define DEV_PROC_CAPABLE_NOT_PRESENT 0x23 /* peripheral qualifier of 0x1 */
    /* peripheral type of 3 - processor */
    /* specifies device capable, but not present */

#define DEV_HISUPPORT 0x10;	/* HiSup = 1; shows support for report luns */
    /* must be returned for lun 0. */

/* NOTE: Linux code assumes inquiry contains 36 bytes. Without checking length
* in buf[4] some linux code accesses bytes beyond 5 to retrieve vendor, product
* & revision.  Yikes! So let us always send back 36 bytes, the minimum for
* inquiry result. */
#define NO_DISK_INQUIRY_RESULT_LEN 36

#define MIN_INQUIRY_RESULT_LEN 5 /* we need at least 5 bytes minimum for inquiry
				  * result */

/* SCSI device version for no disk inquiry result */
#define SCSI_SPC2_VER 4		/* indicates SCSI SPC2 (SPC3 is 5) */

/* Windows and Linux want different things for a non-existent lun. So, we'll let
 * caller pass in the peripheral qualifier and type.
 * NOTE:[4] SCSI returns (n-4); so we return length-1-4 or length-5. */

#define SET_NO_DISK_INQUIRY_RESULT(buf, len, lun, lun0notpresent, notpresent) \
	do {								\
		MEMSET(buf, 0,						\
		       MINNUM(len,					\
			      (unsigned int) NO_DISK_INQUIRY_RESULT_LEN)); \
		buf[2] = (U8) SCSI_SPC2_VER;				\
		if (lun == 0) {						\
			buf[0] = (U8) lun0notpresent;			\
			buf[3] = (U8) DEV_HISUPPORT;			\
		} else							\
			buf[0] = (U8) notpresent;			\
		buf[4] = (U8) (						\
			MINNUM(len,					\
			       (unsigned int) NO_DISK_INQUIRY_RESULT_LEN) - 5);	\
		if (len >= NO_DISK_INQUIRY_RESULT_LEN) {		\
			buf[8] = 'D';					\
			buf[9] = 'E';					\
			buf[10] = 'L';					\
			buf[11] = 'L';					\
			buf[16] = 'P';					\
			buf[17] = 'S';					\
			buf[18] = 'E';					\
			buf[19] = 'U';					\
			buf[20] = 'D';					\
			buf[21] = 'O';					\
			buf[22] = ' ';					\
			buf[23] = 'D';					\
			buf[24] = 'E';					\
			buf[25] = 'V';					\
			buf[26] = 'I';					\
			buf[27] = 'C';					\
			buf[28] = 'E';					\
			buf[30] = ' ';					\
			buf[31] = '.';					\
		}							\
	} while (0)


/*
* Struct & Defines to support sense information.
*/


/* The following struct is returned in sensebuf field in uiscmdrsp_scsi.  It is
* initialized in exactly the manner that is recommended in Windows (hence the
* odd values).
* When set, these fields will have the following values:
* ErrorCode = 0x70		indicates current error
* Valid = 1			indicates sense info is valid
* SenseKey			contains sense key as defined by SCSI specs.
* AdditionalSenseCode		contains sense key as defined by SCSI specs.
* AdditionalSenseCodeQualifier	contains qualifier to sense code as defined by
*				scsi docs.
* AdditionalSenseLength		contains will be sizeof(sense_data)-8=10.
*/
struct sense_data {
	U8 ErrorCode:7;
	U8 Valid:1;
	U8 SegmentNumber;
	U8 SenseKey:4;
	U8 Reserved:1;
	U8 IncorrectLength:1;
	U8 EndOfMedia:1;
	U8 FileMark:1;
	U8 Information[4];
	U8 AdditionalSenseLength;
	U8 CommandSpecificInformation[4];
	U8 AdditionalSenseCode;
	U8 AdditionalSenseCodeQualifier;
	U8 FieldReplaceableUnitCode;
	U8 SenseKeySpecific[3];
};

/* some SCSI ADSENSE codes */
#ifndef SCSI_ADSENSE_LUN_NOT_READY
#define SCSI_ADSENSE_LUN_NOT_READY 0x04
#endif	/*  */
#ifndef SCSI_ADSENSE_ILLEGAL_COMMAND
#define SCSI_ADSENSE_ILLEGAL_COMMAND 0x20
#endif	/*  */
#ifndef SCSI_ADSENSE_ILLEGAL_BLOCK
#endif	/*  */
#ifndef SCSI_ADSENSE_ILLEGAL_BLOCK
#define SCSI_ADSENSE_ILLEGAL_BLOCK  0x21
#endif	/*  */
#ifndef SCSI_ADSENSE_INVALID_CDB
#define SCSI_ADSENSE_INVALID_CDB    0x24
#endif	/*  */
#ifndef SCSI_ADSENSE_INVALID_LUN
#define SCSI_ADSENSE_INVALID_LUN    0x25
#endif	/*  */
#ifndef SCSI_ADWRITE_PROTECT
#define SCSI_ADWRITE_PROTECT        0x27
#endif	/*  */
#ifndef SCSI_ADSENSE_MEDIUM_CHANGED
#define SCSI_ADSENSE_MEDIUM_CHANGED 0x28
#endif	/*  */
#ifndef SCSI_ADSENSE_BUS_RESET
#define SCSI_ADSENSE_BUS_RESET      0x29
#endif	/*  */
#ifndef SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
#define SCSI_ADSENSE_NO_MEDIA_IN_DEVICE 0x3a
#endif	/*  */

struct net_pkt_xmt {
	int len;	/* full length of data in the packet */
	int num_frags;	/* number of fragments in frags containing data */
	struct phys_info frags[MAX_PHYS_INFO];	/* physical page information for
						 * each fragment */
	char ethhdr[ETH_HEADER_SIZE];	/* the ethernet header  */
	struct {

		    /* these are needed for csum at uisnic end */
		U8 valid;	/* 1 = rest of this struct is valid - else
				 * ignore */
		U8 hrawoffv;	/* 1 = hwrafoff is valid */
		U8 nhrawoffv;	/* 1 = nhwrafoff is valid */
		U16 protocol;	/* specifies packet protocol */
		U32 csum;	/* value used to set skb->csum at IOPart */
		U32 hrawoff;	/* value used to set skb->h.raw at IOPart */
		/* hrawoff points to the start of the TRANSPORT LAYER HEADER */
		U32 nhrawoff;	/* value used to set skb->nh.raw at IOPart */
		/* nhrawoff points to the start of the NETWORK LAYER HEADER */
	} lincsum;

	    /* **** NOTE ****
	     * The full packet is described in frags but the ethernet header is
	     * separately kept in ethhdr so that uisnic doesn't have "MAP" the
	     * guest memory to get to the header. uisnic needs ethhdr to
	     * determine how to route the packet.
	     */
};

struct net_pkt_xmtdone {
	U32 xmt_done_result;	/* result of NET_XMIT */
#define XMIT_SUCCESS 0
#define XMIT_FAILED 1
};

/* RCVPOST_BUF_SIZe must be at most page_size(4096) - cache_line_size (64) The
* reason is because dev_skb_alloc which is used to generate RCV_POST skbs in
* virtnic requires that there is "overhead" in the buffer, and pads 16 bytes.  I
* prefer to use 1 full cache line size for "overhead" so that transfers are
* better.  IOVM requires that a buffer be represented by 1 phys_info structure
* which can only cover page_size. */
#define RCVPOST_BUF_SIZE 4032
#define MAX_NET_RCV_CHAIN \
	((ETH_MAX_MTU+ETH_HEADER_SIZE + RCVPOST_BUF_SIZE-1) / RCVPOST_BUF_SIZE)

struct net_pkt_rcvpost {
	    /* rcv buf size must be large enough to include ethernet data len +
	    * ethernet header len - we are choosing 2K because it is guaranteed
	    * to be describable */
	    struct phys_info frag;	/* physical page information for the
					 * single fragment 2K rcv buf */
	    U64 UniqueNum;		/* This is used to make sure that
					 * receive posts are returned to  */
	    /* the Adapter which sent them origonally. */
};

struct net_pkt_rcv {

	/* the number of receive buffers that can be chained  */
	/* is based on max mtu and size of each rcv buf */
	U32 rcv_done_len;	/* length of received data */
	U8 numrcvbufs;		/* number of receive buffers that contain the */
	/* incoming data; guest end MUST chain these together. */
	void *rcvbuf[MAX_NET_RCV_CHAIN];	/* the list of receive buffers
						 * that must be chained; */
	/* each entry is a receive buffer provided by NET_RCV_POST. */
	/* NOTE: first rcvbuf in the chain will also be provided in net.buf. */
	U64 UniqueNum;
	U32 RcvsDroppedDelta;
};

struct net_pkt_enbdis {
	void *context;
	U16 enable;		/* 1 = enable, 0 = disable */
};

struct net_pkt_macaddr {
	void *context;
	U8 macaddr[MAX_MACADDR_LEN];	/* 6 bytes */
};

/* cmd rsp packet used for VNIC network traffic  */
struct uiscmdrsp_net {
	NET_TYPES type;
	void *buf;
	union {
		struct net_pkt_xmt xmt;	/* used for NET_XMIT */
		struct net_pkt_xmtdone xmtdone;	/* used for NET_XMIT_DONE */
		struct net_pkt_rcvpost rcvpost;	/* used for NET_RCV_POST */
		struct net_pkt_rcv rcv;	/* used for NET_RCV */
		struct net_pkt_enbdis enbdis;	/* used for NET_RCV_ENBDIS, */
		/* NET_RCV_ENBDIS_ACK,  */
		/* NET_RCV_PROMSIC, */
		/* and NET_CONNECT_STATUS */
		struct net_pkt_macaddr macaddr;
	};
};

struct uiscmdrsp_scsitaskmgmt {
	TASK_MGMT_TYPES tasktype;

	    /* the type of task */
	struct uisscsi_dest vdest;

	    /* the vdisk for which this task mgmt is generated */
	void *scsicmd;

	    /* This is some handle that the guest has saved off for its own use.
	    * Its value is preserved by iopart & returned as is in the task mgmt
	    * rsp. */
	void *notify;

	    /* For linux guests, this is a pointer to wait_queue_head that a
	    * thread is waiting on to see if the taskmgmt command has completed.
	    * For windows guests, this is a pointer to a location that a waiting
	    * thread is testing to see if the taskmgmt command has completed.
	    * When the rsp is received by guest, the thread receiving the
	    * response uses this to notify the the thread waiting for taskmgmt
	    * command completion.  Its value is preserved by iopart & returned
	    * as is in the task mgmt rsp. */
	void *notifyresult;

	    /* this is a handle to location in guest where the result of the
	    * taskmgmt command (result field) is to saved off when the response
	    * is handled.  Its value is preserved by iopart & returned as is in
	    * the task mgmt rsp. */
	char result;

	    /* result of taskmgmt command - set by IOPart - values are: */
#define TASK_MGMT_FAILED  0
#define TASK_MGMT_SUCCESS 1
};

/* The following is used by uissd to send disk add/remove notifications to
 * Guest */
/* Note that the vHba pointer is not used by the Client/Guest side. */
struct uiscmdrsp_disknotify {
	U8 add;		/* 0-remove, 1-add */
	void *vHba;		/* Pointer to vhba_info for channel info to
				 * route msg */
	U32 channel, id, lun;	/* SCSI Path of Disk to added or removed */
};

/* The following is used by virthba/vSCSI to send the Acquire/Release commands
* to the IOVM.  */
struct uiscmdrsp_vdiskmgmt {
	VDISK_MGMT_TYPES vdisktype;

	    /* the type of task */
	struct uisscsi_dest vdest;

	    /* the vdisk for which this task mgmt is generated */
	void *scsicmd;

	    /* This is some handle that the guest has saved off for its own use.
	    * Its value is preserved by iopart & returned as is in the task mgmt
	    * rsp. */
	void *notify;

	    /* For linux guests, this is a pointer to wait_queue_head that a
	    * thread is waiting on to see if the taskmgmt command has completed.
	    * For windows guests, this is a pointer to a location that a waiting
	    * thread is testing to see if the taskmgmt command has completed.
	    * When the rsp is received by guest, the thread receiving the
	    * response uses this to notify the the thread waiting for taskmgmt
	    * command completion.  Its value is preserved by iopart & returned
	    * as is in the task mgmt rsp. */
	void *notifyresult;

	    /* this is a handle to location in guest where the result of the
	    * taskmgmt command (result field) is to saved off when the response
	    * is handled.  Its value is preserved by iopart & returned as is in
	    * the task mgmt rsp. */
	char result;

	    /* result of taskmgmt command - set by IOPart - values are: */
#define VDISK_MGMT_FAILED  0
#define VDISK_MGMT_SUCCESS 1
};

/* keeping cmd & rsp info in one structure for now cmd rsp packet for scsi */
struct uiscmdrsp {
	char cmdtype;

	    /* describes what type of information is in the struct */
#define CMD_SCSI_TYPE		1
#define CMD_NET_TYPE		2
#define CMD_SCSITASKMGMT_TYPE	3
#define CMD_NOTIFYGUEST_TYPE	4
#define CMD_VDISKMGMT_TYPE	5
	union {
		struct uiscmdrsp_scsi scsi;
		struct uiscmdrsp_net net;
		struct uiscmdrsp_scsitaskmgmt scsitaskmgmt;
		struct uiscmdrsp_disknotify disknotify;
		struct uiscmdrsp_vdiskmgmt vdiskmgmt;
	};
	void *private_data;	/* used to send the response when the cmd is
				 * done (scsi & scsittaskmgmt).  */
	struct uiscmdrsp *next;	/* General Purpose Queue Link */
	struct uiscmdrsp *activeQ_next;	/* Used to track active commands */
	struct uiscmdrsp *activeQ_prev;	/* Used to track active commands  */
};

/* This is just the header of the IO channel.  It is assumed that directly after
* this header there is a large region of memory which contains the command and
* response queues as specified in cmdQ and rspQ SIGNAL_QUEUE_HEADERS. */
typedef struct _ULTRA_IO_CHANNEL_PROTOCOL {
	CHANNEL_HEADER ChannelHeader;
	SIGNAL_QUEUE_HEADER cmdQ;
	SIGNAL_QUEUE_HEADER rspQ;
	union {
		struct {
			struct vhba_wwnn wwnn;	/* 8 bytes */
			struct vhba_config_max max;	/* 20 bytes */
		} vhba;		/* 28 */
		struct {
			U8 macaddr[MAX_MACADDR_LEN];	/* 6 bytes */
			U32 num_rcv_bufs;	/* 4 */
			U32 mtu;	/* 4 */
			GUID zoneGuid;	/* 16 */
		} vnic;		/* total     30 */
	};

#define MAX_CLIENTSTRING_LEN 1024
	 U8 clientString[MAX_CLIENTSTRING_LEN];	/* NULL terminated - so holds
						 * max - 1 bytes */
} ULTRA_IO_CHANNEL_PROTOCOL;

#pragma pack(pop)
/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */

/* define offsets to members of struct uiscmdrsp */
#define OFFSET_CMDTYPE OFFSETOF(struct uiscmdrsp, cmdtype)
#define OFFSET_SCSI OFFSETOF(struct uiscmdrsp, scsi)
#define OFFSET_NET OFFSETOF(struct uiscmdrsp, net)
#define OFFSET_SCSITASKMGMT OFFSETOF(struct uiscmdrsp, scsitaskmgmt)
#define OFFSET_NEXT OFFSETOF(struct uiscmdrsp, next)

/* define offsets to members of struct uiscmdrsp_net */
#define OFFSET_TYPE OFFSETOF(struct uiscmdrsp_net, type)
#define OFFSET_BUF OFFSETOF(struct uiscmdrsp_net, buf)
#define OFFSET_XMT OFFSETOF(struct uiscmdrsp_net, xmt)
#define OFFSET_XMT_DONE_RESULT OFFSETOF(struct uiscmdrsp_net, xmtdone)
#define OFFSET_RCVPOST OFFSETOF(struct uiscmdrsp_net, rcvpost)
#define OFFSET_RCV_DONE_LEN OFFSETOF(struct uiscmdrsp_net, rcv)
#define OFFSET_ENBDIS OFFSETOF(struct uiscmdrsp_net, enbdis)

/* define offsets to members of struct net_pkt_rcvpost */
#define OFFSET_TOTALLEN OFFSETOF(struct net_pkt_rcvpost, totallen)
#define	OFFSET_FRAG OFFSETOF(struct net_pkt_rcvpost, frag)

/*
* INLINE functions for initializing and accessing I/O data channels
*/


#define NUMSIGNALS(x, q) (((ULTRA_IO_CHANNEL_PROTOCOL *)(x))->q.MaxSignalSlots)
#define SIZEOF_PROTOCOL (COVER(sizeof(ULTRA_IO_CHANNEL_PROTOCOL), 64))
#define SIZEOF_CMDRSP (COVER(sizeof(struct uiscmdrsp), 64))

#define IO_CHANNEL_SIZE(x) COVER(SIZEOF_PROTOCOL + \
				 (NUMSIGNALS(x, cmdQ) + \
				  NUMSIGNALS(x, rspQ)) * SIZEOF_CMDRSP, 4096)
#define MIN_IO_CHANNEL_SIZE COVER(SIZEOF_PROTOCOL + \
				  2 * MIN_NUMSIGNALS * SIZEOF_CMDRSP, 4096)
#ifdef __GNUC__
/* These defines should only ever be used in service partitons */
/* because they rely on the size of uiscmdrsp */
#define QSLOTSFROMBYTES(bytes) (((bytes-SIZEOF_PROTOCOL)/2)/SIZEOF_CMDRSP)
#define QSIZEFROMBYTES(bytes) (QSLOTSFROMBYTES(bytes)*SIZEOF_CMDRSP)
#define SignalQInit(x)						\
	do {							\
		x->cmdQ.Size = QSIZEFROMBYTES(x->ChannelHeader.Size);	\
		x->cmdQ.oSignalBase = SIZEOF_PROTOCOL -			\
			OFFSETOF(ULTRA_IO_CHANNEL_PROTOCOL, cmdQ);	\
		x->cmdQ.SignalSize = SIZEOF_CMDRSP;			\
		x->cmdQ.MaxSignalSlots =				\
			QSLOTSFROMBYTES(x->ChannelHeader.Size);		\
		x->cmdQ.MaxSignals = x->cmdQ.MaxSignalSlots - 1;	\
		x->rspQ.Size = QSIZEFROMBYTES(x->ChannelHeader.Size);	\
		x->rspQ.oSignalBase =					\
			(SIZEOF_PROTOCOL + x->cmdQ.Size) -		\
			OFFSETOF(ULTRA_IO_CHANNEL_PROTOCOL, rspQ);	\
		x->rspQ.SignalSize = SIZEOF_CMDRSP;			\
		x->rspQ.MaxSignalSlots =				\
			QSLOTSFROMBYTES(x->ChannelHeader.Size);		\
		x->rspQ.MaxSignals = x->rspQ.MaxSignalSlots - 1;	\
		x->ChannelHeader.oChannelSpace =			\
			OFFSETOF(ULTRA_IO_CHANNEL_PROTOCOL, cmdQ);	\
	} while (0)

#define INIT_CLIENTSTRING(chan, type, clientStr, clientStrLen)	\
	do {								\
		if (clientStr) {					\
			chan->ChannelHeader.oClientString =		\
				OFFSETOF(type, clientString);		\
			MEMCPY(chan->clientString, clientStr,		\
			       MINNUM(clientStrLen,			\
				      (U32) (MAX_CLIENTSTRING_LEN - 1))); \
			chan->clientString[MINNUM(clientStrLen,		\
						  (U32) (MAX_CLIENTSTRING_LEN \
							 - 1))]		\
				= '\0';					\
		}							\
		else							\
			if (clientStrLen > 0)				\
				return 0;				\
	} while (0)


#define ULTRA_IO_CHANNEL_SERVER_READY(x, chanId, logCtx) \
	ULTRA_CHANNEL_SERVER_TRANSITION(x, chanId, SrvState, CHANNELSRV_READY, \
					logCtx);

#define ULTRA_IO_CHANNEL_SERVER_NOTREADY(x, chanId, logCtx)	\
	ULTRA_CHANNEL_SERVER_TRANSITION(x, chanId, SrvState, \
					CHANNELSRV_UNINITIALIZED, logCtx);

static inline int ULTRA_VHBA_init_channel(ULTRA_IO_CHANNEL_PROTOCOL *x,
					      struct vhba_wwnn *wwnn,
					      struct vhba_config_max *max,
					      unsigned char *clientStr,
					      U32 clientStrLen, U64 bytes)  {
	MEMSET(x, 0, sizeof(ULTRA_IO_CHANNEL_PROTOCOL));
	x->ChannelHeader.VersionId = ULTRA_VHBA_CHANNEL_PROTOCOL_VERSIONID;
	x->ChannelHeader.Signature = ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE;
	x->ChannelHeader.SrvState = CHANNELSRV_UNINITIALIZED;
	x->ChannelHeader.HeaderSize = sizeof(x->ChannelHeader);
	x->ChannelHeader.Size = COVER(bytes, 4096);
	x->ChannelHeader.Type = UltraVhbaChannelProtocolGuid;
	x->ChannelHeader.ZoneGuid = Guid0;
	x->vhba.wwnn = *wwnn;
	x->vhba.max = *max;
	INIT_CLIENTSTRING(x, ULTRA_IO_CHANNEL_PROTOCOL, clientStr,
			  clientStrLen);
	SignalQInit(x);
	if ((x->cmdQ.MaxSignalSlots > MAX_NUMSIGNALS) ||
	     (x->rspQ.MaxSignalSlots > MAX_NUMSIGNALS)) {
		return 0;
	}
	if ((x->cmdQ.MaxSignalSlots < MIN_NUMSIGNALS) ||
	     (x->rspQ.MaxSignalSlots < MIN_NUMSIGNALS)) {
		return 0;
	}
	return 1;
}

static inline void ULTRA_VHBA_set_max(ULTRA_IO_CHANNEL_PROTOCOL *x,
				      struct vhba_config_max *max)  {
	x->vhba.max = *max;
}

static inline int ULTRA_VNIC_init_channel(ULTRA_IO_CHANNEL_PROTOCOL *x,
						 unsigned char *macaddr,
						 U32 num_rcv_bufs, U32 mtu,
						 GUID zoneGuid,
						 unsigned char *clientStr,
						 U32 clientStrLen,
						 U64 bytes)  {
	MEMSET(x, 0, sizeof(ULTRA_IO_CHANNEL_PROTOCOL));
	x->ChannelHeader.VersionId = ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID;
	x->ChannelHeader.Signature = ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE;
	x->ChannelHeader.SrvState = CHANNELSRV_UNINITIALIZED;
	x->ChannelHeader.HeaderSize = sizeof(x->ChannelHeader);
	x->ChannelHeader.Size = COVER(bytes, 4096);
	x->ChannelHeader.Type = UltraVnicChannelProtocolGuid;
	x->ChannelHeader.ZoneGuid = Guid0;
	MEMCPY(x->vnic.macaddr, macaddr, MAX_MACADDR_LEN);
	x->vnic.num_rcv_bufs = num_rcv_bufs;
	x->vnic.mtu = mtu;
	x->vnic.zoneGuid = zoneGuid;
	INIT_CLIENTSTRING(x, ULTRA_IO_CHANNEL_PROTOCOL, clientStr,
			   clientStrLen);
	SignalQInit(x);
	if ((x->cmdQ.MaxSignalSlots > MAX_NUMSIGNALS) ||
	     (x->rspQ.MaxSignalSlots > MAX_NUMSIGNALS)) {
		return 0;
	}
	if ((x->cmdQ.MaxSignalSlots < MIN_NUMSIGNALS) ||
	     (x->rspQ.MaxSignalSlots < MIN_NUMSIGNALS)) {
		return 0;
	}
	return 1;
}

#endif	/* __GNUC__ */

/*
* INLINE function for expanding a guest's pfn-off-size into multiple 4K page
* pfn-off-size entires.
*/


/* we deal with 4K page sizes when we it comes to passing page information
 * between */
/* Guest and IOPartition. */
#define PI_PAGE_SIZE  0x1000
#define PI_PAGE_MASK  0x0FFF
#define PI_PAGE_SHIFT 12

/* returns next non-zero index on success or zero on failure (i.e. out of
 * room)
 */
static INLINE  U16
add_physinfo_entries(U32 inp_pfn,	/* input - specifies the pfn to be used
					 * to add entries */
		     U16 inp_off,	/* input - specifies the off to be used
					 * to add entries */
		     U32 inp_len,	/* input - specifies the len to be used
					 * to add entries */
		     U16 index,		/* input - index in array at which new
					 * entries are added */
		     U16 max_pi_arr_entries,	/* input - specifies the maximum
						 * entries pi_arr can hold */
		     struct phys_info pi_arr[]) /* input & output - array to
						  * which entries are added */
{
	U32 len;
	U16 i, firstlen;

	firstlen = PI_PAGE_SIZE - inp_off;
	if (inp_len <= firstlen) {

		/* the input entry spans only one page - add as is */
		if (index >= max_pi_arr_entries)
			return 0;
		pi_arr[index].pi_pfn = inp_pfn;
		pi_arr[index].pi_off = (U16) inp_off;
		pi_arr[index].pi_len = (U16) inp_len;
		    return index + 1;
	}

	    /* this entry spans multiple pages */
	    for (len = inp_len, i = 0; len;
		 len -= pi_arr[index + i].pi_len, i++) {
		if (index + i >= max_pi_arr_entries)
			return 0;
		pi_arr[index + i].pi_pfn = inp_pfn + i;
		if (i == 0) {
			pi_arr[index].pi_off = inp_off;
			pi_arr[index].pi_len = firstlen;
		}

		else {
			pi_arr[index + i].pi_off = 0;
			pi_arr[index + i].pi_len =
			    (U16) MINNUM(len, (U32) PI_PAGE_SIZE);
		}

	}
	return index + i;
}

#endif				/* __IOCHANNEL_H__ */
