LPCOpen Platform  v1.03
LPCOpen Platform for NXP LPC Microcontrollers
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dfuutil_programming_dfu_ops.c
Go to the documentation of this file.
1 /*
2  * @brief DFU utility (programmer) streaming code
3  *
4  * @note
5  * Copyright(C) NXP Semiconductors, 2012
6  * All rights reserved.
7  *
8  * @par
9  * Software that is described herein is for illustrative purposes only
10  * which provides customers with programming information regarding the
11  * LPC products. This software is supplied "AS IS" without any warranties of
12  * any kind, and NXP Semiconductors and its licensor disclaim any and
13  * all warranties, express or implied, including all implied warranties of
14  * merchantability, fitness for a particular purpose and non-infringement of
15  * intellectual property rights. NXP Semiconductors assumes no responsibility
16  * or liability for the use of the software, conveys no license or rights under any
17  * patent, copyright, mask work right, or any other intellectual property rights in
18  * or to any products. NXP Semiconductors reserves the right to make changes
19  * in the software without notification. NXP Semiconductors also makes no
20  * representation or warranty that such application will be suitable for the
21  * specified use without further testing or modification.
22  *
23  * @par
24  * Permission to use, copy, modify, and distribute this software and its
25  * documentation is hereby granted, under NXP Semiconductors' and its
26  * licensor's relevant copyrights in the software, without fee, provided that it
27  * is used in conjunction with NXP Semiconductors microcontrollers. This
28  * copyright, permission, and disclaimer notice must appear in all copies of
29  * this code.
30  */
31 
32 #include "app_usbd_cfg.h"
33 #include "usbd_desc.h"
34 #include "usbd_mscuser.h"
35 #include "usbd.h"
36 #include "usbd_core.h"
37 #include "usbd_hw.h"
38 #include "usbd_mscuser.h"
39 #include "usbd_rom_api.h"
40 
41 #include "board.h"
43 
44 #include "string.h"
45 
46 /*****************************************************************************
47  * Private types/enumerations/variables
48  ****************************************************************************/
49 
50 /* USB handle for ROM driver */
52 
53 /* Saved pointer to region data for programming algorithm */
55 
56 /* Current status of the program and last host command */
57 static volatile uint32_t currStatus;
58 static volatile uint32_t hostCmd;
59 
60 /* Address and size for the current command */
61 static volatile uint32_t currCmdAddr, currCmdSize;
62 
63 /* Time counter in 1mS ticks */
64 static volatile uint32_t u32Milliseconds;
65 
66 /* Size of USB debug message buffer */
67 #define USBMSGBUFFSIZE 1024
68 
69 /* USB debug message buffer and pointers */
70 static char usbBuff[USBMSGBUFFSIZE];
71 static volatile int usbStrIn, usbStrOut;
72 
73 /* Buffer for DFU output (to host) */
74 static uint32_t dfuIn[((sizeof(DFU_TOHOST_PACKETHDR_T) + 64 + 4096) /
75  sizeof(uint32_t))];
76 
77 /* Buffer for DFU input (from host) */
78 static uint32_t dfuOut[((sizeof(DFU_FROMHOST_PACKETHDR_T) + 4096 +
79  sizeof(uint32_t)) / sizeof(uint32_t))];
80 
81 /* Program data buffer (to free up USB buffer) */
82 static uint32_t dfuProgBuff[(4096 / sizeof(uint32_t))];
83 
84 /* IN packet indexing and size */
86 
87 /* Be careful with this number, as the linker may be setup to use ranges
88  just outside this area's size */
89 #define USBROMBUFFSIZE 0x2000
90 
91 /* Temporary string buffer */
92 static char tmpStr[64];
93 
94 /* Quiet mode flag, used with the parameter option to enable verbose
95  debug messages */
96 static bool verifyDebug = true;
97 
98 /*****************************************************************************
99  * Public types/enumerations/variables
100  ****************************************************************************/
101 
102 extern uint8_t USB_DeviceDescriptor[];
103 extern uint8_t USB_FsConfigDescriptor[];
104 extern uint8_t USB_HsConfigDescriptor[];
105 extern uint8_t USB_StringDescriptor[];
106 extern uint8_t USB_DeviceQualifier[];
107 
109 
110 /*****************************************************************************
111  * Private functions
112  ****************************************************************************/
113 
114 /* Delay until ticks expired */
115 static void tickDelayMS(int ticks)
116 {
118  while (u32Milliseconds) {}
119 }
120 
121 /* Sets up debug message buffering for USB status messages */
122 static void usbDebugSetup(void)
123 {
124  /* Just reset pointers to start of buffer */
125  usbStrIn = usbStrOut = 0;
126 }
127 
128 /* Gets message data for a DFU status transfer */
129 static int usbDebugFill(void *buff, int max)
130 {
131  int ssize = 0;
132 
133  if (usbStrIn != usbStrOut) {
134  /* Largest size to send is until the end of the buffer or
135  the passed maximum size */
136  if (usbStrOut > usbStrIn) {
137  /* Can send up to the end of the buffer */
138  ssize = USBMSGBUFFSIZE - usbStrOut;
139  }
140  else {
141  /* Can send up to the end of input */
142  ssize = usbStrIn - usbStrOut;
143  }
144 
145  /* Limit size to input */
146  if (ssize > max) {
147  ssize = max;
148  }
149 
150  /* Copy data */
151  memmove(buff, &usbBuff[usbStrOut], ssize);
152 
153  /* Update start of output and next output index */
154  usbStrOut += ssize;
155  if (usbStrOut >= USBMSGBUFFSIZE) {
156  usbStrOut = 0;
157  }
158  }
159 
160  return ssize;
161 }
162 
163 /* Appends data to a USB buffer */
164 static int usbDFUAppend(char *ptr, void *data, uint32_t bytes)
165 {
166  memmove(ptr, data, bytes);
167 
168  return bytes;
169 }
170 
171 /* Build the response header */
172 static int usbDFUBuildStatus(void *buffer)
173 {
174  char *ptr = (char *) buffer;
175  int strbytes, rsvd, bytes;
176 
177  /* Place string in buffer if needed */
178  strbytes = usbDebugFill((ptr + 16), 64);
179 
180  /* Create header */
181  bytes = usbDFUAppend((ptr + 0), (void *) &hostCmd, 4);
182  bytes += usbDFUAppend((ptr + 4), (void *) &currStatus, 4);
183  bytes += usbDFUAppend((ptr + 8), &strbytes, 4);
184 
185  /* Field is marked as reserved, but it is being used for the
186  DFU buffer size */
187  rsvd = dfuprog_regions->buffer_size;
188  bytes += usbDFUAppend((ptr + 12), &rsvd, 4);/* Reserved[1] field */
189 
190  /* Allocate some space for debug message if needed */
191  if (strbytes != 0) {
192  bytes += 64;
193  }
194 
195  return bytes;
196 }
197 
198 /* Build programming algo status */
199 static int usbDFUReturnStatus(void *buffer)
200 {
201  return usbDFUBuildStatus(buffer);
202 }
203 
204 /* Respond to DFU_HOSTCMD_SETDEBUG command */
205 static void usbDFUSetVerbose(uint32_t addr)
206 {
207  /* Set quiet or verbose mode based on address field */
209 
210  if (addr) {
211  verifyDebug = false;
212  }
213  else {
214  verifyDebug = true;
215  }
216 }
217 
218 /* Respond to DFU_HOSTCMD_ERASE_ALL command */
219 static void usbDFUEraseAll(void)
220 {
221  /* Set erase start state so it starts in the background. */
224 }
225 
226 /* Respond to DFU_HOSTCMD_ERASE_REGION command */
227 static void usbDFUEraseRegion(uint32_t addr, uint32_t size)
228 {
229  /* Set erase start state so it starts in the background. */
232 
233  /* Save erase address and size */
234  currCmdAddr = addr;
235  currCmdSize = size;
236 }
237 
238 /* Respond to DFU_HOSTCMD_PROGRAM command */
239 static void usbDFUProgRegion(uint32_t addr, uint32_t size)
240 {
241  /* Set erase start state so it starts in the background. */
244 
245  /* Save program address and size */
246  currCmdAddr = addr;
247  currCmdSize = size;
248 }
249 
250 /* Respond to DFU_HOSTCMD_READBACK command */
251 static void usbDFUReadRegion(uint32_t addr, uint32_t size)
252 {
253  /* Set erase start state so it starts in the background. */
256 
257  /* Save read address and size */
258  currCmdAddr = addr;
259  currCmdSize = size;
260 }
261 
262 /* Respond to DFU_HOSTCMD_RESET command */
263 static void usbDFUReset(void)
264 {
265  /* Will reset in background. */
268 }
269 
270 /* Respond to DFU_HOSTCMD_EXECUTE command */
271 static void usbDFUExecute(uint32_t addr)
272 {
273  /* Will reset in background. */
276 
277  currCmdAddr = addr;
278 }
279 
280 /* Will set dfu_detach_algo flag when USB detaches */
282 {
283  dfu_detach_algo = 1;
284 }
285 
286 /* Will set dfu_done_algo flag when USB DFU download is complete (ZLP received) */
287 static void dfu_done(void)
288 {
289  dfu_done_algo = 1;
290 }
291 
292 void USB0_IRQHandler(void)
293 {
294  USBD_API->hw->ISR(hUsb);
295 }
296 
297 void USB1_IRQHandler(void)
298 {
299  USBD_API->hw->ISR(hUsb);
300 }
301 
302 /* Checks header */
304 {
305  bool checked = false;
306 
307  if ((pOutHdr->magic != DFUPROG_VALIDVAL) && (checked == false)) {
308  /* Let it keep running, but send a warning */
309  usbDebug("DFU Utility and programming algorithm have different versions\n");
310  checked = true;
311  }
312 
313  return true;
314 }
315 
316 /* Handles OUT (from host) packets and requests from host */
317 static uint8_t dfu_wr(uint32_t block_num, uint8_t * *pBuff, uint32_t length,
318  uint8_t *bwPollTimeout)
319 {
320  DFU_FROMHOST_PACKETHDR_T *pOutHdr;
321  uint32_t addr, size;
322 
323  /* Reset buffer on length = 0 */
324  if (length != 0) {
325  /* Concatenate packets */
326  *pBuff += length;
327  outPktSizeIdx += length;
328  }
329 
330  /* Hack for no ZLP on end of aligned DFU transfer */
332  if (length == 0) {
333  outPktSizeIdx = 0;
334  *pBuff = (uint8_t *) dfuProgBuff;
335  }
336  else if ((outPktSizeIdx == dfuprog_regions->buffer_size) ||
337  (outPktSizeIdx == currCmdSize)) {
341  outPktSizeIdx = 0;
342  *pBuff = (uint8_t *) dfuIn;
343  }
344  }
345  else if (length != 0) {
346  /* Parse packet header, not necessarily used on all receives */
347  pOutHdr = (DFU_FROMHOST_PACKETHDR_T *) dfuIn;
348  addr = pOutHdr->addr;
349  size = pOutHdr->size;
350  checkMagicHeader(pOutHdr);
351 
352  /* Process based on current state */
353  switch (pOutHdr->hostCmd) {
355  usbDFUSetVerbose(addr);
356  break;
357 
359  usbDFUEraseAll();
360  break;
361 
363  usbDFUEraseRegion(addr, size);
364  break;
365 
366  case DFU_HOSTCMD_PROGRAM:
367  /* Only called in stream mode */
368  usbDFUProgRegion(addr, size);
369  outPktSizeIdx = 0;
370  *pBuff = (uint8_t *) dfuProgBuff;
371  return DFU_STATUS_OK;
372 
374  usbDFUReadRegion(addr, size);
375  break;
376 
377  case DFU_HOSTCMD_RESET:
378  usbDFUReset();
379  break;
380 
381  case DFU_HOSTCMD_EXECUTE:
382  usbDFUExecute(addr);
383  break;
384 
385  default:
386  /* Unknown */
387  usbDebug("Unknown command\n");
389  break;
390  }
391 
392  outPktSizeIdx = 0;
393  *pBuff = (uint8_t *) dfuIn;
394  }
395  else {
396  outPktSizeIdx = 0;
397  *pBuff = (uint8_t *) dfuIn;
398  }
399 
400  return DFU_STATUS_OK;
401 }
402 
403 /* DFU IN (to host) state machine for status polling */
404 static uint32_t dfu_rd(uint32_t block_num, uint8_t * *pBuff, uint32_t length)
405 {
406  uint8_t *pBuf = (uint8_t *) *pBuff;
407  uint8_t *ptrCurr;
408 
409  if (length == 0) {
410  inPktSize = 0;
411  }
412 
413  /* Task based on current state */
414  switch (currStatus) {
415  case DFU_OPSTS_IDLE:
416  case DFU_OPSTS_ERRER:
417  case DFU_OPSTS_PROGER:
418  case DFU_OPSTS_READER:
419  case DFU_OPSTS_ERRUN:
420  case DFU_OPSTS_READBUSY:
421  case DFU_OPSTS_READTRIG:
423  case DFU_OPSTS_ERASE_ST:
424  case DFU_OPSTS_ERASE:
425  case DFU_OPSTS_PROG:
427  case DFU_OPSTS_RESET:
428  case DFU_OPSTS_EXEC:
429  case DFU_OPSTS_LOOP:
430  /* All these states return only status */
431  if (inPktSize == 0) {
432  /* Build status response */
433  inPktSize = usbDFUReturnStatus((void *) dfuOut);
434  inPktSizeIdx = 0;
435  ptrCurr = (uint8_t *) dfuOut;
436  }
437 
438  if (inPktSize != 0) {
439  if (length > inPktSize) {
440  length = inPktSize;
441  }
442 
444  /* Switch to read ready state on trnasfer completion */
446  }
447 
448  memmove(pBuf, &ptrCurr[inPktSizeIdx], length);
449  inPktSize -= length;
450  inPktSizeIdx += length;
451  }
452  break;
453 
454  case DFU_OPSTS_READREADY:
455  if (inPktSize == 0) {
457  if (inPktSize > dfuprog_regions->buffer_size) {
458  inPktSize = dfuprog_regions->buffer_size;
459  }
461  inPktSizeIdx = 0;
462  ptrCurr = (uint8_t *) dfuProgBuff;
463  }
464 
465  if (inPktSize != 0) {
466  if (length > inPktSize) {
467  length = inPktSize;
468  }
469 
470  memmove(pBuf, &ptrCurr[inPktSizeIdx], length);
471  inPktSize -= length;
472  inPktSizeIdx += length;
473  }
474 
475  if (inPktSize == 0) {
476  /* Next block */
477  if (currCmdSize == 0) {
478  /* Done with transfer */
480  }
481  else {
483  }
484  }
485  break;
486 
487  default:
488  /* No other cases */
489  usbDebug("Bad programming algorithm state\n");
491  length = dfu_rd(block_num, pBuff, length);
492  break;
493  }
494 
495  return length;
496 }
497 
498 /* USB DFU init via ROM driver */
500  USB_INTERFACE_DESCRIPTOR *pIntfDesc,
501  uint32_t *mem_base,
502  uint32_t *mem_size)
503 {
504  USBD_DFU_INIT_PARAM_T dfu_param;
505  ErrorCode_t ret = LPC_OK;
506 
507  memset((void *) &dfu_param, 0, sizeof(USBD_DFU_INIT_PARAM_T));
508  dfu_param.mem_base = *mem_base;
509  dfu_param.mem_size = *mem_size;
510  /* DFU paramas */
511  dfu_param.wTransferSize = USB_DFU_XFER_SIZE;
512 
513  if ((pIntfDesc == 0) ||
514  (pIntfDesc->bInterfaceClass != USB_DEVICE_CLASS_APP) ||
515  (pIntfDesc->bInterfaceSubClass != USB_DFU_SUBCLASS) ) {
516  return ERR_FAILED;
517  }
518 
519  dfu_param.intf_desc = (uint8_t *) pIntfDesc;
520  /* user defined functions */
521  dfu_param.DFU_Write = dfu_wr; /* From host */
522  dfu_param.DFU_Read = dfu_rd;/* To host */
523  dfu_param.DFU_Done = dfu_done;
524  dfu_param.DFU_Detach = dfu_detach;
525 
526  ret = USBD_API->dfu->init(hUsb, &dfu_param, DFU_STATE_dfuIDLE);
527  /* update memory variables */
528  *mem_base = dfu_param.mem_base;
529  *mem_size = dfu_param.mem_size;
530 
531  return ret;
532 }
533 
534 /* Sets up USB for DFU operation using boot ROM */
536 {
537  USBD_API_INIT_PARAM_T usb_param;
538  USB_CORE_DESCS_T desc;
539  ErrorCode_t ret;
540  USB_INTERFACE_DESCRIPTOR *pIntfDesc;
541 
542  /* Initilize call back structures */
543  memset((void *) &usb_param, 0, sizeof(USBD_API_INIT_PARAM_T));
544 
545  /* USB_EPWR power saving mode will be enabled if the boot ROM
546  was booted from USB1 */
547  if ((LPC_SCU->SFSUSB & (1 << 4)) == 0) {
548  usb_param.usb_reg_base = LPC_USB0_BASE;
549  }
550  else {
551  usb_param.usb_reg_base = LPC_USB1_BASE;
552  }
553 
554  usb_param.max_num_ep = 6;
555  usb_param.mem_base = 0x20000000;
556  usb_param.mem_size = USBROMBUFFSIZE;
557 
558  /* Set the USB descriptors */
564 
565  /* USB Initialization */
566  ret = USBD_API->hw->Init(&hUsb, &desc, &usb_param);
567  if (ret == LPC_OK) {
568  pIntfDesc = (USB_INTERFACE_DESCRIPTOR *) ((uint32_t) desc.high_speed_desc + USB_CONFIGUARTION_DESC_SIZE);
569  ret = usb_dfu_init(hUsb, pIntfDesc, &usb_param.mem_base, &usb_param.mem_size);
570  if (ret != LPC_OK) {
571  return 0;
572  }
573 
574  NVIC_EnableIRQ(USB0_IRQn);
575  NVIC_EnableIRQ(USB1_IRQn);
576  }
577 
578  USBD_API->hw->Connect(hUsb, 1);
579 
580  return 1;
581 }
582 
583 /* Reset chip via the RGU */
584 static void lpc18xx43xx_sys_reset(void)
585 {
586  /* Reset core via RGU */
587  while (1) {
589  }
590 }
591 
596 static void usbPllSetup(void)
597 {
598  /* Nothing to do here, the PLL settings are inherited from the
599  boot ROM on USB boot */
600 }
601 
602 /* DFU processing entry point */
603 static void dfu_util_process(void)
604 {
605  PFV pfunc;
606  int i, blks;
607 
608  /* Initialize programming interfaces and get device gemometry */
609  dfuprog_regions = algo_flash_init();
610 
611  /* Show regions */
612  sprintf(tmpStr, "Number of memory regions : %d\n", dfuprog_regions->num_regions);
613  usbDebug(tmpStr);
614  for (i = 0; i < dfuprog_regions->num_regions; i++) {
615  sprintf(tmpStr, "Region #%d @ 0x%p, size %p bytes\n", i,
616  (void *) dfuprog_regions->pregions[i].region_addr,
617  (void *) dfuprog_regions->pregions[i].region_size);
618  usbDebug(tmpStr);
619  }
620 
621  /* Before starting USB, setup USB state for configuration */
623 
624  /* Setup DFU USB mode and enumerate */
625  if (algo_dfu_setup() == 0) {
626  /* DFU failed, so status can't be returned. Attempt a reset */
627  USBD_API->hw->Connect(hUsb, 0);
629  tickDelayMS(500);
630  }
631 
632  /* Processing loop for background programming operation */
633  while (1) {
634  /* Background processing loop */
635  switch (currStatus) {
636  case DFU_OPSTS_IDLE:
637  /* Do nothing when idle */
638  case DFU_OPSTS_ERRER:
639  case DFU_OPSTS_READER:
640  case DFU_OPSTS_PROGER:
641  case DFU_OPSTS_ERRUN:
642  /* Error states are handled in the interrupt handler, so
643  there is nothing to do here. */
644  case DFU_OPSTS_READREADY:
645  case DFU_OPSTS_READTRIG:
646  __WFI();
647  break;
648 
649  case DFU_OPSTS_READBUSY:
650  blks = currCmdSize;
651  if (blks > dfuprog_regions->buffer_size) {
652  blks = dfuprog_regions->buffer_size;
653  }
654  if (dfuprog_regions->pprogalgos->read(dfuProgBuff, currCmdAddr,
655  blks) == 0) {
657  }
658  else {
659  /* Read data and return to ready state on completion */
661  currCmdAddr += blks;
662  }
663  break;
664 
666  /* Starting a full erase operation */
668  if (dfuprog_regions->pprogalgos->erase_all() == 0) {
670  }
671  break;
672 
673  case DFU_OPSTS_ERASE_ST:
674  /* Starting a region erase operation */
676  if (dfuprog_regions->pprogalgos->erase_region(currCmdAddr,
677  currCmdSize) == 0) {
679  }
680  break;
681 
682  case DFU_OPSTS_ERASE:
683  /* Since the erase function is blocking, this state indicatea a
684  successful erase or program operation. Switch back to idle state. */
686  break;
687 
689  /* Nothing to do in this state */
690  __WFI();
691  break;
692 
693  case DFU_OPSTS_PROG:
694  if (progSize == 0) {
695  /* Done, go back to idle */
697  }
698  else {
699  if (dfuprog_regions->pprogalgos->write((void *) dfuProgBuff,
702  }
703  else if ((progSize < dfuprog_regions->buffer_size) ||
704  (currCmdSize == 0)) {
706  }
707  else {
710  }
711  }
712  break;
713 
714  case DFU_OPSTS_RESET:
715  /* Reset the chip */
716  tickDelayMS(100);
717  USBD_API->hw->Connect(hUsb, 0);
718  NVIC_DisableIRQ(SysTick_IRQn);
720  break;
721 
722  case DFU_OPSTS_EXEC:
723  /* Jump to a address */
724  tickDelayMS(100);
725  pfunc = (PFV) currCmdAddr;
726  USBD_API->hw->Connect(hUsb, 0);
727  NVIC_DisableIRQ(SysTick_IRQn);
728  pfunc();
729  break;
730 
731  case DFU_OPSTS_LOOP:
732  /* Dead loop, happens on an error */
733  USBD_API->hw->Connect(hUsb, 0);
734  while (1) {}
735  }
736  }
737 }
738 
739 /*****************************************************************************
740  * Public functions
741  ****************************************************************************/
742 
747 void SysTick_Handler(void)
748 {
749  if (u32Milliseconds > 0) {
750  u32Milliseconds--;
751  }
752 }
753 
754 /* Queues a message for DFU status transfer */
755 void usbDebug(char *tmp)
756 {
757  if (verifyDebug == true) {
758  /* Wrap without limits */
759  while (*tmp != '\0') {
760  usbBuff[usbStrIn] = *tmp;
761  usbStrIn++;
762  if (usbStrIn >= USBMSGBUFFSIZE) {
763  usbStrIn = 0;
764  }
765  tmp++;
766  }
767  }
768 }
769 
774 int main(void)
775 {
776  /* Updates SystemCoreClock global var with current clock speed */
778 
779  /* Setup and enable USB PLL and PHY */
780  usbPllSetup();
781 
782  /* Setup 1mS tick source for timing */
783  SysTick_Config(Chip_Clock_GetRate(CLK_MX_MXCORE) / 1000);
784 
785  /* Start debug output buffering for for DFU Utility */
786  usbDebugSetup();
787  usbDebug("LPC18xx/43xx DFUSec programming API tool\n");
788  usbDebug("Build date: " __DATE__ ":" __TIME__ "\n");
789 
790  /* Will stay in processing until a reset is requested by host */
792 
793  /* dfu_util_process() shouldn't exit, so reset the chip/board
794  if it does. */
796 
797  return 0;
798 }