#include "reg3067.h"
#include "usb.h"

#define DATA0_WR    0x07   // (Arm+Enable+tranmist to Host+DATA0)
#define DATA1_WR    0x47   // (Arm+Enable+tranmist to Host on DATA1)
#define ZDATA0_WR   0x05   // (Arm+Transaction Ignored+tranmist to Host+DATA0)
#define ZDATA1_WR   0x45   // (Arm+Transaction Ignored+tranmist to Host+DATA1)
#define DATA0_RD    0x03   // (Arm+Enable+received from Host+DATA0)
#define DATA1_RD    0x43   // (Arm+Enable+received from Host+DATA1)

#define PID_SETUP   0xd0
#define PID_SOF     0x50
#define PID_IN      0x90
#define PID_OUT     0x10

#define SL11H_HOSTCTLREG        0
#define SL11H_BUFADDRREG        1
#define SL11H_BUFLNTHREG        2
#define SL11H_PKTSTATREG        3	/* read */
#define SL11H_PIDEPREG          3	/* write */
#define SL11H_XFERCNTREG        4	/* read */
#define SL11H_DEVADDRREG        4	/* write */
#define SL11H_CTLREG1           5
#define SL11H_INTENBLREG        6

#define SL11H_HOSTCTLREG_B      8
#define SL11H_BUFADDRREG_B      9
#define SL11H_BUFLNTHREG_B      10
#define SL11H_PKTSTATREG_B      11	/* read */
#define SL11H_PIDEPREG_B        11	/* write */
#define SL11H_XFERCNTREG_B      12	/* read */
#define SL11H_DEVADDRREG_B      12	/* write */

#define SL11H_INTSTATREG        13	/* write clears bitwise */
#define SL11H_HWREVREG          14	/* read */
#define SL11H_SOFLOWREG         14	/* write */
#define SL11H_SOFTMRREG         15	/* read */
#define SL11H_CTLREG2           15	/* write */
#define SL11H_DATA_START        16

/* Host control register bits (addr 0) */

#define SL11H_HCTLMASK_ARM      1
#define SL11H_HCTLMASK_ENBLEP   2
#define SL11H_HCTLMASK_WRITE    4
#define SL11H_HCTLMASK_ISOCH    0x10
#define SL11H_HCTLMASK_AFTERSOF 0x20
#define SL11H_HCTLMASK_SEQ      0x40
#define SL11H_HCTLMASK_PREAMBLE 0x80

/* Packet status register bits (addr 3) */

#define SL11H_STATMASK_ACK      1
#define SL11H_STATMASK_ERROR    2
#define SL11H_STATMASK_TMOUT    4
#define SL11H_STATMASK_SEQ      8
#define SL11H_STATMASK_SETUP    0x10
#define SL11H_STATMASK_OVF      0x20
#define SL11H_STATMASK_NAK      0x40
#define SL11H_STATMASK_STALL    0x80

/* Control register 1 bits (addr 5) */
#define SL11H_CTL1MASK_DSBLSOF  1
#define SL11H_CTL1MASK_NOTXEOF2 4
#define SL11H_CTL1MASK_DSTATE   0x18
#define SL11H_CTL1MASK_NSPD     0x20
#define SL11H_CTL1MASK_SUSPEND  0x40
#define SL11H_CTL1MASK_CLK12    0x80

#define SL11H_CTL1VAL_RESET     8

/* Interrut enable (addr 6) and interrupt status register bits (addr 0xD) */

#define SL11H_INTMASK_XFERDONE  1
#define SL11H_INTMASK_SOFINTR   0x10
#define SL11H_INTMASK_INSRMV    0x20
#define SL11H_INTMASK_USBRESET  0x40
#define SL11H_INTMASK_DSTATE    0x80	/* only in status reg */

/* HW rev and SOF lo register bits (addr 0xE) */

#define SL11H_HWRMASK_HWREV     0xF0

/* SOF counter and control reg 2 (addr 0xF) */

#define SL11H_CTL2MASK_SOFHI    0x3F
#define SL11H_CTL2MASK_DSWAP    0x40
#define SL11H_CTL2MASK_HOSTMODE 0xae

#define USB_RESET	46
#define USB_SETUP	47
#define USB_GETSPEED	48
 #define USB_LOW	1
 #define USB_FULL	2

#define USB_EP		1
#define USB_TOGGLE	2
#define USB_HDRSIZ	2


#define SL_NUM	1
typedef struct {
	volatile char	*addr_reg;
	volatile char	*data_reg;
	char		speed;
	char		addr;
} SL811Info;
static SL811Info sl811[SL_NUM];
#define USB_ACK		1
#define USB_NAK		2
#define USB_STALL	4

