/*	
 *	clipusb.h
 *
 *	USBドライバを定義します。
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2015 Naoyuki Sawa
 *
 *	* Sat Oct 30 05:43:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Sun Jul 03 03:45:00 JST 2005 Naoyuki Sawa
 *	- ベンダーID、プロダクトIDの定義を追加しました。
 *	* Wed Jul 06 05:26:00 JST 2005 Naoyuki Sawa
 *	- エンドポイントインデクスの定義を追加しました。
 *	* Mon Aug 22 04:24:00 JST 2005 Naoyuki Sawa
 *	- usb_init(),usb_free()->usb_start(),usb_stop()に名前変更しました。
 *	* Sat Dec 08 00:00:11 JST 2007 Naoyuki Sawa
 *	- 受信オーバーランを回避する実装を可能とするための仕様変更を行いました。
 *	  これまでは、ep1_txdone(),ep2_rxdone()をコールバックする前に受信バッファを読み出し、クリアしていましたが、
 *	  今後は、ep1_txdone(),ep2_rxdone()内で受信バッファを読み出し、処理できた場合のみクリアする仕様となりました。
 *	- この仕様変更を吸収し、これまでと同じ動作とする方法は、clipuio.c,clipeth.cを参照してください。
 *	  この仕様変更に対応し、受信オーバーランを回避する実装とする方法は、clipacm.cを参照してください。
 *	* Thu Sep 04 22:00:29 JST 2014 Naoyuki Sawa
 *	- 定数の定義を追加しました。
 *	* Sun Sep 21 14:34:51 JST 2014 Naoyuki Sawa
 *	- ユーティリティとして、USB_UINT16()～USB_UINT128()のマクロを追加しました。
 *	* Thu May 07 21:15:37 JST 2015 Naoyuki Sawa
 *	- 受信コントロールデータバッファを動的に割り当てるようにしました。
 *	- これまでは、受信コントロールデータバッファを、16バイト固定サイズにしていました。
 *	  受信コントロールデータサイズが16バイトを超える事は無いだろうと考えてそうしていたのですが、16バイトを超える事が発生しました。
 *	  具体的には、UVC(USB video device class)の、SET_CURでリクエストで、26バイトです。
 *	  これまでの実装では、UVCのSET_CURでリクエストに対して、失敗の応答(=ストール)を返してしまっていました。
 *	- 対策案としては、単純に、受信コントロールデータバッファを増やす方法も有りますが、その方法は取らない事にしました。
 *	  様々なデバイスクラスで、受信コントロールデータサイズの最大値がどれぐらいまで有り得るのか、想定できないからです。
 *	  そこで、Device-to-hostのデバイスリクエストの都度、受信コントロールデータバッファを動的に割り当てる事にしました。
 *	- 元々、当モジュールを作成した当初は、割り込み内での動的メモリ割り当てが出来なかったので、前述のような固定サイズの方法を取っていたのですが、今では、割り込み内での動的メモリ割り当ても安全に使用できます。
 *	  その他のデメリットとして、これまでよりも多少、デバイスリクエストへの割り込み応答性能が低下しますが、デバイスリクエストに高速性はあまり重要では無いと思うので、大丈夫だと思います。
 *	  まとめると、今回の変更による実用上のデメリットはほぼ無く、今後はデバイスリクエストの受信コントロールデータサイズを気にせずに、下位モジュールを実装する事が出来るようになりました。
 *	  当モジュールの外部インターフェイスは変更していませんので、既存の下位モジュールの変更も不要です。
 *	* Tue May 12 20:44:50 JST 2015 Naoyuki Sawa
 *	- D12OutCmd(),D12OutDat(),D12InDat()を、ローカル関数⇒グローバル関数に変更しました。
 *	  アプリケーションから、「PDIUSBD12 コマンド発行」関数群よりも細かな制御を、行いたいケースが生じたからです。
 *	- コマンドコードのシンボル定義(D12_SET_ADDRESS_ENABLE～D12_READ_CHIP_ID)を、clipusb.c⇒clipusb.hへ移動しました。
 *	  上記の関数をグローバル関数に変更したので、アプリケーションからもコマンドコードを参照する必要が生じたからです。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'～'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 */
#ifndef __CLIP_USB_H__
#define __CLIP_USB_H__

#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus

