#ifndef __UMSS_H__
#define __UMSS_H__

#define MAX_BULK_TRANSFER_LENGTH		0x100000

#define PROTOCOL_CBI       				0x00
#define PROTOCOL_CB        				0x01
#define PROTOCOL_BULKONLY  				0x50

#define PROTOCOL_UNDEFINED 				0xFF  // Not in spec

#define UMSS_SUBCLASS_RBC				0x01
#define UMSS_SUBCLASS_SFF8020I			0X02
#define UMSS_SUBCLASS_QIC157			0x03
#define UMSS_SUBCLASS_UFI				0x04
#define UMSS_SUBCLASS_SFF8070I			0x05
#define UMSS_SUBCLASS_SCSI_TCS			0x06

#define ACCEPT_DEVICE_SPECIFIC_COMMAND 	0

#define BULK_ONLY_MASS_STORAGE_RESET 	0xFF
#define BULK_ONLY_GET_MAX_LUN 			0xFE

#define CBW_SIGNATURE 					0x43425355L
#define CSW_SIGNATURE 					0x53425355L
#define CSW_OLYMPUS_SIGNATURE			0x55425355L

#define CSW_STATUS_PASSED         		0x00
#define CSW_STATUS_FAILED         		0x01
#define CSW_STATUS_PHASE_ERROR    		0x02

#define IOCTL_UMSS_SUBMIT_CDB			CTL_CODE( FILE_USB_DEV_TYPE, 4200, METHOD_BUFFERED,		FILE_ANY_ACCESS )
// for request with no other input and output
// input buffer is a _USER_IO_PACKET and input_buffer_length is length of the _USER_IO_PACKET
// output_buffer is NULL, and output_buffer_length is zero

#define IOCTL_UMSS_SUBMIT_CDB_IN		CTL_CODE( FILE_USB_DEV_TYPE, 4201, METHOD_IN_DIRECT,	FILE_ANY_ACCESS )
// for request to read in data
// input_buffer is a _USER_IO_PACKET and input_buffer_length is length to the _USER_IO_PACKET
// output_buffer is a buffer to receive the data from dev, and
// output_buffer_length is the size of the buffer

#define IOCTL_UMSS_SUBMIT_CDB_OUT		CTL_CODE( FILE_USB_DEV_TYPE, 4202, METHOD_OUT_DIRECT,	FILE_ANY_ACCESS )
// for request to write data to device
// input_buffer is a _USER_IO_PACKET and input_buffer_length is length to the _USER_IO_PACKET
// output_buffer is data to send to the device, and
// output_buffer_length is the size of the buffer

#define IOCTL_REGISTER_DRIVER 			CTL_CODE( FILE_USB_DEV_TYPE, 4203, METHOD_BUFFERED, FILE_ANY_ACCESS )
// input_buffer is a CLASS_DRV_REG_INFO, and input_buffer_length is equal to or greater than
// sizeof( CLASS_DRV_REG_INFO ); the output_buffer is null and no output_buffer_length,
// only the following fields in urb can be accessed, others must be zeroed.

#define IOCTL_REVOKE_DRIVER 			CTL_CODE( FILE_USB_DEV_TYPE, 4204, METHOD_BUFFERED, FILE_ANY_ACCESS )
// tell the umss driver to clear the information in the drivers registry
// no other parameters

#define IOCTL_UMSS_SUBMIT_SRB 			CTL_CODE( FILE_USB_DEV_TYPE, 4205, METHOD_BUFFERED, FILE_ANY_ACCESS )
// irpStack->Parameters.Scsi.Srb points to an Srb structure and all the data buffer and buffer
// size are stored in the srb


#define IOCTL_UMSS_SET_FDO			CTL_CODE( FILE_USB_DEV_TYPE, 4206, METHOD_BUFFERED,	FILE_ANY_ACCESS )
// input_buffer is a pointer to PDEVICE_OBJECT, and input_buffer_length should be
// no less than sizeof( PDEVICE_OBJECT )
// output buffer is NULL, and output_buffer_length is zero
// if the deivce is accessable, the fdo is set, else, the fdo is not set and return
// STATUS_DEVICE_DOES_NOT_EXIST

#define SFF_FORMAT_UNIT			0x04
#define SFF_INQUIRY				0x12
#define SFF_MODE_SELECT			0x55
#define SFF_MODE_SENSE			0x5a
#define SFF_ALLOW_REMOVE		0x1e
#define SFF_READ10				0x28
#define SFF_READ12				0xa8
#define SFF_READ_CAPACITY		0x25
#define	SFF_REQUEST_SENSEE		0x03
#define SFF_SEEK				0x2b
#define SFF_START_STOP			0x1b
#define SFF_TUR					0x00
#define SFF_VERIFY				0x2f
#define SFF_WRITE10				0x2a
#define SFF_WRITE12				0xaa
#define SFF_READ_FMT_CAPACITY	0x23
#define SFF_WRITE_VERIFY		0x2e

