~ruther/stm32h747i-disco-usb-image-viewer

ref: 241e20059313285b3c69d72d5360f27dbcee7573 stm32h747i-disco-usb-image-viewer/firmware/include/usb_device.h -rw-r--r-- 12.7 KiB
241e2005 — Rutherther feat: allow reading larger packets without data loss 3 months ago
                                                                                
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
#include <stm32h747xx.h>
#include "usb.h"
#include "generic.h"
#include "queue.h"

typedef uint16_t char16_t;

#ifndef USB_DEVICE_H
#define USB_DEVICE_H

/**
 * @enum usb_device_state_t
 * State of the usb_device.
 */
typedef enum {
  // error states first so they are lowest
  CONTROL_ERROR = -4,                                        /**< There was an error on the control interface. The device doesn't know how to proceed. */
  UNKNOWN_CONTROL_COMMAND = -4,                              /**< An unknown control command has been received. The device doesn't know how to proceed. */
  USB_DEV_ERROR = -3,                                                /**< An unspecified error. */
  UNKNOWN_INTERRUPT = -2,                                    /**< There was an unknown interrupt. This should not occur as all interrupts should be handled. */
  OTG_ERROR = -1,                                            /**< There was an interrupt from otg / error from otg. Otg is not used, so this is not supposed to happen. */

  INIT = 0,                                                  /**< The device has just been created. */
  USB_DEV_RESET = 1,                                                 /**< Received a reset from the usb host. */
  RESET_DONE = 2,                                            /**< The reset from the host is over. */
  SET_ADDRESS_RCVD = 3,                                      /**< Set address has been received and set to the register. */
  ENUMERATED = 4,                                            /**< The device has received SET_CONFIGURATION command, endpoints were configured. */
} usb_device_state_t;

/**
 * @struct usb_fifo_t
 * Represents fifo memory area. You can write/read any data here, it behaves the same.
 * Every read value is popped, every written value is enqueued to the fifo.
 */
typedef struct {
  volatile uint32_t data[1024]; /**< Data memory. Every address here is the same. */
} usb_fifo_t;

struct usb_class_header_t;
struct usb_device_t;
typedef struct usb_device_t usb_device_t;

typedef enum {
  PACKET_GLOBAL_OUT_NAK = 1,
  PACKET_OUT_DATA_PACKET_RCVD = 2,
  PACKET_OUT_TRANSFER_COMPLETED = 3,
  PACKET_SETUP_TRANSACTION_COMPLETED = 4,
  PACKET_SETUP_DATA_PACKET_RECEIVED = 6,
} packet_status_t;

typedef enum {
  DATA0 = 0,
  DATA1 = 1,
  DATA2 = 2,
  MDATA = 3
} packet_dpid_t;

typedef struct {
  packet_status_t packet_status;
  packet_dpid_t dpid;
  uint16_t byte_count;
  uint8_t endpoint_num;
  uint8_t status_phase_start;
} packet_info_t;

/**
 * @struct usb_class_vtable_t
 * A vtable struct for usb class,
 * with various callback functions.
 * Every usb device has to have a class specified.
 * The class provides the configuration of interfaces, endpoints,
 * the pointers, and way to handle interrupts AFTER the device is initialized.
 */
