/*
 * $QNXLicenseC: 
 * Copyright 2008, QNX Software Systems.  
 *  
 * Licensed under the Apache License, Version 2.0 (the "License"). You  
 * may not reproduce, modify or distribute this software except in  
 * compliance with the License. You may obtain a copy of the License  
 * at: http://www.apache.org/licenses/LICENSE-2.0  
 *  
 * Unless required by applicable law or agreed to in writing, software  
 * distributed under the License is distributed on an "AS IS" basis,  
 * WITHOUT WARRANTIES OF ANY KIND, either express or implied. 
 * 
 * This file may contain contributions from others, either as  
 * contributors under the License or as licensors under other terms.   
 * Please review this entire file for other proprietary rights or license  
 * notices, as well as the QNX Development Suite License Guide at  
 * http://licensing.qnx.com/license-guide/ for other information. 
 * $ 
 */

#ifndef SIM_H_INCLUDED
#define SIM_H_INCLUDED

#include <pthread.h>
#include <sys/queue.h>
#include <sys/resmgr.h>
#include <sys/syspage.h>

#include <cam.h>

// Useful TAILQ manifests possibly not in sys/queue.h
#ifndef TAILQ_EMPTY
	#define	TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#endif
#ifndef TAILQ_FIRST
	#define	TAILQ_FIRST(head) ((head)->tqh_first)
#endif
#ifndef TAILQ_LAST
	#define	TAILQ_LAST(head) ((head)->tqh_last)
#endif
#ifndef TAILQ_NEXT
	#define	TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#endif
#ifndef TAILQ_PREV
	#define TAILQ_PREV(elm, field) ((elm)->field.tqe_prev)
#endif

typedef struct _sim_hba_stats		SIM_HBA_STATS;
typedef struct _sim_hba_extension	SIM_HBA_EXT;
typedef struct _sim_hba				SIM_HBA;

// NOTES:
//
//  An initiator may not mix the use of tagged and untagged queuing for I/O
//  processes to a logical unit, except during a contingent allegiance or
//  extended contingent allegiance condition when only untagged initial
//  connections are allowed.
//
//  If the disconnect privilege is not granted in the IDENTIFY message for
//  a tagged I/O process, the target will return BUSY status.
//
//  A series of linked commands constitue a single I/O process.  These
//  commands are assigned the queue tag established in the initial
//  connection.
//
//  The cam_status is | with CAM_SIM_QFRZN.

// state defines
#define SIM_CCB_READY			0x0000		// CCB is not active
#define SIM_CCB_NEXUS			0x0001		// CCB has been dequeued/started
#define SIM_CCB_ARB				0x0002		// CCB is currently arbitrating
#define SIM_CCB_SEL				0x0004		// CCB is currently selecting
#define SIM_CCB_CMD				0x0008		// CCB is in command in phase
#define SIM_CCB_MSGI			0x0010		// CCB is in message in phase
#define SIM_CCB_MSGO			0x0020		// CCB is in message out phase
#define SIM_CCB_DIN				0x0040		// CCB is in data in phase
#define SIM_CCB_DOUT			0x0080		// CCB is in data out phase
#define SIM_CCB_STAT			0x0100		// CCB is in status phase
#define SIM_CCB_DISC			0x0200		// CCB is disconnected
#define SIM_CCB_DONE			0x0400		// CCB has completed
#define SIM_CCB_ABORT			0x8000		// CCB is to be aborted
#define SIM_CCB_RESET			0x4000		// CCB is to be aborted
#define SIM_CCB_MASK			0x3fff		// CCB state mask
#define SIM_CCB_QUERY			0xFFFF		// Query ccb state

typedef struct sim_lun_queue {
	_uint32					frzn_cnt;
	TAILQ_HEAD(sim_ccb_tq,ccb_scsiio)			clist;	// linked list of ccbs
	TAILQ_HEAD(sim_active_ccb_tq,ccb_scsiio)	alist;	// linked list of active ccbs (nexus)
	_uint32					ltag_id;					// last tag id given out
	_uint32					tag;						// count of active tagged ccbs
	_uint32					nontag;						// count of active non tagged ccbs
	_uint32					max_tagged;					// maximum tagged per lun
} SIM_LUN_QUEUE;

typedef struct sim_target_queue {
	SIM_LUN_QUEUE		*lque;				// array of lun queues
} SIM_TARGET_QUEUE;