#define MAX_CDB_LENGTH			0x10

typedef struct _USER_IO_PACKET
{
	UCHAR					sub_class;
	UCHAR					lun;
	UCHAR   				cdb_length;
	UCHAR   				cdb[ MAX_CDB_LENGTH ];

} USER_IO_PACKET, *PUSER_IO_PACKET;

//flags for IO_PACKET::flags
#define IOP_FLAG_REQ_SENSE			0x80000000 		// sense data would be fetched if error occurs
#define IOP_FLAG_SRB_TRANSFER		0x40000000		// current tranfer is initiated by an srb request, the srb is held by the irp
#define IOP_FLAG_SCSI_CTRL_TRANSFER 0x20000000		// current transfer is initiated by an scsi ioctrl request

#define IOP_FLAG_DIR_IN			USB_DIR_IN
#define IOP_FLAG_STAGE_MASK		0x03
#define IOP_FLAG_STAGE_NORMAL	0x00
#define IOP_FLAG_STAGE_SENSE	0x01

typedef struct _IO_PACKET
{
        ULONG   			flags;
        UCHAR   			cdb_length;
        UCHAR   			cdb[ MAX_CDB_LENGTH ];
		UCHAR				lun;
        PVOID   			data_buffer;
        ULONG   			data_length;
		PVOID				sense_data;
		ULONG				sense_data_length;
		PIRP				pirp;

} IO_PACKET, *PIO_PACKET;

#pragma pack( 1 )

typedef struct _COMMAND_BLOCK_WRAPPER
{
        ULONG 				dCBWSignature;
        ULONG 				dCBWTag;
        ULONG 				dCBWDataTransferLength;
        UCHAR 				bmCBWFlags;
        UCHAR 				bCBWLun;
        UCHAR 				bCBWLength;
        UCHAR 				CBWCB[ MAX_CDB_LENGTH ];

} COMMAND_BLOCK_WRAPPER, *PCOMMAND_BLOCK_WRAPPER;

typedef struct _COMMAND_STATUS_WRAPPER
{
        ULONG 				dCSWSignature;
        ULONG 				dCSWTag;
        ULONG 				dCSWDataResidue;
        UCHAR 				bCSWStatus;

} COMMAND_STATUS_WRAPPER, *PCOMMAND_STATUS_WRAPPER;


typedef struct _INTERRUPT_DATA_BLOCK
{
        UCHAR 				bType;
        UCHAR 				bValue;

} INTERRUPT_DATA_BLOCK, *PINTERRUPT_DATA_BLOCK;

#pragma pack()

#define UMSS_PNPMSG_STOP			0x01
#define UMSS_PNPMSG_DISCONNECT 		0x02

typedef NTSTATUS ( *PCLASS_DRVR_PNP_DISP )( PDEVICE_OBJECT pdo, ULONG ctrl_code, PVOID context );
	// pdo is the device object umss created

typedef PDEVICE_OBJECT ( *PCLASS_DRIVER_ADD_DEV )( PDRIVER_OBJECT fdo_drvr, PDEVICE_OBJECT pdo );
	// if the return value is not zero, it is a pointer to the
	// fdo sitting over the pdo of this driver. if it is null,
	// the add_device failed, and initialization process should
	// stall.

typedef struct _CLASS_DRV_REGISTRY_INFO
{
	// class driver will pass this structure to umss port
	// driver after loaded
	PDRIVER_OBJECT			fdo_driver;
	PCLASS_DRIVER_ADD_DEV   add_device;
	PCLASS_DRVR_PNP_DISP	pnp_dispatch;

} CLASS_DRV_REG_INFO, *PCLASS_DRV_REG_INFO;

typedef struct _UMSS_PORT_DEVICE_EXTENSION
{
	// this structure is the device extension for port dev_obj
	// it is used to has class driver pass CLASS_DRV_REG_INFO
	// to our umss driver.
	DEVEXT_HEADER			dev_ext_hdr;
	PUSB_DRIVER				pdriver;

} UMSS_PORT_DEV_EXT, *PUMSS_PORT_DEV_EXT;

typedef struct _UMSS_DRVR_EXTENSION
{
	LIST_HEAD				dev_list;
	FAST_MUTEX				dev_list_mutex;
	UCHAR					dev_count;
	CLASS_DRV_REG_INFO		class_driver_info;
	PDEVICE_OBJECT			port_dev_obj;	// we use this obj as a connection point for class driver, its name usbPort0

} UMSS_DRVR_EXTENSION, *PUMSS_DRVR_EXTENSION;