typedef struct {

/**
 * @brief A function that initializes class object.
 * @details Called inside of usb_device_init, to set the device->class field. Few information about the device are given.
 * @param[in,out] device The usb device created so far. device->class is not valid.
 * @param[in] id_vendor Id of the vendor.
 * @param[in] id_product Id of the product.
 * @param[out] vendor_name Name of the vendor.
 * @param[out] product_name The name of product.
 * @param[in] serial_number The serial number.
 * @param[out] serial_name Written representation of the serial number.
 * @return The class itself, having header as first field. This will be saved to device->class.
 */
  struct usb_class_header_t *(*init)(usb_device_t *device,
                uint16_t id_vendor, uint16_t id_product,
                char16_t *vendor_name, char16_t *product_name,
                uint16_t serial_number, char16_t* serial_name);

/**
 * @brief Configure the endpoint zero, and send configuration, including writing to the fifo.
 * @details This is called when GET_DESCRIPTOR of CONFIGURATION type is received. This should
 * configure the sizes, write to the fifo, and enable the endpoint.
 * @param[in,out] device The usb device.
 * @param[out] cmd The command that has been received, GET_DESCRIPTOR of type CONFIGURATION
 */
  task_result_t (*send_configuration)(usb_device_t* device, usb_setup_command_t* cmd);

/**
 * @brief Set all endpoints except for endpoint 0 based on the configuration.
 * @details This is called after SET_CONFIGURATION command is received.
 * @param[in,out] device The usb device.
 * @param[in] configuration Number of the configuration, parameter from SET_CONFIGURATION.
 */
  void (*setup_endpoints)(usb_device_t* device, uint8_t configuration);

/**
 * @brief Reset all used endpoints, except endpoint 0.
 * @details This is called after usb RESET.
 * @param[in,out] device The usb device.
 */
  void (*reset_endpoints)(usb_device_t* device);

/**
 * @brief Callback called when reset is detected.
 * @param[in,out] device The usb device.
 */
  void (*reset_callback)(usb_device_t* device);

/**
 * @brief Received unhandled setup packet.
 * @details Got a setup packet that cannot be handled by the device handler. That likely means a class specific setup command.
 * @param[in,out] device The usb device.
 * @param[out] cmd The command that was received.
 * @return A result of the callback, successfull or error.
 */
  task_result_t (*setup_packet_callback)(usb_device_t* device, usb_setup_command_t* cmd);

/**
 * @brief Received SET_CONFIGURATION, Endpoints set up.
 * @param[in,out] device The usb device.
 */
  void (*enumeration_done_callback)(usb_device_t* device);

/**
 * @brief Callback on tx fifo empty.
 * @param[in,out] device The usb device.
 * @param[in] endpoint The number of the endpoint.
 */
  void (*txfifo_empty_callback)(usb_device_t* device, uint8_t endpoint);

/**
 * @brief Callback on rx fifo full.
 * @param[in,out] device The usb device.
 * @param[in] endpoint The number of the endpoint.
 */
  void (*rxfifo_full_callback)(usb_device_t* device, uint8_t endpoint);

/**
 * @brief Callback on tx transmission done - all data were sent from configured size.
 * @param[in,out] device The usb device.
 * @param[in] endpoint The number of the endpoint.
 * @return Description
 */
  void (*tx_done_callback)(usb_device_t* device, uint8_t endpoint);

/**
 * @brief Callback on rx transmission done - all data were sent from configured size.
 * @param[in,out] device The usb device.
 * @param[in] endpoint The number of the endpoint.
 * @return Description
 */
  void (*rx_done_callback)(usb_device_t* device, uint8_t endpoint);

  void (*received_data_callback)(usb_device_t* device, packet_info_t* packet);

/**
 * @brief Callback on nak being effective.
 * @param[in,out] device The usb device.
 * @param[in] endpoint The number of the endpoint.
 */
  void (*nak_callback)(usb_device_t* device, uint8_t endpoint);

/**
 * @brief Callback on nyet being effective.
 * @param[in,out] device The usb device.
 * @param[in] endpoint The number of the endpoint.
 */
  void (*nyet_callback)(usb_device_t* device, uint8_t endpoint);
} usb_class_vtable_t;

/**
 * @struct usb_interface_t
 * Descriptors of all interfaces the device has
 */
typedef struct {
  usb_interface_descriptor_t interface_descriptor;
  uint8_t endpoint_descriptors_count;              /**< Number of elements in @see endpoint_descriptors */
  usb_endpoint_descriptor_t *endpoint_descriptors; /**< A list of endpoint descriptors. */
} usb_interface_t;

/**
 * @struct usb_class_header_t
 * A header of every class, should be the first field of the class.
 * The header contains common information. It is assumed the device
 * has only one configuration. This is a limitation from our side, usb
 * can support multiple alternate configurations.
 */
struct usb_class_header_t {
  usb_device_descriptor_t device_descriptor;
  usb_device_qualifier_t device_qualifier;
  usb_configuration_descriptor_t configuration_descriptor;
  uint8_t interfaces_count;                                /**< Number of elements in @see interfaces */
  usb_interface_t *interfaces;                             /**< A list of interfaces */
  usb_string_descriptor_zero_t string_descriptor_zero;
  usb_unicode_string_descriptor_t *string_descriptors;     /**< A list of string descriptors. There is no count of those, since not all are sent. The host will request a specific one. Currently it is not checked if the given descriptors exists, which can lead to wrong memory part being read! */
};
typedef struct usb_class_header_t usb_class_header_t;

/**
 * @enum usb_setup_command_stage_t
 * Stages of setup command receival
 */