static void waitms(int ms) {
	volatile int	a, b;

	for(a = 0;a < ms;a++) {
		for(b = 0;b < 2000;b++);
	}
}

static void waitus(int us) {
	volatile int	a;

	for(a = 0;a < us*2;a++);
}

static char SL811Read(int num, char offset) {
	char data;

	*(sl811[num].addr_reg) = offset;
	data = *(sl811[num].data_reg);
	return data;
}

static void SL811Write(int num, char offset, char data){
	*(sl811[num].addr_reg) = offset;
	*(sl811[num].data_reg) = data;
}

static void SL811BufRead(int num, short offset, char* buf, short size){
	if(size <= 0) return;
	*(sl811[num].addr_reg) = (char)offset;
	while(size--) *buf++ = *(sl811[num].data_reg);
}

static void SL811BufWrite(int num, short offset, char* buf, short size) {
	if(size <= 0) return;
	*(sl811[num].addr_reg) = (char)offset;
	while(size--) {
		*(sl811[num].data_reg) = *buf;
		buf++;
	}
}

static int regTest(int num){
	int	i, data, result = 0;
	char	buf[256];

	for(i = 16;i < 256; i++) {
		buf[i] = (char)SL811Read(num, i);
		SL811Write(num, i, i);
	}
	for(i = 16;i < 256; i++) {
		data = SL811Read(num, i);
		if(data != i) {
			result = -1;
		}
	}
	for(i = 16;i < 256;i++) SL811Write(num, i, buf[i]);
	return result;
}

static int USBReset(int num) {
	int	status;

	SL811Write(num, SL11H_CTLREG2, 0xae);
	SL811Write(num, SL11H_CTLREG1, 0x08);	// reset USB
	waitms(20);				// 20ms
	SL811Write(num, SL11H_CTLREG1, 0);	// remove SE0

	for(status = 0;status < 100;status++) {
		SL811Write(num, SL11H_INTSTATREG, 0xff); // clear all interrupt bits
	}
	status = SL811Read(num, SL11H_INTSTATREG);
	if(status & 0x40){  // Check if device is removed
		sl811[num].speed = 0;	// None
		SL811Write(num, SL11H_INTENBLREG, SL11H_INTMASK_XFERDONE | 
			   SL11H_INTMASK_SOFINTR | SL11H_INTMASK_INSRMV);
		return -1;
	}
	SL811Write(num, SL11H_BUFLNTHREG_B, 0);	//zero lenth
	SL811Write(num, SL11H_PIDEPREG_B, 0x50);	//send SOF to EP0
	SL811Write(num, SL11H_DEVADDRREG_B, 0x01);	//address0
	SL811Write(num, SL11H_SOFLOWREG, 0xe0);
	if(!(status & 0x80)) {
		sl811[num].speed = USB_LOW;	// Low
		SL811Write(num, SL11H_CTLREG1, 0x8);
		waitms(20);
		SL811Write(num, SL11H_SOFTMRREG, 0xee);
		SL811Write(num, SL11H_CTLREG1, 0x21);
		SL811Write(num, SL11H_HOSTCTLREG_B, 0x01);
		for(status = 0;status < 20;status++) {
			SL811Write(num, SL11H_INTSTATREG, 0xff);
		}
	} else {
		sl811[num].speed = USB_FULL;	// Full
		SL811Write(num, SL11H_CTLREG1, 0x8);
		waitms(20);
		SL811Write(num, SL11H_SOFTMRREG, 0xae);
		SL811Write(num, SL11H_CTLREG1, 0x01 );
		SL811Write(num, SL11H_HOSTCTLREG_B, 0x01);
		SL811Write(num, SL11H_INTSTATREG, 0xff);
	}
	SL811Write(num, SL11H_INTENBLREG, SL11H_INTMASK_XFERDONE | 
		   SL11H_INTMASK_SOFINTR|SL11H_INTMASK_INSRMV);
	return 0;
}

static int write_setup(int num, char* data) {
	char	pktstat, ret;

	SL811BufWrite(num, 0x10, data, 8);
	SL811Write(num, SL11H_BUFADDRREG, 0x10);
	SL811Write(num, SL11H_BUFLNTHREG, 8);
	SL811Write(num, SL11H_DEVADDRREG, sl811[num].addr);
	SL811Write(num, SL11H_PIDEPREG, PID_SETUP);
	SL811Write(num, SL11H_HOSTCTLREG, DATA0_WR);
	waitus(200);
	while(SL811Read(num, SL11H_INTSTATREG) & SL11H_INTMASK_XFERDONE == 0);
	pktstat = SL811Read(num, SL11H_PKTSTATREG);
	ret = 0;
	if(pktstat & SL11H_STATMASK_ACK) ret |= USB_ACK;
	if(pktstat & SL11H_STATMASK_NAK) ret |= USB_NAK;
	if(pktstat & SL11H_STATMASK_STALL) ret |= USB_STALL;
	return ret;
}

