summaryrefslogtreecommitdiff
path: root/sw/Core/Src/webusb.h
blob: 495acd7be3f9d358a2615d6ce9e158037274059b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#pragma once
#ifdef DEBUG
#define DEBUG_WU
#endif
#ifdef DEBUG_WU
#define WUDebugLog DebugLog
#else
#define WUDebugLog(...)
#endif

#ifdef EMU
static inline uint32_t tud_vendor_write_available(void) { return 0; }
static inline uint32_t tud_vendor_available(void) { return 0; }
static inline uint32_t tud_vendor_read(void* buffer, uint32_t bufsize) { return 0; }
static inline uint32_t tud_vendor_write(const void* buffer, uint32_t bufsize) { return 0; }
#else
uint32_t tud_vendor_n_write_available (uint8_t itf);
uint32_t tud_vendor_n_available (uint8_t itf);
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
static inline uint32_t tud_vendor_write_available (void)
{
  return tud_vendor_n_write_available(0);
}
static inline uint32_t tud_vendor_available (void)
{
  return tud_vendor_n_available(0);
}
static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize)
{
  return tud_vendor_n_read(0, buffer, bufsize);
}
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize)
{
  return tud_vendor_n_write(0, buffer, bufsize);
}

#endif
/* webusb wire format. 10 byte header, then data. 
u32 magic = 0xf30fabca
u8 cmd // 0 = get, 1=set
u8 idx // 0
u8 idx2 // 0 
u8 idx3 // 0
u16 datalen // in bytes, not including this header, 0 for get, 1552 for set
*/
typedef struct WebUSBHeader { // 10 byte header
	u8 magic[4];
	u8 cmd,idx;
	u16 offset;
	u16 len;
} WebUSBHeader;
const static u8 wu_magic[4] = { 0xf3,0x0f,0xab,0xca }; // we expect magic to be these
const static u8 wu_magic_ext[4] = { 0xf4,0x1f,0xcb,0xdb }; // we expect magic to be these
#define WEB_USB_TIMEOUT 500
enum { // state machine ticks thru these in order, more or less
	WU_MAGIC0,
	WU_MAGIC1,
	WU_MAGIC2,
	WU_MAGIC3,
	WU_RECVHDR,
	WU_RECVDATA,
	WU_SENDHDR,
	WU_SENDDATA,
};
u8 wu_state = WU_MAGIC0; // current state
WebUSBHeader wu_hdr; // header of current command
u8* wu_data=(u8*)&wu_hdr; // buffer where we are reading/writing atm
int wu_len=1; // how much left to read/write before state transition
int wu_lasteventtime; // for timeout detection
void SetWUState(u8 state, u8* data, int len) { wu_state = state; wu_data = data; wu_len = len;  }
void PumpWebUSB() {
	while (1) {
		if (millis() > wu_lasteventtime + WEB_USB_TIMEOUT) { // timeout!
		reset:		
			SetWUState(WU_MAGIC0, wu_hdr.magic, 1);
		}
		int n;
		if (wu_state < WU_SENDHDR) { // receive
			//n = tud_vendor_available();
			//if (n<6) break;
			n = tud_vendor_read(wu_data, wu_len);
		}
		else { // send
			if (wu_len <= 0) {
				// odd corner case. the state doesnt want to send anything. just pretend the state is done
				goto statedone;
			}
			n = tud_vendor_write_available();
			if (n > wu_len) n = wu_len;
			if (n <= 0) break;
			n = tud_vendor_write(wu_data, n);
		}
		if (n <= 0) break; // nothing to do
#ifdef DEBUG_WU
		WUDebugLog("%d (%d): ", wu_state,wu_len);
		for (int i=0;i<n;++i) WUDebugLog("%02x ", wu_data[i]);
		WUDebugLog("\r\n");
#endif
		wu_lasteventtime = millis();
		wu_len -= n;
		wu_data += n;
		if (wu_len > 0) break; // not done with this state yet. next time!
		// ok! end of a state. whats next
statedone:
		switch (wu_state) {
		case WU_MAGIC0: case WU_MAGIC1: case WU_MAGIC2: case WU_MAGIC3: {
			u8 m = wu_hdr.magic[wu_state];
			if (m != wu_magic[wu_state]) {
				WUDebugLog("magic fail %02x %02x\r\n",m, wu_magic[wu_state]);
				wu_hdr.magic[0] = m;
				wu_len = 1;
				wu_state = (m == wu_magic[0]) ? WU_MAGIC1 : WU_MAGIC0; // if we got the first byte, we can tick into next state!
				wu_data = wu_hdr.magic + wu_state;
				continue;
			}
			wu_state++;
			wu_len = 1;
			if (wu_state == WU_RECVHDR) { // time to get rest of header
				WUDebugLog("get header!\r\n");
				wu_len = 10-4;
			}
			continue; }
		case WU_RECVHDR: // ok we got the header. what do they want:
			WUDebugLog("got header %d %d!\r\n", wu_hdr.cmd,wu_hdr.len);
			if (wu_hdr.cmd > 1) goto reset; // invalid cmd?
			if (wu_hdr.cmd == 0) {
				// they asked to get a preset!
				if (wu_hdr.len + wu_hdr.offset > sizeof(Preset) ) goto reset;
				if (wu_hdr.offset >= sizeof(Preset)) goto reset;
				if (wu_hdr.idx >= 32) wu_hdr.idx = sysparams.curpreset;
				wu_hdr.cmd = 1;
				if (wu_hdr.len == 0) 
					wu_hdr.len = sizeof(Preset) - wu_hdr.offset;
				SetWUState(WU_SENDHDR, (u8*)&wu_hdr, 10);
			}
			else {
				// they asked to set!
				if (wu_hdr.len + wu_hdr.offset > sizeof(Preset) || wu_hdr.len<=0) goto reset;
				if (wu_hdr.offset >= sizeof(Preset)) goto reset;
				if (wu_hdr.idx >= 32) wu_hdr.idx = sysparams.curpreset; // idx>32, just use current
				// switch to the specified preset ready to receive.
				SetPreset(wu_hdr.idx, false);
				SetWUState(WU_RECVDATA, ((u8*)&rampreset) + wu_hdr.offset, wu_hdr.len);
			}
			continue;
		case WU_RECVDATA:
			if (wu_hdr.cmd == 1) {
				// finished receiving preset. mark it dirty 
				ramtime[GEN_PRESET] = millis();
			}
			goto reset;
		case WU_SENDHDR:// ok we sent them the header; now send them the data
		{
			u8* data = (wu_hdr.idx == sysparams.curpreset) ? (u8 * )&rampreset : (u8*)GetSavedPreset(wu_hdr.idx);
			SetWUState(WU_SENDDATA, data + wu_hdr.offset, wu_hdr.len);
			continue;
		}
		case WU_SENDDATA: // ok we finished sending our data. nothing to do except get ready for more.
			goto reset;
		} // state
	} // while loop
}