#include <scsi.h>
#include "defines.h"
#include "ascsi.h"

#define	DEBUG	0

#if 0 // S_Retry()に任せるため外す
static unchar ReqSense[]={
	 0x03
	,0x00	// bit7,6,5=LUNだが、規格ではここは0にしてIDENTIFYを使うことを推奨しているので使わない
	,0x00
	,0x00
	,0x08	// 転送バイト
	,0x00
};
#endif

int S_CMD0( unlong id, int cdblen, unchar *cdb, int dlen, unchar *data )
// 任意のSCSIコマンド発行
//	id	SCSI-ID : 下位16bit=ID / 上位16bit=LUN
//	cdblen	CDB の長さ
//	cdb	CDB のアドレス
//	dlen	データ読み込みの場合、 バイト数
//		データ書き込みの場合、-バイト数
//		０ならデータ読み込み無し
//	data	データアドレス
// SCSI-BIOSがサポートしないベンダーユニークコマンドなどはこれで発行する
{
int rc,rc2,i;
unchar status,msg;
	// セレクショフェーズ
	if (id&0xffff0000) { // LUNがある時
	    unchar lun;
		lun=((unchar)(id>>16))&7;
		// LUNはMSGOUTで処理するので、S_SELECTAを使う
		for (i=0;i<SCSIRETRYMaxCount;i++) {
			if ( (rc=S_SELECTA( id&0xf )) ==0 ) break;
			// バスが確保出来なかったらSCSIRETRYMaxCount回リトライ
		}
		if (rc!=0) { // それでも確保出来ない時はエラー
			#if DEBUG
			printf("セレクションフェーズ(LUN):$%x\n",rc);
			#endif
			return( -1 ) ;
		}
		// LUNをIDENTIFYメッセージで送る
		lun|=BIT7;
		if ((rc = S_MSGOUT(&lun))!=0) {
			#if DEBUG
			printf("メッセージアウトフェーズ(LUN):$%x\n",rc);
			#endif
			return( -1 ) ;
		}
	} else { // LUNなし
		for (i=0;i<SCSIRETRYMaxCount;i++) {
			if ( (rc=S_SELECT( id&0xf )) ==0 ) break;
			// バスが確保出来なかったらSCSIRETRYMaxCount回リトライ
		}
		if (rc!=0) { // それでも確保出来ない時はエラー
			#if DEBUG
			printf("セレクションフェーズ:$%x\n",rc);
			#endif
			return( -1 ) ;
		}
	}
	
	// コマンドフェーズ
	if ( (rc=S_CMDOUT( cdblen, cdb )) !=0 ) {
		#if DEBUG
		printf("コマンドフェーズ:$%x\n",rc);
		#endif
		return( -1 );
	}
	
	// コマンドまで通ったら、データイン・アウトの有無に関わらず
	// ステータスフェーズ以下は実行する
	
	// データイン・アウトフェーズ
	if ( dlen != 0 ) { // データイン・アウトフェーズ指定
		if ( dlen>0 ) { /* ﾃﾞｰﾀｲﾝﾌｪｰｽﾞ */
			#if DEBUG
			printf("DataIn :%p〜%d\n",data,dlen);
			#endif
			rc2 = S_DATAIN( dlen, data );
			#if DEBUG
			if (rc2) printf("データインフェーズ:$%x\n",rc2);
			#endif
		} else { /* ﾃﾞｰﾀｱｳﾄﾌｪｰｽﾞ */
			#if DEBUG
			printf("DataOut:%p〜%d\n",data,-dlen);
			#endif
			rc2 = S_DATAOUT( -dlen, data );
			#if DEBUG
			if (rc2) printf("データアウトフェーズ:$%x\n",rc2);
			#endif
		}
		// データイン・アウトが失敗した時には何等かのエラーステータスが返る
	} 
	
	// ステータスインフェーズ
	if ( (rc=S_STSIN( &status ))!=0 ) {
		#if DEBUG
		printf("ステータスインフェーズ:$%x\n",rc);
		#endif
		return( -1 );
	}
	
	// メッセージインフェーズ
	if ( (rc=S_MSGIN( &msg )) !=0 ) {
		#if DEBUG
		printf("メッセージインフェーズ:$%x\n",rc);
		#endif
		return( -1 );
	}
	
	#if 0 // S_Retry()に任せるため外す
	if ((status&0b111110)==0b000010) { // CheckConditionが返ってきた
		unchar buff[8];	// CheckCondition発生時のデータ読み取り用
		// たいていのSCSIデバイスはCheckConditionが返ると
		// S_REQUEST()以外受け付けなくなるらしい。
		// また、ドライブがリセットされたり電源がON/OFFされると
		// 最初のコマンドに対する応答は必ずこれになる。
		#if DEBUG
		printf("CheckCondition\n");
		#endif
		rc=S_CMD0(id,sizeof(ReqSense),ReqSense,sizeof(buff),buff);
		if (rc<0) return(-1); // ReqestSenseが出来ない時はしょうがない
		msg=(buff[2]&0b00001111); // センスキー
	}
	#endif
	return( (msg<<8) | status );
}