typedef struct sim_queue {
	pthread_mutex_t		mutex;				// sim queue mutex
	timer_t				timerid;			// timer id
	_uint32				ntargs;				// number of targets
	_uint32				nluns;				// number of logical units
	_uint32				max_tagged;			// maximum tagged per lun
	_uint32				max_non_tagged;		// maximum non tagged per lun
	_uint32				mactive;			// maximum active ccbs
	_uint32				actcnt;				// number of active ccbs
	_uint32				qcnt;				// number of ccbs queued
	_uint32				tindx;				// used for round-robin scheduling
	_uint32				lindx;				// used for round-robin scheduling
	_uint32				timeout;
	SIM_HBA 			*hba;				// pointer to hba structure
	SIM_TARGET_QUEUE	*tque;				// variable length array of targets
	struct qtime_entry	*systime;			// fast access to current time
} SIM_QUEUE;

// This structure must be the first entry in the private data
typedef struct sim_que_data {
	TAILQ_ENTRY(ccb_scsiio)	clink;			// tailq entry for prev/next ccb
	_uint16					state;			// SCSI state of ccb
	_uint8					rsvd;
	_uint8					tag_id;			// tag id for this ccb
	_uint32					timeout;		// timeout
} SIMQ_DATA;

#define SIMQ_TIMER_DISABLE	0x00
#define SIMQ_TIMER_ENABLE	0x01

#define SIM_TIME_DEFAULT	0x02			// default to a 2 second timeout

#define SIM_TAG_INVALID		0xff

#define SIM_ENQUEUE			0x01
#define SIM_INTERRUPT		0x02
#define SIM_TIMER			0x03
#define SIM_DRVR_TIMER		0x04
#define SIM_DMA_INTERRUPT	0x05
#define SIM_RESET			0x06

#define SIM_PRIORITY		21

#define HBA_FLAG_NORESET			0x0001
#define HBA_FLAG_NOPARITY			0x0002
#define HBA_FLAG_TERMLO				0x0004
#define HBA_FLAG_TERMHI				0x0008
#define HBA_FLAG_SCAM				0x0010

#define HBA_FLAG_ACCESS8			0x0020
#define HBA_FLAG_ACCESS16			0x0040
#define HBA_FLAG_ACCESS32			0x0080

#define HBA_STATE_FAILURE			0x01
#define HBA_STATE_INITIALIZING		0x02
#define HBA_STATE_INITIALIZED		0x04
#define HBA_STATE_RESETING			0x08
#define HBA_STATE_EXPECT_INTR		0x10

struct _sim_hba_stats {
	_uint32				stat_irqs;
	_uint32				stat_timeouts;
	_uint32				stat_selections;
	_uint32				stat_disconnects;
	_uint32				stat_reselections;
	_uint32				stat_bus_resets;
	_uint8				rsvd[36];
};

// sim extentions must include the following members
struct _sim_hba_extension {
	SIM_HBA				*hba;
	_uint32				eflags;
};

#define ISADEVICE		0x00000001
#define EISADEVICE		0x00000002
#define PCIDEVICE		0x00000004
#define PCMCIADEVICE	0x00000008
#define PNPISADEVICE	0x00000010
#define MCADEVICE		0x00000020

typedef struct _sim_device_id
{
	_uint32	BusID;		/* bus type, 0 is undefined */
	_uint32	DevID;		/* physical device ID, 0xffffffff is undefined */
	_uint32	CompID;		/* compatibility ID */
	_uint32	SerialNum;	/* device instance or serial number, 0 is undefined */
	_uint32	LogicalID;	/* Logical device ID for Plug and Play ISA, Class code for */
						/* PCI, 0xffffffff is undefined */
	_uint32	Flags;		/* Bit 0: device has been intialized (1) */
						/* Bit 1: device is enabled (1) */
						/* Bit 2: device config has been locked (1) */
						/* Bit 3: device has temporary config resources assigned (1) */
						/* Bit 4: device is re-configurable (1) */
						/* Bit 31: device id is a QNX defined ID */
} SIM_DEVICE_ID;