int write_sl811(int num, char* data, int size) {
	char	pktstat, ret;

	if(size < USB_HDRSIZ) return 0;
	SL811BufWrite(num, 0x10, &data[USB_HDRSIZ], size - USB_HDRSIZ);
	SL811Write(num, SL11H_BUFADDRREG, 0x10);
	SL811Write(num, SL11H_BUFLNTHREG, size - USB_HDRSIZ);
	SL811Write(num, SL11H_DEVADDRREG, sl811[num].addr);
	SL811Write(num, SL11H_PIDEPREG, PID_OUT | data[USB_EP]);
	SL811Write(num, SL11H_HOSTCTLREG, (data[USB_TOGGLE] == 0) ? DATA0_WR : DATA1_WR);
	waitus(200);
	while(SL811Read(num, SL11H_INTSTATREG) & SL11H_INTMASK_XFERDONE == 0);
	pktstat = SL811Read(num, SL11H_PKTSTATREG);
	ret = 0;
	if(pktstat & SL11H_STATMASK_ACK) ret |= USB_ACK;
	if(pktstat & SL11H_STATMASK_NAK) ret |= USB_NAK;
	if(pktstat & SL11H_STATMASK_STALL) ret |= USB_STALL;
	return ret;
}

int read_sl811(int num, char* data, int size) {
	char	pktstat, ret;
	int	timovr;

	if(size < 0) return 0;
	SL811Write(num, SL11H_BUFADDRREG, 0x10);
	SL811Write(num, SL11H_BUFLNTHREG, size);
	SL811Write(num, SL11H_DEVADDRREG, sl811[num].addr);
	SL811Write(num, SL11H_PIDEPREG, PID_IN | data[USB_EP]);
	for(timovr = 0;timovr < 200;timovr++) {
		SL811Write(num, SL11H_HOSTCTLREG, (data[USB_TOGGLE] == 0) ? DATA0_RD : DATA1_RD);
		waitus(200);
		while(SL811Read(num, SL11H_INTSTATREG) & SL11H_INTMASK_XFERDONE == 0);
		pktstat = SL811Read(num, SL11H_PKTSTATREG);
		if(!(pktstat & SL11H_STATMASK_NAK)) break;
	}
	if(pktstat & SL11H_STATMASK_ACK) SL811BufRead(num, 0x10, data, size);
	ret = 0;
	if(pktstat & SL11H_STATMASK_ACK) ret |= USB_ACK;
	if(pktstat & SL11H_STATMASK_NAK) ret |= USB_NAK;
	if(pktstat & SL11H_STATMASK_STALL) ret |= USB_STALL;
	return ret;
}

int seek_sl811(int num, int position) {
	if(num >= SL_NUM) return -1;
	sl811[num].addr = position;
	return 0;
}

int ioctl_sl811(int num, int data, int op) {
	static char	getdesc[8] = {0x80, 0x06, 0, 1, 0, 0, 64, 0};
	static char	setaddr[8] = {0x00, 0x05, 2, 0, 0, 0, 0, 0};
	char		buf[10];
	int		ret;

	if(num >= SL_NUM) return -1;
	ret = 0;
	switch(op) {
	case USB_RESET:
		ret = -1;
		sl811[num].addr = 0;
		if(USBReset(num) != 0) break;
		if((write_setup(num, getdesc) & USB_ACK) == 0) break;
		buf[USB_EP] = 0, buf[USB_TOGGLE] = 1;
		if((read_sl811(num, buf, 8) & USB_ACK) == 0) break;
		buf[USB_EP] = 0, buf[USB_TOGGLE] = 1;
		if((write_sl811(num, buf, 2) & USB_ACK) == 0) break;
		if((write_setup(num, setaddr) & USB_ACK) == 0) break;
		buf[USB_EP] = 0, buf[USB_TOGGLE] = 1;
		if((read_sl811(num, buf, 0) & USB_ACK) == 0) break;
		sl811[num].addr = data;
		ret = buf[7];
		break;
	case USB_GETSPEED:
		ret = sl811[num].speed;
		break;
	case USB_SETUP:
		ret = write_setup(num, (char*)data);
		break;
	default:
		ret = -1;
	}
	return ret;
}

