1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * dfu.c -- dfu command 4 * 5 * Copyright (C) 2015 6 * Lukasz Majewski <l.majewski@majess.pl> 7 * 8 * Copyright (C) 2012 Samsung Electronics 9 * authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com> 10 * Lukasz Majewski <l.majewski@samsung.com> 11 */ 12 13#include <common.h> 14#include <command.h> 15#include <log.h> 16#include <watchdog.h> 17#include <dfu.h> 18#include <console.h> 19#include <g_dnl.h> 20#include <usb.h> 21#include <net.h> 22 23int run_usb_dnl_gadget(int usbctrl_index, char *usb_dnl_gadget) 24{ 25 bool dfu_reset = false; 26 int ret, i = 0; 27 28 ret = usb_gadget_initialize(usbctrl_index); 29 if (ret) { 30 pr_err("usb_gadget_initialize failed\n"); 31 return CMD_RET_FAILURE; 32 } 33 g_dnl_clear_detach(); 34 ret = g_dnl_register(usb_dnl_gadget); 35 if (ret) { 36 pr_err("g_dnl_register failed"); 37 return CMD_RET_FAILURE; 38 } 39 40#ifdef CONFIG_DFU_TIMEOUT 41 unsigned long start_time = get_timer(0); 42#endif 43 44 while (1) { 45 if (g_dnl_detach()) { 46 /* 47 * Check if USB bus reset is performed after detach, 48 * which indicates that -R switch has been passed to 49 * dfu-util. In this case reboot the device 50 */ 51 if (dfu_usb_get_reset()) { 52 dfu_reset = true; 53 goto exit; 54 } 55 56 /* 57 * This extra number of usb_gadget_handle_interrupts() 58 * calls is necessary to assure correct transmission 59 * completion with dfu-util 60 */ 61 if (++i == 10000) 62 goto exit; 63 } 64 65 if (ctrlc()) 66 goto exit; 67 68 if (dfu_get_defer_flush()) { 69 /* 70 * Call to usb_gadget_handle_interrupts() is necessary 71 * to act on ZLP OUT transaction from HOST PC after 72 * transmitting the whole file. 73 * 74 * If this ZLP OUT packet is NAK'ed, the HOST libusb 75 * function fails after timeout (by default it is set to 76 * 5 seconds). In such situation the dfu-util program 77 * exits with error message. 78 */ 79 usb_gadget_handle_interrupts(usbctrl_index); 80 ret = dfu_flush(dfu_get_defer_flush(), NULL, 0, 0); 81 dfu_set_defer_flush(NULL); 82 if (ret) { 83 pr_err("Deferred dfu_flush() failed!"); 84 goto exit; 85 } 86 } 87 88#ifdef CONFIG_DFU_TIMEOUT 89 unsigned long wait_time = dfu_get_timeout(); 90 91 if (wait_time) { 92 unsigned long current_time = get_timer(start_time); 93 94 if (current_time > wait_time) { 95 debug("Inactivity timeout, abort DFU\n"); 96 goto exit; 97 } 98 } 99#endif 100 101 if (dfu_reinit_needed) 102 goto exit; 103 104 WATCHDOG_RESET(); 105 usb_gadget_handle_interrupts(usbctrl_index); 106 } 107exit: 108 g_dnl_unregister(); 109 usb_gadget_release(usbctrl_index); 110 111 if (dfu_reset) 112 do_reset(NULL, 0, 0, NULL); 113 114 g_dnl_clear_detach(); 115 116 return ret; 117} 118