/****************************************************************************
 *	P/ECE開発ツール C:/usr/PIECE/sysdev/pcekn/pceusb.h より
 ****************************************************************************/

/* ベンダーIDです。
 * [note]
 *	* 登録 OeRSTED, Inc. 16進=0x0e19 10進=3609
 */
#define PCE_VENDOR_ID		0x0e19

/* プロダクトIDです。
 */
#define PCE_PRODUCT_ID		0x1000
#define PCE_PRODUCT_ID_BASIC	(PCE_PRODUCT_ID + 0)	/* 基本モード */
#define PCE_PRODUCT_ID_GAMEPAD	(PCE_PRODUCT_ID + 1)	/* GamePadモード */

/* プロダクトIDの予約
 * [note]
 *	* 0x1002 - 0x10bf : P/ECEの機能拡張のため予約
 *	  0x10c0 - 0x10ff : ユーザ実験用に予約
 *	  上記以外の番号は使用しないでください。
 */

/****************************************************************************
 *	PDIUSBD12
 ****************************************************************************/

/* エンドポイントインデクス */
#define EP0OUT					  0
#define EP0IN					  1
#define EP1OUT					  2
#define EP1IN					  3
#define EP2OUT					  4
#define EP2IN					  5

/* 各エンドポイントの最大パケットサイズ */
#define EP0_PACKET_SIZE				 16
#define EP1_PACKET_SIZE				 16
#define EP2_NONISO_PACKET_SIZE			 64	/* double-buffered */
#define EP2_ISOOUT_PACKET_SIZE			128	/* double-buffered */
#define EP2_ISOIN_PACKET_SIZE			128	/* double-buffered */
#define EP2_ISOIO_PACKET_SIZE			 64	/* double-buffered */
//{{2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
//#define EP0TO2_MAX_PACKET_SIZE		128
//}}2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更

/* D12OutCmd(cmd) */
/* Initialization commands */
#define D12_SET_ADDRESS_ENABLE			0xD0
#define D12_SET_ENDPOINT_ENABLE			0xD8
#define D12_SET_MODE				0xF3
#define D12_SET_DMA				0xFB
/* Data flow commands */
#define D12_READ_INTERRUPT_REGISTER		0xF4
#define D12_SELECT_ENDPOINT			0x00	/* 0x00～0x05 */
#define D12_READ_LAST_TRANSACTION_STATUS	0x40	/* 0x40～0x45 */
#define D12_READ_ENDPOINT_STATUS		0x80	/* 0x80～0x85 */
#define D12_READ_BUFFER				0xF0
#define D12_WRITE_BUFFER			0xF0
#define D12_SET_ENDPOINT_STATUS			0x40	/* 0x40～0x45 */
#define D12_ACKNOWLEDGE_SETUP			0xF1
#define D12_CLEAR_BUFFER			0xF2
#define D12_VALIDATE_BUFFER			0xFA
#define D12_SEND_RESUME				0xF6
#define D12_READ_CURRENT_FRAME_NUMBER		0xF5
/* Undocumented commands */
#define D12_READ_CHIP_ID			0xFD

