endpoint0.cpp
9.52 KB
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#include "endpoint0.h"
#include <string.h>
#include <ion/src/device/regs/regs.h>
#include "device.h"
#include "interface.h"
#include "request_recipient.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
namespace Ion {
namespace USB {
namespace Device {
void Endpoint0::setup() {
// Setup the IN direction
// Reset the device IN endpoint 0 transfer size register
class OTG::DIEPTSIZ0 dieptsiz0(0);
/* Transfer size. The core interrupts the application only after it has
* exhausted the transfer size amount of data. The transfer size is set to the
* maximum packet size, to be interrupted at the end of each packet. */
dieptsiz0.setXFRSIZ(k_maxPacketSize);
OTG.DIEPTSIZ0()->set(dieptsiz0);
// Reset the device IN endpoint 0 control register
class OTG::DIEPCTL0 diepctl0(0); // Reset value
// Set the maximum packet size
diepctl0.setMPSIZ(OTG::DIEPCTL0::MPSIZ::Size64);
// Set the NAK bit: all IN transactions on endpoint 0 receive a NAK answer
diepctl0.setSNAK(true);
// Enable the endpoint
diepctl0.setEPENA(true);
OTG.DIEPCTL0()->set(diepctl0);
// Setup the OUT direction
setupOut();
// Set the NAK bit
OTG.DOEPCTL0()->setSNAK(true);
// Enable the endpoint
enableOut();
// Setup the Tx FIFO
/* Tx FIFO depth
* We process each packet as soon as it arrives, so we only need
* k_maxPacketSize bytes. TX0FD being in terms of 32-bit words, we divide
* k_maxPacketSize by 4. */
OTG.DIEPTXF0()->setTX0FD(k_maxPacketSize/4);
/* Tx FIFO RAM start address. It starts just after the Rx FIFOso the value is
* Rx FIFO start address (0) + Rx FIFO depth. the Rx FIFO depth is set in
* usb.cpp, but because the code is linked separately, we cannot get it. */
OTG.DIEPTXF0()->setTX0FSA(128);
}
void Endpoint0::setupOut() {
class OTG::DOEPTSIZ0 doeptsiz0(0);
// Number of back-to-back SETUP data packets the endpoint can receive
doeptsiz0.setSTUPCNT(1);
// Packet count, false if a packet is written into the Rx FIFO
doeptsiz0.setPKTCNT(true);
/* Transfer size. The core interrupts the application only after it has
* exhausted the transfer size amount of data. The transfer size is set to the
* maximum packet size, to be interrupted at the end of each packet. */
doeptsiz0.setXFRSIZ(64);
OTG.DOEPTSIZ0()->set(doeptsiz0);
}
void Endpoint0::setOutNAK(bool nak) {
m_forceNAK = nak;
/* We need to keep track of the NAK state of the endpoint to use the value
* after a setupOut in poll() of device.cpp. */
if (nak) {
OTG.DOEPCTL0()->setSNAK(true);
} else {
OTG.DOEPCTL0()->setCNAK(true);
}
}
void Endpoint0::enableOut() {
OTG.DOEPCTL0()->setEPENA(true);
}
void Endpoint0::reset() {
flushTxFifo();
flushRxFifo();
}
void Endpoint0::readAndDispatchSetupPacket() {
setOutNAK(true);
// Read the 8-bytes Setup packet
if (readPacket(m_largeBuffer, sizeof(SetupPacket)) != sizeof(SetupPacket)) {
stallTransaction();
return;
};
m_request = SetupPacket(m_largeBuffer);
uint16_t maxBufferLength = MIN(m_request.wLength(), MaxTransferSize);
// Forward the request to the request recipient
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
if (type == 0) {
// Device recipient
m_requestRecipients[0]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
} else {
// Interface recipient
m_requestRecipients[1]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
}
}
void Endpoint0::processINpacket() {
switch (m_state) {
case State::DataIn:
sendSomeData();
break;
case State::LastDataIn:
m_state = State::StatusOut;
// Prepare to receive the OUT Data[] transaction.
setOutNAK(false);
break;
case State::StatusIn:
{
m_state = State::Idle;
// All the data has been received. Callback the request recipient.
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
if (type == 0) {
// Device recipient
m_requestRecipients[0]->wholeDataReceivedCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
} else {
// Interface recipient
m_requestRecipients[1]->wholeDataReceivedCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
}
}
break;
default:
stallTransaction();
}
}
void Endpoint0::processOUTpacket() {
switch (m_state) {
case State::DataOut:
if (receiveSomeData() < 0) {
break;
}
if ((m_request.wLength() - m_transferBufferLength) <= k_maxPacketSize) {
m_state = State::LastDataOut;
}
break;
case State::LastDataOut:
if (receiveSomeData() < 0) {
break;
}
// Send the DATA1[] to the host.
writePacket(NULL, 0);
m_state = State::StatusIn;
break;
case State::StatusOut:
{
// Read the DATA1[] sent by the host.
readPacket(NULL, 0);
m_state = State::Idle;
// All the data has been sent. Callback the request recipient.
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
if (type == 0) {
// Device recipient
m_requestRecipients[0]->wholeDataSentCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
} else {
// Interface recipient
m_requestRecipients[1]->wholeDataSentCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
}
}
break;
default:
stallTransaction();
}
}
void Endpoint0::flushTxFifo() {
// Set IN endpoint NAK
OTG.DIEPCTL0()->setSNAK(true);
// Wait for core to respond
while (!OTG.DIEPINT(0)->getINEPNE()) {
}
// Get the Tx FIFO number
uint32_t fifo = OTG.DIEPCTL0()->getTXFNUM();
// Wait for AHB idle
while (!OTG.GRSTCTL()->getAHBIDL()) {
}
// Flush Tx FIFO
OTG.GRSTCTL()->setTXFNUM(fifo);
OTG.GRSTCTL()->setTXFFLSH(true);
// Reset packet counter
OTG.DIEPTSIZ0()->set(0);
// Wait for the flush
while (OTG.GRSTCTL()->getTXFFLSH()) {
}
}
void Endpoint0::flushRxFifo() {
// Set OUT endpoint NAK
OTG.DOEPCTL0()->setSNAK(true);
// Wait for AHB idle
while (!OTG.GRSTCTL()->getAHBIDL()) {
}
// Flush Rx FIFO
OTG.GRSTCTL()->setRXFFLSH(true);
// Reset packet counter
OTG.DOEPTSIZ0()->set(0);
// Wait for the flush
while (OTG.GRSTCTL()->getRXFFLSH()) {
}
}
void Endpoint0::discardUnreadData() {
for (int i = 0; i < m_receivedPacketSize; i += 4) {
OTG.DFIFO0()->get();
}
m_receivedPacketSize = 0;
}
void Endpoint0::sendSomeData() {
if (k_maxPacketSize < m_transferBufferLength) {
// More than one packet needs to be sent
writePacket(m_largeBuffer + m_bufferOffset, k_maxPacketSize);
m_state = State::DataIn;
m_bufferOffset += k_maxPacketSize;
m_transferBufferLength -= k_maxPacketSize;
return;
}
// Last data packet sent
writePacket(m_largeBuffer + m_bufferOffset, m_transferBufferLength);
if (m_zeroLengthPacketNeeded) {
m_state = State::DataIn;
} else {
m_state = State::LastDataIn;
}
m_bufferOffset = 0;
m_zeroLengthPacketNeeded = false;
m_transferBufferLength = 0;
}
void Endpoint0::clearForOutTransactions(uint16_t wLength) {
m_transferBufferLength = 0;
m_state = (wLength > k_maxPacketSize) ? State::DataOut : State::LastDataOut;
setOutNAK(false);
}
uint16_t Endpoint0::receiveSomeData() {
// If it is the first chunk of data to be received, m_transferBufferLength is 0.
uint16_t packetSize = MIN(k_maxPacketSize, m_request.wLength() - m_transferBufferLength);
uint16_t sizeOfPacketRead = readPacket(m_largeBuffer + m_transferBufferLength, packetSize);
if (sizeOfPacketRead != packetSize) {
stallTransaction();
return -1;
}
m_transferBufferLength += packetSize;
return packetSize;
}
uint16_t Endpoint0::readPacket(void * buffer, uint16_t length) {
uint32_t * buffer32 = (uint32_t *) buffer;
uint16_t buffer32Length = MIN(length, m_receivedPacketSize);
int i;
// The RX FIFO is read 4 bytes by 4 bytes
for (i = buffer32Length; i >= 4; i -= 4) {
*buffer32++ = OTG.DFIFO0()->get();
m_receivedPacketSize -= 4;
}
if (i) {
/* If there are remaining bytes that should be read, read the next 4 bytes
* and copy only the wanted bytes. */
uint32_t extraData = OTG.DFIFO0()->get();
memcpy(buffer32, &extraData, i);
if (m_receivedPacketSize < 4) {
m_receivedPacketSize = 0;
} else {
m_receivedPacketSize -= 4;
}
}
return buffer32Length;
}
uint16_t Endpoint0::writePacket(const void * buffer, uint16_t length) {
const uint32_t * buffer32 = (uint32_t *) buffer;
// Return if there is already a packet waiting to be read in the TX FIFO
if (OTG.DIEPTSIZ0()->getPKTCNT()) {
return 0;
}
// Enable transmission
class OTG::DIEPTSIZ0 dieptsiz0(0);
// Indicate that the Transfer Size is one packet
dieptsiz0.setPKTCNT(1);
// Indicate the length of the Transfer Size
dieptsiz0.setXFRSIZ(length);
OTG.DIEPTSIZ0()->set(dieptsiz0);
// Enable the endpoint
OTG.DIEPCTL0()->setEPENA(true);
// Clear the NAK bit
OTG.DIEPCTL0()->setCNAK(true);
// Copy the buffer to the TX FIFO by writing data 32bits by 32 bits.
for (int i = length; i > 0; i -= 4) {
OTG.DFIFO0()->set(*buffer32++);
}
return length;
}
void Endpoint0::stallTransaction() {
OTG.DIEPCTL0()->setSTALL(true);
m_state = State::Idle;
}
void Endpoint0::computeZeroLengthPacketNeeded() {
if (m_transferBufferLength
&& m_transferBufferLength < m_request.wLength()
&& m_transferBufferLength % k_maxPacketSize == 0)
{
m_zeroLengthPacketNeeded = true;
return;
}
m_zeroLengthPacketNeeded = false;
}
}
}
}