#define UMSS_DEV_FLAG_IF_DEV	0x01
#define UMSS_DEV_FLAG_OLYMPUS_DEV	0x02

#define UMSS_OLYMPUS_VENDOR_ID	0x07b4

typedef struct _UMSS_DEVICE_EXTENSION
{
	//this structure is the device extension for dev_obj
	//created for the device.
	DEVEXT_HEADER			dev_ext_hdr;

	ULONG					flags;
	LIST_ENTRY				dev_obj_link;	// this link is used by the driver object to track the existing dev_objs

	PDEVICE_OBJECT  		pdo;			// this is the pdo
	PDEVICE_OBJECT			fdo;			// driver object for the dev_obj

	DEV_HANDLE				dev_handle;		// handle to the usb_dev under

	PUCHAR					desc_buf;
	UCHAR					umss_dev_id;	// used to build symbolic link

	PUSB_INTERFACE_DESC 	pif_desc;
	PUSB_ENDPOINT_DESC  	pout_endp_desc, pin_endp_desc, pint_endp_desc;
	UCHAR					if_idx, out_endp_idx, in_endp_idx, int_endp_idx;

	struct _USB_DEV_MANAGER	*dev_mgr;

	//working data
	COMMAND_BLOCK_WRAPPER	cbw;
	union
	{
		INTERRUPT_DATA_BLOCK	idb;
		COMMAND_STATUS_WRAPPER	csw;
	};

	KEVENT					sync_event; //for umss_sync_submit_urb
	KSPIN_LOCK				dev_lock;
	IO_PACKET				io_packet;
	BOOLEAN					retry;

	PUSB_DRIVER				pdriver;	//used by umss_delete_device
	NTSTATUS				reset_pipe_status;
} UMSS_DEVICE_EXTENSION, *PUMSS_DEVICE_EXTENSION;

// for device creation workitem
typedef struct _UMSS_CREATE_DATA
{
	DEV_HANDLE 				dev_handle;
	PUCHAR					desc_buf;
	PUSB_DEV_MANAGER 		dev_mgr;
	PUSB_DRIVER				pdriver;

} UMSS_CREATE_DATA, *PUMSS_CREATE_DATA;

// for reset pipe item
//typedef void ( _stdcall *COMPLETION_HANDLER )( PVOID );
typedef void ( *UMSS_WORKER_ROUTINE )( PVOID );

typedef struct _UMSS_WORKER_PACKET
{
    UMSS_WORKER_ROUTINE 	completion;
    PVOID 					context;
	PUSB_DEV_MANAGER		dev_mgr;
	PVOID					pdev;

} UMSS_WORKER_PACKET, *PUMSS_WORKER_PACKET;

BOOLEAN
umss_driver_init(
PUSB_DEV_MANAGER dev_mgr,
PUSB_DRIVER pdriver
);

BOOLEAN
umss_if_driver_init(
PUSB_DEV_MANAGER dev_mgr,
PUSB_DRIVER pdriver
);

BOOLEAN
umss_driver_destroy(
PUSB_DEV_MANAGER dev_mgr,
PUSB_DRIVER pdriver
);

#define umss_if_driver_destroy umss_driver_destroy

BOOLEAN
umss_if_driver_destroy(
PUSB_DEV_MANAGER dev_mgr,
PUSB_DRIVER pdriver
);

VOID
umss_complete_request(
PUMSS_DEVICE_EXTENSION pdev_ext,
NTSTATUS status
);

NTSTATUS
umss_reset_pipe(
PUMSS_DEVICE_EXTENSION pdev_ext,
DEV_HANDLE endp_handle
);

PVOID
umss_get_buffer(
PUMSS_DEVICE_EXTENSION  pdev_ext,
ULONG* buf_length
);

NTSTATUS
umss_bulk_transfer(
IN PUMSS_DEVICE_EXTENSION pdev_ext,
IN UCHAR trans_dir,
IN PVOID buf,
IN ULONG buf_length,
IN PURBCOMPLETION completion
);

BOOLEAN
umss_schedule_workitem(
PVOID context,
UMSS_WORKER_ROUTINE completion,
PUSB_DEV_MANAGER dev_mgr,
DEV_HANDLE dev_handle
);

NTSTATUS
umss_bulkonly_startio(
IN PUMSS_DEVICE_EXTENSION pdev_ext,
IN PIO_PACKET io_packet
);

NTSTATUS
umss_cbi_startio(
IN PUMSS_DEVICE_EXTENSION pdev_ext,
IN PIO_PACKET io_packet
);

#define UMSS_FORGE_GOOD_SENSE( sense_BUF ) \
{\
    int i;\
	PUCHAR buf = ( PUCHAR )( sense_BUF ); \
    for( i = 0; i < 18; i++)\
    {\
        buf[i] = 0;\
    }\
    buf[7] = 10;\
}

#endif
