summaryrefslogtreecommitdiff
path: root/src/class/dfu/dfu_rt_device.c
blob: 6fafb8e77497be94dca65946787a3dcec7548c44 (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
/*
 * SPDX-FileCopyrightText: Copyright (c) 2019 Sylvain Munaut <[email protected]>
 * SPDX-FileCopyrightText: Copyright (c) 2019 Ha Thach (tinyusb.org)
 * SPDX-License-Identifier: MIT
 *
 * This file is part of the TinyUSB stack.
 */

#include "tusb_option.h"

#if (CFG_TUD_ENABLED && CFG_TUD_DFU_RUNTIME)

#include "device/usbd.h"
#include "device/usbd_pvt.h"

#include "dfu_rt_device.h"

//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+

// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
#ifndef CFG_TUD_DFU_RUNTIME_LOG_LEVEL
  #define CFG_TUD_DFU_RUNTIME_LOG_LEVEL   CFG_TUD_LOG_LEVEL
#endif

#define TU_LOG_DRV(...)   TU_LOG(CFG_TUD_DFU_RUNTIME_LOG_LEVEL, __VA_ARGS__)

//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+

//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
void dfu_rtd_init(void) {
}

bool dfu_rtd_deinit(void) {
  return true;
}

void dfu_rtd_reset(uint8_t rhport) {
  (void) rhport;
}

uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
  (void) rhport;
  (void) max_len;

  // Ensure this is DFU Runtime
  TU_VERIFY((itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS) &&
            (itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT), 0);

  uint8_t const * p_desc = tu_desc_next( itf_desc );
  uint16_t drv_len = sizeof(tusb_desc_interface_t);

  if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) )
  {
    drv_len += tu_desc_len(p_desc);
    p_desc   = tu_desc_next(p_desc);
  }

  return drv_len;
}

// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
  // nothing to do with DATA or ACK stage
  if ( stage != CONTROL_STAGE_SETUP ) return true;

  TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);

  // dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request
  if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
       TUSB_REQ_SET_INTERFACE == request->bRequest )
  {
    tud_control_status(rhport, request);
    return true;
  }

  // Handle class request only from here
  TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);

  switch (request->bRequest)
  {
    case DFU_REQUEST_DETACH:
    {
      TU_LOG_DRV("  DFU RT Request: DETACH\r\n");
      tud_control_status(rhport, request);
      tud_dfu_runtime_reboot_to_dfu_cb();
    }
    break;

    case DFU_REQUEST_GETSTATUS:
    {
      TU_LOG_DRV("  DFU RT Request: GETSTATUS\r\n");
      dfu_status_response_t resp;
      // Status = OK, Poll timeout is ignored during RT, State = APP_IDLE, IString = 0
      TU_VERIFY(tu_memset_s(&resp, sizeof(resp), 0x00, sizeof(resp))==0);
      tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
    }
    break;

    default:
    {
      TU_LOG_DRV("  DFU RT Unexpected Request: %d\r\n", request->bRequest);
      return false; // stall unsupported request
    }
  }

  return true;
}

#endif