/* D12_SetMode(config) */
#define D12_NOLAZYCLOCK				0x02
#define D12_CLOCKRUNNING			0x04
#define D12_INTERRUPTMODE			0x08
#define D12_SOFTCONNECT				0x10
#define D12_ENDP_NONISO				0x00
#define D12_ENDP_ISOOUT				0x40
#define D12_ENDP_ISOIN				0x80
#define D12_ENDP_ISOIO				0xC0
/* D12_SetMode(clkdiv) */
#define D12_CLOCK_12M				0x03
#define D12_CLOCK_4M				0x0b
#define D12_SETTOONE				0x40
#define D12_SOFONLY				0x80
/* D12_SetDMA(mode) */
#define D12_DMASINGLE				0x00
#define D12_BURST_4				0x01
#define D12_BURST_8				0x02
#define D12_BURST_16				0x03
#define D12_DMAENABLE				0x04
#define D12_DMA_INTOKEN				0x08
#define D12_AUTOLOAD				0x10
#define D12_NORMALPLUSSOF			0x20
#define D12_ENDP4INTENABLE			0x40
#define D12_ENDP5INTENABLE			0x80
/* D12_ReadInterruptRegister(retval) */
#define D12_INT_ENDP0OUT			0x01
#define D12_INT_ENDP0IN				0x02
#define D12_INT_ENDP1OUT			0x04
#define D12_INT_ENDP1IN				0x08
#define D12_INT_ENDP2OUT			0x10
#define D12_INT_ENDP2IN				0x20
#define D12_INT_BUSRESET			0x40
#define D12_INT_SUSPENDCHANGE			0x80
#define D12_INT_EOT				0x0100
/* D12_ReadLastTransactionStatus(retval) */
#define D12_DATARECEIVETRANSMITSUCCESS		0x01																		/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_NOERROR				0x00	//no error																/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_PIDENCODINGERROR			0x02	//PID encoding error; bits 7 to 4 are not the inversion of bits 3 to 0									/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_PIDUNKNOWN				0x04	//PID unknown; encoding is valid, but PID does not exist										/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_UNEXPECTEDPACKET			0x06	//unexpected packet; packet is not of the type expected (= token, data or acknowledge), or SETUP token to a non-control endpoint	/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_TOKENCRCERROR			0x08	//token CRC error															/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_DATACRCERROR			0x0A	//data CRC error															/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_TIMEOUTERROR			0x0C	//time-out error															/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_NEVERHAPPENS			0x0E	//never happens																/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_UNEXPECTEDEOP			0x10	//unexpected End-Of-Packet (EOP)													/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_SENTORRECEIVEDNAK			0x12	//sent or received NAK															/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_SENTSTALL				0x14	//sent stall, a token was received, but the endpoint was stalled									/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_OVERFLOWERROR			0x16	//overflow error, the received packet was longer than the available buffer space							/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_BITSTUFFERROR			0x1A	//bit stuff error															/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_WRONGDATAPID			0x1E	//wrong DATA PID; the received DATA PID was not what was expected									/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_SETUPPACKET				0x20
#define D12_DATA01PACKET			0x40																		/*{{2014/09/04追加:定数の定義を追加しました。}}*/
#define D12_PREVIOUSSTATUSNOTREAD		0x80																		/*{{2014/09/04追加:定数の定義を追加しました。}}*/
/* D12_ReadEndpointStatus(retval) */
//#define D12_SETUPPACKET			0x04	//D12_ReadLastTransactionStatusでの定義と名前が被るのでこちらは定義しないことにしました。
#define D12_BUFFER0FULL				0x20
#define D12_BUFFER1FULL				0x40
#define D12_ENDPOINTSTALLED			0x80																		/*{{2014/09/04追加:定数の定義を追加しました。}}*/
/* D12_SelectEndpoint(retval) */
#define D12_FULLEMPTY				0x01
#define D12_STALL				0x02

void D12OutCmd(int cmd);
void D12OutDat(int dat);
int D12InDat();

/* Initialization commands */
void D12_SetAddressEnable(int addr, int enable);
void D12_SetEndpointEnable(int enable);
void D12_SetMode(int config, int clkdiv);
void D12_SetDMA(int mode);
/* Data flow commands */
int D12_ReadInterruptRegister();
int D12_SelectEndpoint(int endp);
int D12_ReadLastTransactionStatus(int endp);
int D12_ReadEndpointStatus(int endp);
//{{2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
//int D12_ReadEndpoint(int endp, void* buf, int maxlen);
//int D12_WriteEndpoint(int endp, const void* ptr, int len);
//↓2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
int D12_ReadBuffer(int endp, void* buf, int maxlen);
int D12_WriteBuffer(int endp, const void* ptr, int len);
//}}2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
void D12_SetEndpointStatus(int endp, int stall);
void D12_AcknowledgeSetup(int endp);
//{{2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
void D12_ClearBuffer(int endp);
void D12_ValidateBuffer(int endp);
//}}2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
/* General commands */
void D12_SendResume();
int D12_ReadCurrentFrameNumber();
/* Undocumented commands */
int D12_ReadChipID();

/****************************************************************************
 *	USB仕様
 ****************************************************************************/

/* DEVICE_REQUEST.bRequest */
#define GET_STATUS		 0	/* デバイスなどの状態を取得する */
#define CLEAR_FEATURE		 1	/* 指定した機能を無効にする。おもにSTALLのクリアで使用 */
#define SET_FEATURE		 3	/* 指定した機能を有効にする */
#define SET_ADDRESS		 5	/* デバイスのアドレスを設定する */
#define GET_DESCRIPTOR		 6	/* ディスクリプタを取得する */
#define SET_DESCRIPTOR		 7	/* ディスクリプタを設定する */
#define GET_CONFIGURATION	 8	/* コンフィギュレーションを取得する */
#define SET_CONFIGURATION	 9	/* コンフィギュレーションを決定する */
#define GET_INTERFACE		10	/* インターフェースを取得する */
#define SET_INTERFACE		11	/* インターフェースを決定する */
#define SYNCH_FRAME		12	/* 同期を取るためにフレーム番号を取得する */