typedef union _sim_bus_access
{
	struct	PCIAccess {
		_uint8 	BusNumber;			/* Bus number 0-255 */
		_uint8 	DevFuncNumber;		/* Device (bits 7:3) and function (in bits 2:0) IDs */
		_uint16 PCIreserved;			
	} PCIAccess;
	struct	EISAAccess {
		_uint8 	SlotNumber;			/* Card slot number */
		_uint8	FunctionNumber;		/* (E)ISA sub-function number on multi function card */
		_uint16	EISAreserved;		
	} EISAAccess;
	struct	PnPAccess {
		_uint8 	CSN;				/* Card slot number determined during ISA isolation */
		_uint8	LogicalDevNumber;	/* Logical device number */
		_uint16	Read_data_port;		/* Read data I/O port determined during ISA isolation */
	} PnPAccess;
	struct	PCMCIAAccess {
		_uint16 	LogicalSocket;		/* Card logical socket # */
		_uint16 	PCMCIAReserved1;	/* Reserved */
	} PCMCIAAccess;
} SIM_BUS_ACCESS;

#define	SIM_MAX_MEM_REGISTERS		6
#define	SIM_MAX_IO_PORTS			6
#define	SIM_MAX_IRQS				6
#define	SIM_MAX_DMA_CHANNELS		6

typedef struct _sim_config_info {
	SIM_DEVICE_ID	Device_ID;							/* Device ID information */
	SIM_BUS_ACCESS	BusAccess;							/* Info to allow config. access */

	_uint32			NumMemWindows;						/* Num memory windows */
	_uint64			MemBase[SIM_MAX_MEM_REGISTERS];  	/* Memory window base */
	_uint32			MemLength[SIM_MAX_MEM_REGISTERS];	/* Memory window length */
	_uint32			MemAttrib[SIM_MAX_MEM_REGISTERS];	/* Memory window Attrib */

	_uint64			IOPort_Base[SIM_MAX_IO_PORTS];		/* I/O port base */
	_uint32			IOPort_Length[SIM_MAX_IO_PORTS];	/* I/O port length */
	_uint32			NumIOPorts;							/* Num IO ports */

	_uint32			NumIRQs;							/* Num IRQ info */
	_uint32			IRQRegisters[SIM_MAX_IRQS];			/* IRQ list */
	_uint32			IRQAttrib[SIM_MAX_IRQS];			/* IRQ Attrib list */

	_uint32			NumDMAs;							/* Num DMA channels */
	_uint32			DMALst[SIM_MAX_DMA_CHANNELS];	 	/* DMA list */
	_uint32			DMAAttrib[SIM_MAX_DMA_CHANNELS];	/* DMA Attrib list */

	char			Description[40];					/* Device specific desc */
	_uint8			Reserved1[16];						/* Reserved */
} SIM_CONFIG_INFO;

/* IO attributes */
#define ATTR_IO_EXCL				0x00
#define ATTR_IO_SHARED				0x01

/* Memory Range Attributes */
#define ATTR_MEM_RANGE_DECODE		0x01	/* Use memory range length for decoding */
#define ATTR_MEM_8_BIT				0x00	/* 8-bit memory */
#define ATTR_MEM_16_BIT				0x02	/* 16-bit memory */
#define ATTR_MEM_8_16_BIT			0x04	/* 8 and 16-bit memory */
#define ATTR_MEM_32_BIT				0x00	/* 32-bit memory */
#define ATTR_MEM_16_32_BIT			0x08	/* 16 and 32-bit memory */
#define ATTR_MEM_ROM				0x00	/* Read Only (ROM) */
#define ATTR_MEM_RAM				0x20	/* Read/Write (RAM) */
#define ATTR_MEM_SHARED				0x40	/* Shared Memory */

/* IRQ Attributes */
#define ATTR_IRQ_EDGE_TRIGGERED		0x00	/* Edge Triggered */
#define ATTR_IRQ_LEVEL_TRIGGERED	0x01	/* Level Triggered */
#define ATTR_IRQ_LEVEL_LOW			0x00	/* Level Low */
#define ATTR_IRQ_LEVEL_HIGH			0x02	/* Level High */
#define ATTR_IRQ_SHARED				0x04	/* Shared */

#define ATTR_IRQ_DEFAULT			0x80	/* IRQ default */

