Numworks Epsilon  1.4.1
Graphing Calculator Operating System
dfu_interface.cpp
Go to the documentation of this file.
1 #include "dfu_interface.h"
2 #include "../regs/cm4.h"
3 #include "../regs/flash.h"
4 #include <string.h>
5 
6 namespace Ion {
7 namespace USB {
8 namespace Device {
9 
10 static inline uint32_t min(uint32_t x, uint32_t y) { return (x<y ? x : y); }
11 
12 void DFUInterface::StatusData::push(Channel * c) const {
13  c->push(m_bStatus);
14  c->push(m_bwPollTimeout[2]);
15  c->push(m_bwPollTimeout[1]);
16  c->push(m_bwPollTimeout[0]);
17  c->push(m_bState);
18  c->push(m_iString);
19 }
20 
21 void DFUInterface::StateData::push(Channel * c) const {
22  c->push(m_bState);
23 }
24 
25 void DFUInterface::wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {
26  if (request->bRequest() == (uint8_t) DFURequest::Download) {
27  // Handle a download request
28  if (request->wValue() == 0) {
29  // The request is a special command
30  switch (transferBuffer[0]) {
31  case (uint8_t) DFUDownloadCommand::SetAddressPointer:
32  setAddressPointerCommand(request, transferBuffer, *transferBufferLength);
33  return;
34  case (uint8_t) DFUDownloadCommand::Erase:
35  eraseCommand(transferBuffer, *transferBufferLength);
36  return;
37  default:
38  m_state = State::dfuERROR;
39  m_status = Status::errSTALLEDPKT;
40  return;
41  }
42  }
43  if (request->wValue() == 1) {
45  return;
46  }
47  if (request->wLength() > 0) {
48  // The request is a "real" download. Compute the writing address.
49  m_writeAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer;
50  // Store the received data until we copy it on the flash.
51  memcpy(m_largeBuffer, transferBuffer, *transferBufferLength);
52  m_largeBufferLength = *transferBufferLength;
53  m_state = State::dfuDNLOADSYNC;
54  }
55  }
56 }
57 
58 void DFUInterface::wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {
59  if (request->bRequest() == (uint8_t) DFURequest::GetStatus) {
60  // Do any needed action after the GetStatus request.
61  if (m_state == State::dfuMANIFEST) {
62  // Leave DFU routine: Leave DFU, reset device, jump to application code
63  leaveDFUAndReset();
64  } else if (m_state == State::dfuDNBUSY) {
65  if (m_largeBufferLength != 0) {
66  // Here, copy the data from the transfer buffer to the flash memory
67  writeOnMemory();
68  }
69  changeAddressPointerIfNeeded();
70  eraseMemoryIfNeeded();
71  m_state = State::dfuDNLOADIDLE;
72  }
73  }
74 }
75 
76 bool DFUInterface::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
77  if (Interface::processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) {
78  return true;
79  }
80  switch (request->bRequest()) {
81  case (uint8_t) DFURequest::Detach:
82  m_device->detach();
83  return true;
84  case (uint8_t) DFURequest::Download:
85  return processDownloadRequest(request->wLength(), transferBufferLength);
86  case (uint8_t) DFURequest::Upload:
87  return processUploadRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
88  case (uint8_t) DFURequest::GetStatus:
89  return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
90  case (uint8_t) DFURequest::ClearStatus:
91  return clearStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
92  case (uint8_t) DFURequest::GetState:
93  return getState(transferBuffer, transferBufferLength, transferBufferMaxLength);
94  case (uint8_t) DFURequest::Abort:
95  return dfuAbort(transferBufferLength);
96  }
97  return false;
98 }
99 
100 bool DFUInterface::processDownloadRequest(uint16_t wLength, uint16_t * transferBufferLength) {
101  if (m_state != State::dfuIDLE && m_state != State::dfuDNLOADIDLE) {
102  m_state = State::dfuERROR;
103  m_status = Status::errNOTDONE;
105  return false;
106  }
107  if (wLength == 0) {
108  // Leave DFU routine: Reset the device and jump to application code
109  m_state = State::dfuMANIFESTSYNC;
110  } else {
111  // Prepare to receive the download data
112  m_ep0->clearForOutTransactions(wLength);
113  m_state = State::dfuDNLOADSYNC;
114  }
115  return true;
116 }
117 
118 bool DFUInterface::processUploadRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
119  if (m_state != State::dfuIDLE && m_state != State::dfuUPLOADIDLE) {
121  return false;
122  }
123  if (request->wValue() == 0) {
124  /* The host requests to read the commands supported by the bootloader. After
125  * receiving this command, the device should returns N bytes representing
126  * the command codes for :
127  * Get command / Set Address Pointer / Erase / Read Unprotect
128  * We no not need it for now. */
129  return false;
130  } else if (request->wValue() == 1) {
132  return false;
133  } else {
134  /* We decided to never protect Read operation. Else we would have to check
135  * here it is not protected before reading. */
136 
137  // Compute the reading address
138  uint32_t readAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer;
139  // Copy the requested memory zone into the transfer buffer.
140  uint16_t copySize = min(transferBufferMaxLength, request->wLength());
141  memcpy(transferBuffer, (void *)readAddress, copySize);
142  *transferBufferLength = copySize;
143  }
144  m_state = State::dfuUPLOADIDLE;
145  return true;
146 }
147 
148 void DFUInterface::setAddressPointerCommand(SetupPacket * request, uint8_t * transferBuffer, uint16_t transferBufferLength) {
149  assert(transferBufferLength == 5);
150  // Compute the new address but change it after the next getStatus request.
151  m_potentialNewAddressPointer = transferBuffer[1]
152  + (transferBuffer[2] << 8)
153  + (transferBuffer[3] << 16)
154  + (transferBuffer[4] << 24);
155  m_state = State::dfuDNLOADSYNC;
156 }
157 
158 void DFUInterface::changeAddressPointerIfNeeded() {
159  if (m_potentialNewAddressPointer == 0) {
160  // There was no address change waiting.
161  return;
162  }
163  // If there is a new address pointer waiting, change the pointer address.
164  m_addressPointer = m_potentialNewAddressPointer;
165  m_potentialNewAddressPointer = 0;
166  m_state = State::dfuDNLOADIDLE;
167  m_status = Status::OK;
168 }
169 
170 void DFUInterface::eraseCommand(uint8_t * transferBuffer, uint16_t transferBufferLength) {
171  /* We determine whether the commands asks for a mass erase or which sector to
172  * erase. The erase must be done after the next getStatus request. */
173  if (transferBufferLength == 1) {
174  // Mass erase
175  m_erasePage = k_flashMemorySectorsCount;
176  } else {
177  // Sector erase
178  assert(transferBufferLength == 5);
179  /* Find the sector number to erase. If the address is not a valid start of
180  * sector, return an error. */
181  uint32_t sectorAddresses[k_flashMemorySectorsCount] = {
182  0x08000000,
183  0x08004000,
184  0x08008000,
185  0x0800C000,
186  0x08010000,
187  0x08020000,
188  0x08040000,
189  0x08060000,
190  0x08080000,
191  0x080A0000,
192  0x080C0000,
193  0x080E0000
194  };
195  uint32_t eraseAddress = transferBuffer[1]
196  + (transferBuffer[2] << 8)
197  + (transferBuffer[3] << 16)
198  + (transferBuffer[4] << 24);
199  m_erasePage = k_flashMemorySectorsCount + 1;
200  for (uint8_t i = 0; i < k_flashMemorySectorsCount; i++) {
201  if (sectorAddresses[i] == eraseAddress) {
202  m_erasePage = i;
203  break;
204  }
205  }
206  if (m_erasePage == k_flashMemorySectorsCount + 1) {
207  m_state = State::dfuERROR;
208  m_status = Status::errTARGET;
209  return;
210  }
211  }
212  m_state = State::dfuDNLOADSYNC;
213 }
214 
215 
216 void DFUInterface::eraseMemoryIfNeeded() {
217  if (m_erasePage == k_flashMemorySectorsCount + 1) {
218  // There was no erase waiting.
219  return;
220  }
221 
222  // Unlock the Flash and check that no memory operation is ongoing
223  unlockFlashMemory();
224  while (FLASH.SR()->getBSY()) {
225  }
226 
227  if (m_erasePage == k_flashMemorySectorsCount) {
228  // Mass erase
229  FLASH.CR()->setMER(true);
230  } else {
231  // Sector erase
232  FLASH.CR()->setSER(true);
233  FLASH.CR()->setSNB(m_erasePage);
234  }
235  // Trigger the erase operation
236  FLASH.CR()->setSTRT(true);
237 
238  // Lock the Flash after all operations are done
239  while (FLASH.SR()->getBSY()) {
240  }
241  lockFlashMemoryAndPurgeCaches();
242 
243  /* Put an out of range value in m_erasePage to indicate that no erase is
244  * waiting. */
245  m_erasePage = k_flashMemorySectorsCount + 1;
246  m_state = State::dfuDNLOADIDLE;
247  m_status = Status::OK;
248 }
249 
250 void DFUInterface::writeOnMemory() {
251  if (m_writeAddress >= k_flashStartAddress && m_writeAddress <= k_flashEndAddress) {
252  // Write ont the Flash
253 
254  /* We should check here that the destination is not the option bytes: it
255  * won't happen for us. */
256 
257  // Unlock the Flash and check that no memory operation is ongoing
258  unlockFlashMemory();
259  while (FLASH.SR()->getBSY()) {
260  }
261  FLASH.CR()->setPG(true);
262 
263  uint32_t * source = reinterpret_cast<uint32_t *>(m_largeBuffer);
264  uint32_t * destination = reinterpret_cast<uint32_t *>(m_writeAddress);
265  for (uint16_t i=0; i<m_largeBufferLength/sizeof(uint32_t); i++) {
266  *destination++ = *source++;
267  }
268 
269  // Lock the Flash after all operations are done
270  while (FLASH.SR()->getBSY()) {
271  }
272  lockFlashMemoryAndPurgeCaches();
273  } else if (m_writeAddress >= k_sramStartAddress && m_writeAddress <= k_sramEndAddress) {
274  // Write on SRAM
275  // FIXME We should check that we are not overriding the current instructions.
276  memcpy((void *)m_writeAddress, m_largeBuffer, m_largeBufferLength);
277  } else {
278  // Invalid write address
279  m_largeBufferLength = 0;
280  m_state = State::dfuERROR;
281  m_status = Status::errTARGET;
282  return;
283  }
284 
285  // Reset the buffer length
286  m_largeBufferLength = 0;
287  // Change the interface state and status
288  m_state = State::dfuDNLOADIDLE;
289  m_status = Status::OK;
290 }
291 
292 void DFUInterface::unlockFlashMemory() {
293  /* After a reset, program and erase operations are forbidden on the flash.
294  * They can be unlocked by writting the appropriate keys in the FLASH_KEY
295  * register. */
296  while (FLASH.SR()->getBSY()) {
297  }
298  if (FLASH.CR()->getLOCK()) {
299  FLASH.KEYR()->set(0x45670123);
300  FLASH.KEYR()->set(0xCDEF89AB);
301  // Set the parallelism size
302  FLASH.CR()->setPSIZE(FLASH::CR::PSIZE::X32);
303  }
304 }
305 
306 void DFUInterface::lockFlashMemoryAndPurgeCaches() {
307  while (FLASH.SR()->getBSY()) {
308  }
309  FLASH.CR()->setLOCK(true);
310 
311  if (FLASH.ACR()->getDCEN()) {
312  FLASH.ACR()->setDCEN(false);
313  FLASH.ACR()->setDCRST(true);
314  }
315  if (FLASH.ACR()->getICEN()) {
316  FLASH.ACR()->setICEN(false);
317  FLASH.ACR()->setICRST(true);
318  }
319 }
320 
321 bool DFUInterface::getStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
322  // Change the status if needed
323  if (m_state == State::dfuMANIFESTSYNC) {
324  m_state = State::dfuMANIFEST;
325  } else if (m_state == State::dfuDNLOADSYNC) {
326  m_state = State::dfuDNBUSY;
327  }
328  // Copy the status on the TxFifo
329  *transferBufferLength = StatusData(m_status, m_state).copy(transferBuffer, transferBufferMaxLength);
330  return true;
331 }
332 
333 bool DFUInterface::clearStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
334  m_status = Status::OK;
335  m_state = State::dfuIDLE;
336  return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
337 }
338 
339 bool DFUInterface::getState(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t maxSize) {
340  *transferBufferLength = StateData(m_state).copy(transferBuffer, maxSize);
341  return true;
342 }
343 
344 bool DFUInterface::dfuAbort(uint16_t * transferBufferLength) {
345  m_status = Status::OK;
346  m_state = State::dfuIDLE;
347  *transferBufferLength = 0;
348  return true;
349 }
350 
351 void DFUInterface::leaveDFUAndReset() {
352  m_device->setResetOnDisconnect(true);
353  m_device->detach();
354 }
355 
356 }
357 }
358 }
void wholeDataSentCallback(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength) override
#define assert(e)
Definition: assert.h:9
void clearForOutTransactions(uint16_t wLength)
Definition: endpoint0.cpp:254
unsigned short uint16_t
Definition: stdint.h:5
unsigned char uint8_t
Definition: stdint.h:4
void set(Register< T > value) volatile
Definition: register.h:12
void wholeDataReceivedCallback(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength) override
c(generic_all_nodes)
unsigned int uint32_t
Definition: stdint.h:6
Definition: app.cpp:5
static constexpr int MaxTransferSize
Definition: endpoint0.h:26
bool processSetupInRequest(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) override
bool processSetupInRequest(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) override
Definition: interface.cpp:9
void setResetOnDisconnect(bool reset)
Definition: device.h:27
Definition: backlight.h:6
Definition: flash.h:6
void * memcpy(void *dst, const void *src, size_t n)
Definition: memcpy.c:3