typedef struct _DEVICE_REQUEST {
	unsigned char bmRequestType;	/* D7  : 転送方向 */
					/*       0:OUT 1:IN */
					/* D6-5: タイプ */
					/*       0:標準 1:クラス 2:ベンダ 3:予約 */
					/* D4-0: 受信側 */
					/*       0:デバイス 1:インターフェース 2:エンドポイント 3:その他 4以降:予約 */
	unsigned char  bRequest;	/* リクエストの特定 */
	unsigned short wValue;		/* bRequestで定められる */
	unsigned short wIndex;		/* bRequestで定められるインデックスまたはオフセット */
	unsigned short wLength;		/* データの長さ */
} __attribute__((packed)) DEVICE_REQUEST;
//~~~~~~~~~~~~~~~~~~~~~~~
//DEVICE_REQUEST構造体に関して言えば、パックを明示しなくても同じですが、わざと明示しています。
//他のUSB構造体定義においても、パディングを防ぐため__attribute__((packed))を明示してください。
//
// 2004/10/31 20:10:00 Naoyuki Sawa 追記
//
// * P/ECEのCコンパイラで__attribute__((packed))を使う場合は、細心の注意が必要です!!
//   構造体末尾のパディングを除去し、奇数サイズを維持する程度の用途にしか使えません。
//   構造体途中に、奇数オフセットから始まるHalfやLongがあると、誤ったバイナリが生成されます。
//
// * 幸い、DEVICE_REQUESTや以下のディスクリプタ構造体は、packedの有無にかかわらず同じ配置となります。
//   (USB仕様そのものが、構造体の奇数オフセットにHalfやLongを配置しないよう、配慮してあるのだと思います)
//   構造体末尾のパディングを除去し、奇数サイズとするためだけに、__attribute__((packed))を指定しています。
//
// * P/ECEのCコンパイラは、__attribute__((packed))を正しく扱えません。
//   具体的に言うと、Cコンパイラ(gcc33.exe)はpackedを正しく認識し、オフセットやサイズを計算しているのですが、
//   アセンブラ(as33.exe)が勝手にパディングを追加してしまっています。
//
//	struct ARRAY {
//		unsigned char  a;
//		unsigned short b;
//	} __attribute__((packed));
//	struct ARRAY array = { 0x12, 0x3456 };
//	/* sizeof(struct ARRAY)は正しく3となっています。が... */
//
//	↓gcc33.exeによって生成されるコードは...
//
//	.byte 0x12
//	.half 0x3456	; .half擬似命令は、★直前に★.align 0がない限り、暗黙のアライメントを行う
//
//	↓as33.exeによって生成されるバイナリは...
//
//	0x12
//	0x00		; .half擬似命令による暗黙のパディング
//	0x56
//	0x34
//
// * 幸い、DEVICE_REQUESTや以下のディスクリプタ構造体は、奇数オフセットにHalfやLongがないので、
//   ★単独で使う限り★、上述の例のような問題は発生しません。
//   しかしながら、構造体配列を使うと、問題が発生します。
//
//	const ENDPOINT_DESCRIPTOR endpoint_desc[2] = {{
//		bLength:		sizeof(ENDPOINT_DESCRIPTOR),
//		bDescriptorType:	ENDPOINT_DESCRIPTOR_TYPE,
//		bEndpointAddress:	0x82, /* EP2IN */
//		bmAttributes:		2, /* Bulk */
//		wMaxPacketSize:		EP2_NONISO_PACKET_SIZE,
//	},{
//		bLength:		sizeof(ENDPOINT_DESCRIPTOR),
//		bDescriptorType:	ENDPOINT_DESCRIPTOR_TYPE,
//		bEndpointAddress:	0x02, /* EP2OUT */
//		bmAttributes:		2, /* Bulk */
//		wMaxPacketSize:		EP2_NONISO_PACKET_SIZE,
//	}};
//
//	↓gcc33.exeによって生成されるコードは...
//
//	.byte	7	// + 0,1 ... endpoint_desc[0]の開始
//	.byte	5	// + 1,1
//	.byte	130	// + 2,1
//	.byte	2	// + 3,1
//	.half	64	// + 4,2
//	.space	1	// + 6,1
//	.byte	7	// + 7,1 ... endpoint_desc[1]の開始
//	.byte	5	// + 8,1
//	.byte	2	// + 9,1
//	.byte	2	// +10,1
//	(パディングx1)	// 次の.half擬似命令が、暗黙のアライメントを行ってしまう!!
//	.half	64	// +12,2
//	.space	1	// +14,1
//
// * 結論は次のとおりです。
//   基本的には、P/ECEのCコンパイラで__attribute__((packed))を使ってはいけません。
//   __attribute__((packed))の有無にかかわらず同じフィールドオフセットとなる構造体において、
//   構造体末尾のパディングを除去し、奇数サイズを維持するためだけに__attribute__((packed))を
//   指定しても構いませんが、そのような構造体の配列を使うと、不正なバイナリが生成されます。
//   __attribute__((packed))を指定した構造体の配列を作成してはいけません。