/* DMA Attributes */
#define ATTR_DMA_8_BIT				0x0000	/* 8-bit transfers */
#define ATTR_DMA_8_16_BIT			0x0001	/* 8 and 16-bit transfers */
#define ATTR_DMA_16_BIT				0x0002	/* 16-bit transfers */
#define ATTR_DMA_32_BIT				0x0003	/* 32-bit transfers */
#define ATTR_DMA_BUS_MASTER			0x0004	/* logical device is a bus master */
#define ATTR_DMA_COUNT_BYTE_MODE	0x0008	/* DMA may execute in count by byte mode */
#define ATTR_DMA_COUNT_WORD_MODE	0x0010	/* DMA may execute in count by word mode */
/* DMA channel speed support */
#define ATTR_DMA_ISA				0x0000	/* ISA compatible timing */
#define ATTR_DMA_TYPE_A				0x0020	/* type A timing */
#define ATTR_DMA_TYPE_B				0x0040	/* type B timing */
#define ATTR_DMA_TYPE_F				0x0060	/* type F timing */
#define ATTR_DMA_SHARED				0x0100	/* Shared DMA */
#define ATTR_DMA_TYPE_C				0x0200	/* C type timing supported */

#define ATTR_DMA_DEFAULT			0x8000	/* DMA default */

struct _sim_hba {
	TAILQ_ENTRY( _sim_hba )	hlink;
	SIM_HBA_EXT			*ext;
	SIM_QUEUE			*simq;
	CAM_ENTRY			*cam_funcs;
	SIM_CONFIG_INFO		cfg;
	int					verbosity;
	_uint32				hflags;				// see HBA_FLAGS_...
	_uint32				state;				// see HBA_STATE_...
	int					coid;
	int					chid;
	int					iid;
	pthread_t			tid;				// id of event handling thread
	void				*dhdl;				// device handle (pci)
	path_id_t			pathid;
	int					priority;

	_uint32				rsvd[7];

	_uint8				stride;				// I/O stride
	_uint8				decode;				// I/O decode

	_uint8				scsiid;
	_uint8				seltimo;			// selection timeout;

	_uint8				ntargets;
	_uint8				nluns;

	_uint8				speed;
	_uint8				rsvd1[3];

	_uint16				nsgelm;				// number of scatter gather elements

	_uint16				tag_mask;
	_uint16				disc_mask;

	_uint16				sync_mask;
	_uint16				fast_mask;

	_uint16				wide_mask;
	_uint16				ultra_mask;

	_uint16				ultra2_mask;
	_uint16				ultra160_mask;

	_uint16				ultra320_mask;
	_uint16				ultra640_mask;

	_uint32				timeout;			// command completion timeout
	_uint32				rst_settle;			// reset settle time
	SIM_HBA_STATS		stats;
};

int simq_ccb_enqueue( SIM_QUEUE *simq, CCB_SCSIIO *ccb );
CCB_SCSIIO *simq_ccb_dequeue( SIM_QUEUE *simq );
CCB_SCSIIO *simq_ccb_locate( SIM_QUEUE *simq, target_id_t target, lun_id_t lun, int tag );
SIM_QUEUE *simq_init( int coid, void *hba, int ntargs, int nluns, int max_non_tagged, int max_tagged, int mactive, int timeout );
int simq_dinit( SIM_QUEUE *simq );
void simq_set_qdepth( SIM_QUEUE *simq, target_id_t target, lun_id_t lun, int qdepth );
void simq_set_max_tag( SIM_QUEUE *simq, target_id_t target, lun_id_t lun, int mtag );
int simq_get_tag( CCB_SCSIIO *ccb );
int simq_ccb_state( CCB_SCSIIO *ccb, int state );
void simq_post_ccb( SIM_QUEUE *simq, CCB_SCSIIO *ccb );
void simq_scsi_reset( SIM_QUEUE *simq );
void simq_reset_bus( SIM_QUEUE *simq, CCB_RESETBUS *ccb );
void simq_reset_dev( SIM_QUEUE *simq, CCB_RESETDEV *ccb );
int simq_ccb_abort( SIM_QUEUE *simq, CCB_ABORT *ccb );
int simq_rel_simq( SIM_QUEUE *simq, CCB_RELSIM *ccb );
int simq_frz_simq( SIM_QUEUE *simq, CCB_FRZSIM *ccb );
int simq_timer_settime( SIM_QUEUE *simq, int seconds );
void simq_clock_gettime( SIM_QUEUE *simq, struct timespec *ts );

SIM_HBA *sim_alloc_hba( int ext_size );
void sim_free_hba( SIM_HBA *sim );
int sim_drvr_options( SIM_HBA *sim, char *options );
int sim_display_config( SIM_HBA *sim );

#endif

#if defined(__QNXNTO__) && defined(__USESRCVERSION)
#include <sys/srcversion.h>
__SRCVERSION("$URL: http://svn.ott.qnx.com/product/hardware/branches/release/hardware/devb/include/sim.h $ $Rev: 982571 $")
#endif