void get_desc(int num, short type, char *data, int datasize, int epsize, char index, short lang) {
	SetupPKG	command;
	char		toggle, buf[2];
	int		i, remain, size, offset;

	command.bmRequest = TRANS_TO_HOST;
	command.bRequest = GET_DESCRIPTOR;
	command.wValue = htole16((type << 8) | index);
	command.wIndex = lang;
	command.wLength = htole16(datasize);
	ioctl_sl811(num, (int)&command, USB_SETUP);
	offset = 0;
	toggle = 1;
	buf[USB_TOGGLE] = 1;
	for(remain = datasize;remain > 0;remain -= size) {
		size = (remain > epsize) ? epsize : remain;
		if(size < USB_HDRSIZ) {
			buf[USB_EP] = 0;
			read_sl811(num, buf, size);
			data[offset] = buf[0];
		} else {
			data[offset + USB_EP] = 0;
			data[offset + USB_TOGGLE] = toggle;
			read_sl811(num, &data[offset], size);
		}
		if(offset == 0 && data[1] != CONFIG_TYPE) remain = (int)data[0] & 0xff;
		toggle = (toggle == 0) ? 1 : 0;
		offset += size;		
	}
	buf[USB_EP] = 0;
	buf[USB_TOGGLE] = 1;
	write_sl811(num, buf, 2);
}

int main() {
	int		num, i, ep, cfgsiz, offset;
	char		buffer[128], *p;
	DevDesc		device;
	CfgDesc		config;
	StrDesc		string;
	IntfDesc	interface;
	EPDesc		endpoint[MAX_EP];
	int		epsize, epnum;
	short		vendor, product;
	char		class, subclass, protocol;

	/* BUS  initialize */
	P1DDR = 0xff;	/* A0-7    is enable */
	P2DDR = 0xff;	/* A8-15   is enable */
	P5DDR = 0x01;	/* A16     is enable */
	P8DDR = 0x0e;	/* CS1-3 is enable */
	sio_init();
	sio_speed(57600, 25);
	num = 0;
	sl811[num].addr_reg = (volatile char *)0x200001;
	sl811[num].data_reg = (volatile char *)0x200003;
	sl811[num].addr = 0;
	if(regTest(num) != 0) {
		print("USB not found!\n");
		for(;;);
	}
	epsize = ioctl_sl811(num, 2, USB_RESET);
	if(epsize == -1) {
		print("Device not found!\n");
		for(;;);
	}
	print("Size of endpoint is %d[byte]", epsize);
	get_desc(num, DEVICE_TYPE, (char*)&device, sizeof(DevDesc), epsize, 0, 0);
	vendor = le16toh(device.idVendor);
	product = le16toh(device.idProduct);
	print("\nDevice desc : ");
	p = (char*)&device;
	for(i = 0;i < sizeof(DevDesc);i++) print("%02x ", (int)p[i] & 0xff);
	get_desc(num, CONFIG_TYPE, (char*)&config, sizeof(CfgDesc), epsize, 0, 0);
	cfgsiz = le16toh(config.wLength);
	if(cfgsiz > 128) cfgsiz = 128;
	print("\nConfiguration desc : ");
	p = (char*)&config;
	for(i = 0;i < sizeof(CfgDesc);i++) print("%02x ", (int)p[i] & 0xff);
	if(device.iProduct != 0) {
		get_desc(num, STRING_TYPE, (char*)&string, sizeof(StrDesc), epsize, 0, 0);
		print("\nString desc : ");
		p = (char*)&string;
		for(i = 0;i < sizeof(StrDesc);i++) print("%02x ", (int)p[i] & 0xff);
		get_desc(num, STRING_TYPE, buffer, 128, epsize, device.iProduct, le16toh(string.wLang));
		print("\n[");
		p = (char*)&string;
		for(i = 2;i < ((int)buffer[0] & 0xff);i+=2) print("%c", buffer[i]);
		print("]");
	}
	get_desc(num, CONFIG_TYPE, buffer, cfgsiz, epsize, 0, 0);
	offset = sizeof(CfgDesc);
	memcpy((char*)&interface, &buffer[offset], sizeof(IntfDesc));
	print("\nInterface desc : ");
	p = (char*)&interface;
	for(i = 0;i < sizeof(IntfDesc);i++) print("%02x ", (int)p[i] & 0xff);
	offset += sizeof(IntfDesc);
	epnum = interface.bEndPoints;
	class = interface.iClass;
	subclass = interface.iSub;
	protocol = interface.iProto;
	for(ep = 0;ep < epnum;ep++) {
		if(ep == MAX_EP) break;
		memcpy((char*)&endpoint[ep], &buffer[offset], sizeof(EPDesc));
		offset += sizeof(EPDesc);
		print("\nEndpoint desc(%d) : ", ep);
		p = (char*)&endpoint[ep];
		for(i = 0;i < sizeof(EPDesc);i++) print("%02x ", (int)p[i] & 0xff);
	}
	print("\nEndpoint number is %d\n", epnum);
	print("Vendor %d product %d Class %d Sub %d Proto %d\n",
		(int)vendor, (int)product, (int)class & 0xff, (int)subclass & 0xff, (int)protocol & 0xff);
	for(;;);
}