/* ????_DESCRIPTOR.bDescriptorType */
#define DEVICE_DESCRIPTOR_TYPE		1
#define CONFIGURATION_DESCRIPTOR_TYPE	2
#define STRING_DESCRIPTOR_TYPE		3
#define INTERFACE_DESCRIPTOR_TYPE	4
#define ENDPOINT_DESCRIPTOR_TYPE	5

/* 構造体配列使用禁止!! 理由は上の方のコメントを参照 */
typedef struct _DEVICE_DESCRIPTOR {
	unsigned char  bLength;			/* ディスクリプタのサイズ (18) */
	unsigned char  bDescriptorType;		/* ディスクリプタのタイプ (1) */
	unsigned short bcdUSB;			/* BCD表現のUSB仕様リリース番号 */
	unsigned char  bDeviceClass;		/* クラスコード */
	unsigned char  bDeviceSubClass;		/* サブクラスコード */
	unsigned char  bDeviceProtocol;		/* プロトコルコード */
	unsigned char  bMaxPacketSize0;		/* エンドポイント0の最大パケットサイズ */
	unsigned short idVendor;		/* ベンダID */
	unsigned short idProduct;		/* プロダクトID */
	unsigned short bcdDevice;		/* BCD表現のデバイスのリリース番号 */
	unsigned char  iManufacture;		/* 製造者ストリングディスクリプタインデックス */
	unsigned char  iProduct;		/* 製品ストリングディスクリプタインデックス */
	unsigned char  iSerialNumber;		/* 製造番号ストリングディスクリプタインデックス */
	unsigned char  bNumConfigurations;	/* 構成可能な数 */
} __attribute__((packed)) DEVICE_DESCRIPTOR;

/* 構造体配列使用禁止!! 理由は上の方のコメントを参照 */
typedef struct _CONFIGURATION_DESCRIPTOR {
	unsigned char  bLength;			/* ディスクリプタのサイズ (9) */
	unsigned char  bDescriptorType;		/* ディスクリプタのタイプ (2) */
	unsigned short wTotalLength;		/* 構成全体(コンフィギュレーション、インターフェース、エンドポイントディスクリプタの合計)の長さ */
	unsigned char  bNumInterfaces;		/* コンフィギューレションが持つインターフェースの数 */
	unsigned char  bConfigurationValue;	/* Set Configuration でこの構成を選択するための引数 */
	unsigned char  iConfiguration;		/* コンフィギュレーションのストリングディスクリプタへのインデックス */
	unsigned char  bmAttributes;		/* 構成の特性 */
						/* D7:バス電源 D6:自己電源 D5:リモートウェークアップ */
	unsigned char  MaxPower;		/* 最大バス電力消費量を2mA単位で指定 */
} __attribute__((packed)) CONFIGURATION_DESCRIPTOR;

/* 構造体配列使用禁止!! 理由は上の方のコメントを参照 */
typedef struct _STRING_DESCRIPTOR {
	unsigned char  bLength;			/* ディスクリプタのサイズ (2+サイズ) */
	unsigned char  bDescriptorType;		/* ディスクリプタのタイプ (3) */
	// unsigned short wLANGID[サイズ/2] または char bString[サイズ] が続きます。 */
} __attribute__((packed)) STRING_DESCRIPTOR;