typedef enum {
  SETUP_STAGE_NONE,              /**< No setup command being handled. */
  SETUP_STAGE_RCVD_SETUP_PACKET, /**< Received a setup packet, saved to memory. */
  SETUP_STAGE_AWAITING_DATA, /**< Handled a setup packet, waiting for data from the host. */
  SETUP_STAGE_SENDING_RESPONSE,  /**< Handling setup packet by sending response to host. */
  SETUP_STAGE_SENDING_ACK,       /**< Handling setup packet by sending acknowledge. */
  SETUP_STAGE_AWAITING_ACK,      /**< Handled a setup packet, sent data, waiting for acknowledge */
} usb_setup_command_stage_t;

#define MAX_SETUP_PACKETS 3
typedef struct {
  queue_t* received_setup_commands;

  uint8_t* rcvd_data;
  uint16_t rcvd_count;
  uint16_t rcvd_awaiting;

  usb_setup_command_stage_t stage;          /**< Current stage status. */

  uint8_t detected_setup_errors;                  /**< Number of errors when handling setup commands. This should be zero. */
} usb_device_setup_t;

/**
 * @struct usb_device_t
 * Struct representing usb device.
 */
struct usb_device_t {
  USB_OTG_GlobalTypeDef *core;                    /**< Address of the usb otg peripheral core. */
  USB_OTG_DeviceTypeDef *device;                  /**< Address of the usb otg peripheral device. */

  USB_OTG_OUTEndpointTypeDef *out;                /**< Address of the usb otg peripheral out endpoints, one by one, so out[1] will point to endpoint 1. */
  USB_OTG_INEndpointTypeDef *in;                  /**< Address of the usb otg peripheral in endpoints, one by one, so in[2] will point to endpoint 2. */

  usb_fifo_t *fifos;                              /**< The fifos, one by one, so fifos[1] will point to fifo for endpoint1 */

  usb_class_vtable_t vt;                          /**< The vtable for the class. */
  usb_class_header_t* class;                      /**< The state of the class, being unknown type, with header as first field. */

  usb_device_state_t state;                       /**< The current status of the device. */

  usb_device_setup_t setup;
};

// has configuration etc

/**
 * @enum usb_device_slot_t
 * The microcontroller has two peripherals,
 * this says which one to initialize. This is important,
 * since there can be only two devices at one point,
 * and to know what device structure to use on interrupt
 * received, there is a global variable for given slot.
 */
typedef enum {
  USB_OTG_HS1 = 0, /**< High speed usb otg at slot 1 */
  USB_OTG_FS2 = 1, /**< Full speed usb otg at slot 2 */
} usb_device_slot_t;
extern usb_device_t usb_devices[2];

/**
 * @brief Create and initialize usb device structure.
 * @details The function will initialize a device, its peripheral addresses, class, and return it.
 * It is expected that the classes will have further initialization functions
 * that need to be called before further proceeding with the initialization sequence.
 * So something like @see usb_cdc_acm_configure has to be called,
 * and only after that @see usb_device_setup can be called.
 * @param[in] slot The slot of the device, used to get the peripheral addresses, and to fill in the global variable.
 * @param[out] class The class vtable. init will be called on this class as part of this function to initialize device->class.
 * @param[in] id_vendor The id of the vendor.
 * @param[in] id_product The id of the product.
 * @param[out] vendor_name The name of vendor
 * @param[out] product_name The name of product
 * @param[in] serial_number The number of serial to ?
 * @param[out] serial_name The name of serial
 * @return The usb device.
 */
void *usb_device_init(usb_device_slot_t slot, usb_class_vtable_t *class,
                      uint16_t id_vendor, uint16_t id_product,
                      char16_t *vendor_name, char16_t *product_name,
                      uint16_t serial_number, char16_t* serial_name);

/**
 * @brief Setup device endpoint 0
 * @details This function handles the setup of the device, it will initialize endpoint zero, and disable the soft disconnect.
 * That means the device will get resetted by the host afterwards, if it's connected. And the setup stage will be initiated by
 * the host.
 * @param[in,out] device The usb device to call setup on.
 */
void usb_device_setup(void* device);

/**
 * @brief Wait for the device to get enumerated
 * @details After initializing the device, this waits for all setup commands to advance
 * to SET_CONFIGURATION command.
 * @param[in,out] device The usb device to wait for.
 */
void usb_device_wait_for_handshake(void* device);

#endif // USB_DEVICE_H
Do not follow this link