/* 構造体配列使用禁止!! 理由は上の方のコメントを参照 */
typedef struct _INTERFACE_DESCRIPTOR {
	unsigned char bLength;			/* ディスクリプタのサイズ (9) */
	unsigned char bDescriptorType;		/* ディスクリプタのタイプ (4) */
	unsigned char bInterfaceNumber;		/* このインターフェースを表すインデックス番号 */
	unsigned char bAlternateSetting;	/* Set Interface で代替設定を選択するための引数値 */
	unsigned char bNumEndpoints;		/* このインターフェースが持つエンドポイントの数 */
	unsigned char bInterfaceClass;		/* クラスコード(ストレージ、プリンタなどのクラスを指定する) */
	unsigned char bInterfaceSubClass;	/* サブクラスコード */
	unsigned char bInterfaceProtocol;	/* プロトコルコード */
	unsigned char iInterface;		/* インターフェースのストリングディスクリプタへのインデックス */
} __attribute__((packed)) INTERFACE_DESCRIPTOR;

/* 構造体配列使用禁止!! 理由は上の方のコメントを参照 */
typedef struct _ENDPOINT_DESCRIPTOR {
	unsigned char  bLength;			/* ディスクリプタのサイズ (7) */
	unsigned char  bDescriptorType;		/* ディスクリプタのタイプ (5) */
	unsigned char  bEndpointAddress;	/* エンドポイント番号 */
						/* D7  : 方向 */
						/*       0:OUT 1:IN */
						/* D6-4: 予約(0) */
						/* D3-0: エンドポイント番号 */
	unsigned char  bmAttributes;		/* 属性 */
						/* D1-0: 転送タイプ */
						/*       0:コントロール 1:アイソクロナス 2:バルク 3:インタラプト */
	unsigned short wMaxPacketSize;		/* 最大パケットサイズ */
	unsigned char  bInterval;		/* ポーリング間隔 */
} __attribute__((packed)) ENDPOINT_DESCRIPTOR;

/****************************************************************************
 *	USBドライバ
 ****************************************************************************/

/* USBINF.(Device request callbacks) */
typedef void (*DEVICE_REQUEST_CALLBACK)(const DEVICE_REQUEST* req, const unsigned char* dat, int len);

/* USBフック情報
 * * usb_start()への引数として利用します。
 *   modeは必ず設定してください。通常は0x00:NONISOなので、ゼロクリアだけでも構いません。
 *   イベント/リクエストハンドラは使うものだけ設定し、使わないものはNULLにしてください。
 *
 * // ★★★重要!!★★★
 * // * アプリケーションにてUSBINFを作成する場合に、GCC拡張の構造体メンバ指定構文を使ってはいけません!!
 * //   たとえば、modeとep1_txdoneとset_addressだけを設定し、残りメンバをNULLとする場合に、
 * //   次のようなコードを書いたとすると...
 * // 
 * // 	／＊間違ったメモリイメージが生成される例!!＊／
 * // 	USBINF usb_inf = {
 * // 		mode: D12_ENDP_NONISO,
 * // 		ep1_txdone: my_ep1_txdone,
 * // 		standard_request: {
 * // 			set_address: my_set_address,
 * // 		},
 * // 	};
 * // 
 * //   gcc33.exeが構造体の初期化を誤り、不要な空白がたくさん入ったメモリイメージを生成してしまいます。
 * //   どうやらgcc33.exeは、ネストした構造体に対する、GCC拡張の構造体メンバ初期化構文に不具合があるようです。
 * //   gcc33.exe以外のGCC(x86,Sparc,MIPS,SH用等...)では大丈夫なのかどうか、未確認です。
 * //   なおgcc33.exeでも、ネストしていない構造体の場合は問題なくGCC拡張の構造体メンバ初期化構文が使えます。
 * // 
 * →2004/10/31 15:39:00 Naoyuki Sawa
 *   上述の問題を避けるため、構造体のネストをやめました。これで、GCC拡張構文を使って初期化しても大丈夫です。
 *   他のプログラムでも、構造体をネストするときは、GCC拡張構文を使って初期化しないよう、気をつけてください。
 */
typedef struct _USBINF {
	/* USBC mode */
	int mode;							/* USBCモード(D12_ENDP_NONISO/ISOOUT/ISOIN/ISOIO) */
	/* USBC raw event */
	void (*bus_reset)();						/* バスリセットイベント */
//{{2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
//	void (*ep1_rxdone)(const unsigned char dat[/*len*/], int len);	/* EP1受信完了イベント */
//↓2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
	void (*ep1_rxdone)();						/* EP1受信完了イベント */
//}}2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
	void (*ep1_txdone)();						/* EP1送信完了イベント */
//{{2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
//	void (*ep2_rxdone)(const unsigned char dat[/*len*/], int len);	/* EP2受信完了イベント */
//↓2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
	void (*ep2_rxdone)();						/* EP2受信完了イベント */
//}}2007/12/08:受信オーバーランを回避する実装を可能とするための仕様変更
	void (*ep2_txdone)();						/* EP2送信完了イベント */
	/* Device request */
	//struct {
	//→構造体のネストはやめました。理由は上のコメントを参照してください。
		DEVICE_REQUEST_CALLBACK get_status;			/* デバイスなどの状態を取得する */
		DEVICE_REQUEST_CALLBACK clear_feature;			/* 指定した機能を無効にする。おもにSTALLのクリアで使用 */
		DEVICE_REQUEST_CALLBACK set_feature;			/* 指定した機能を有効にする */
		DEVICE_REQUEST_CALLBACK set_address;			/* デバイスのアドレスを設定する */
		DEVICE_REQUEST_CALLBACK get_descriptor;			/* ディスクリプタを取得する */
		DEVICE_REQUEST_CALLBACK set_descriptor;			/* ディスクリプタを設定する */
		DEVICE_REQUEST_CALLBACK get_configuration;		/* コンフィギュレーションを取得する */
		DEVICE_REQUEST_CALLBACK set_configuration;		/* コンフィギュレーションを決定する */
		DEVICE_REQUEST_CALLBACK get_interface;			/* インターフェースを取得する */
		DEVICE_REQUEST_CALLBACK set_interface;			/* インターフェースを決定する */
		DEVICE_REQUEST_CALLBACK synch_frame;			/* 同期を取るためにフレーム番号を取得する */
	//} standard_request;
	//→構造体のネストはやめました。理由は上のコメントを参照してください。
	DEVICE_REQUEST_CALLBACK class_request;
	DEVICE_REQUEST_CALLBACK vendor_request;
} USBINF;

/* ★★★重要!!★★★
 *
 * ユーザー定義のep2_rxdone()は、次のように実装してください。
 *
 *	<例: NONISOモードの場合> ※ISOOUT/ISOIN/ISOIOモードの場合も同様です。
 *
 *		while((len = D12_ReadEndpoint(4, dat, 64)) != -1／＊受信未完了＊／) {
 *			／＊ ここでdatを処理する。 ＊／
 *		}
 *
 * メインエンドポイントはダブルバッファ構成になっているので、一度に二つのパケットを受信できます。
 * USBCの受信完了割り込みは、①“受信バッファにパケットがある”場合に発行され続けるのではなく、
 *                           ②“新しいパケットが到着した瞬間”だけに発行されます。
 * なんらかの原因で、一つ目パケット受信完了に対する応答が遅れ、その間に二つ目のパケットも受信完了したとします。
 * ここでもし、一つ目パケットだけを処理して割り込みフラグをクリアすると、まだ処理していない二つ目のパケットの
 * 割り込みもまとめてクリアしてしまうことになります。
 *
 * USBCの割り込み条件が上述①の仕様だったならば、二つ目のパケットを処理せずに割り込みフラグをクリアしても、
 * すぐにまた割り込みフラグがセットされて、二つ目のパケットに対する割り込みが発生するのでしょうけれども、
 * 残念ながらPDIUSBD12の仕様は上述②であり、二つ目のパケットに対する割り込み要求は失われてしまいます。
 *
 * この問題を防ぐために、『すくなくとも、今回のD12_ReadLastTransactionStatus()によって割り込みフラグをクリア
 * した時点で到着していたパケットは、確実に全て処理する』ことが必要です。
 * すなわち、<例>のようなコードを記述してください。
 * 
 * パケット処理中に新しいパケットが到着した場合は、そのパケットも処理してしまって大丈夫です。
 * 新しいパケットに対応する割り込みフラグをクリアせずに、新しいパケットを処理することになり、
 * 次回、無駄な割り込みが一回発生するだけです。(次回のwhile(len=...)ループは、何もせずに抜ける)
 *
 * ↓2004/11/06 追記
 *
 * 上述のwhile()に相当する処理は、ライブラリ内で行うように変更しました。
 * 受信完了通知コールバックを呼び出す前に、ライブラリ内でD12_ReadEndpoint()を行い、コールバックにはデータと長さを渡します。
 * もう、メインエンドポイントがダブルバッファであることを意識する必要はありません。
 * 詳しくは、fn_usb_isr()の実装を参照してください。
 */

/* USBコントローラの制御をフックします。
 * [in]
 *	ui	USBフック情報へのポインタ
 *		usb_start()はポインタの指す内容をコピーして保持します。
 *		関数が処理を返したら、開放してしまっても構いません。
 * [note]
 *	* 既にフックされていた場合は、自動的にいったんフック解除してからフックします。
 *	  従って、usb_start()関数は常に成功します。
 *	* アプリケーション終了前に、必ず明示的にusb_stop()を呼んでフック解除してください。
 *	  usb_stop()を呼ばずにアプリケーション終了すると、システムが正しく動作しません。
 */
void usb_start(const USBINF* ui);

/* USBコントローラの制御をフック解除します。
 * [note]
 *	* まだフックされていない場合は、この関数は何もしません。
 */
void usb_stop();

/* エンドポイント0 IN(デバイス→ホスト)を使って、データを送信します。
 * [in]
 *	ptr	送信するデータの先頭アドレス
 *	len	送信するデータのバイト数
 * [note]
 *	* この関数は、デバイスリクエストへの応答としてのみ利用できます。
 *	  USBINF構造体に指定したデバイスリクエストハンドラがコールバックされた時に、
 *	  コールバックの中からだけ利用可能です。具体的な用途は、次のとおりです。
 *	  - GETリクエストに対するコントロールデータ返信
 *	  - SETリクエストに対するステータス(空パケット)返信
 *	  これら以外の目的・タイミングで利用すると、予測できない結果となります。
 *	* 送信処理は非同期に行われます。
 *	  送信処理が完了するまで、ptrの指すデータが参照され続けます。
 *	  データを開放/変更しないよう気をつけてください。
 *	  次のデバイスリクエストを受信したら、データを開放/変更しても安全です。
 */
void usb_ep0_tx(const void* ptr, int len);

/* エンドポイント0 IN/OUT をストールさせます。
 * [note]
 *	* この関数は、デバイスリクエストへの応答としてのみ利用できます。
 *	  USBINF構造体に指定したデバイスリクエストハンドラがコールバックされた時に、
 *	  コールバックの中からだけ利用可能です。具体的な用途は、次のとおりです。
 *	  - 不正なデバイスリクエストに対する否定応答
 *	  これ以外の目的・タイミングで利用すると、予測できない結果となります。
 */
void usb_ep0_stall();

/****************************************************************************
 *	ユーティリティ
 ****************************************************************************/

/* クラス定義のディスクリプタをバイト配列として定義するときに、16ビット以上の値をアライメント無しでバイト配列に埋め込むためのマクロです。
 * USB_UINT48(),USB_UINT64(),USB_UINT128()の引数は、(下位ワード,上位ワード)の順であることに注意してください。
 * 使用例は、clipptp.cなどを参照してください。 */
#define USB_UINT16( a)		((uint8_t)(a)),((uint8_t)((a)>>8))
#define USB_UINT24( a)		((uint8_t)(a)),((uint8_t)((a)>>8)),((uint8_t)((a)>>16))		//<使用例>Audio Device Class⇒Type I Format Type Descriptor⇒tSamFreq
#define USB_UINT32( a)		((uint8_t)(a)),((uint8_t)((a)>>8)),((uint8_t)((a)>>16)),((uint8_t)((a)>>24))
#define USB_UINT64( a,b)	USB_UINT32(a  ),USB_UINT32(b  )					//<使用例>Picture Transfer Protocol⇒StorageInfo Dataset⇒MaxCapacity
#define USB_UINT128(a,b,c,d)	USB_UINT64(a,b),USB_UINT64(c,d)					//<使用例>Media Transfer Protocol⇒ObjectPropDesc dataset⇒PersistentUniqueObjectIdentifier⇒DefaultValue

#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus

#endif /*__CLIP_USB_H__*/
