-
Notifications
You must be signed in to change notification settings - Fork 120
/
usb_stream_helper.c
138 lines (124 loc) · 5.62 KB
/
usb_stream_helper.c
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
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include <stdio.h>
#include "pico/usb_stream_helper.h"
static uint32_t _usb_stream_chunk_offset(struct usb_stream_transfer *transfer) {
return transfer->offset & (transfer->chunk_size - 1);
}
void usb_stream_packet_handler_complete(struct usb_stream_transfer *transfer) {
struct usb_buffer *buffer;
struct usb_endpoint *ep = transfer->ep;
#ifndef NDEBUG
assert(transfer->packet_handler_complete_expected);
transfer->packet_handler_complete_expected = false;
#endif
assert(ep);
if (ep->in) {
buffer = usb_current_in_packet_buffer(ep);
assert(buffer);
assert(buffer->data_max == 64);
uint chunk_offset = _usb_stream_chunk_offset(transfer);
uint data_len = 64;
if (transfer->offset + 64 > transfer->transfer_length) {
data_len = transfer->transfer_length - transfer->offset;
}
buffer->data_len = data_len;
memcpy(buffer->data, transfer->chunk_buffer + chunk_offset, data_len);
} else {
buffer = usb_current_out_packet_buffer(ep);
assert(buffer);
assert(buffer->data_len);
}
transfer->offset += buffer->data_len;
if (ep->num > 2) usb_debug(" %d transfer_offset %d\n", ep->num, (uint) transfer->offset);
assert(transfer->funcs && transfer->funcs->on_packet_complete);
transfer->funcs->on_packet_complete(transfer);
#ifdef USE_BOOTROM_GPIO
gpio_clr_mask(usb_activity_gpio_pin_mask);
#endif
usb_packet_done(ep);
}
void usb_stream_chunk_done(struct usb_stream_transfer *transfer) {
usb_stream_packet_handler_complete(transfer);
}
void _usb_stream_packet_packet_handler(struct usb_endpoint *ep) {
#ifdef USE_BOOTROM_GPIO
gpio_set_mask(usb_activity_gpio_pin_mask);
#endif
// todo assert type
struct usb_stream_transfer *transfer = (struct usb_stream_transfer *) ep->current_transfer;
uint chunk_offset = _usb_stream_chunk_offset(transfer);
uint chunk_len = 0; // set to non zero to call on_chunk
if (ep->in) {
if (!chunk_offset) {
// we are at the beginning of a chunk so want to call on_chunk
chunk_len = (transfer->offset + transfer->chunk_size) > transfer->transfer_length ?
transfer->transfer_length - transfer->offset : transfer->chunk_size;
if (ep->num > 2)
usb_warn("chunko %d len %05x offset %08x size %04x transfer %08x\n", ep->num, chunk_len, chunk_offset,
(uint) transfer->chunk_size, (uint) transfer->transfer_length);
}
} else {
// usb_debug("write packet %04x %d\n", (uint)transfer->offset, ep->current_take_buffer);
struct usb_buffer *buffer = usb_current_out_packet_buffer(ep);
assert(buffer);
// note we only set chunk_len if this is the end of a chunk
if (transfer->offset + 64 >= transfer->transfer_length) {
// we have ended the transfer (possibly mid-chunk)
chunk_len = transfer->transfer_length & (transfer->chunk_size - 1);
if (chunk_len) {
usb_warn(">> Truncated %08x\n", chunk_len);
} else {
chunk_len = transfer->chunk_size;
}
} else if (chunk_offset + 64 >= transfer->chunk_size) {
// end of regular chunk
chunk_len = transfer->chunk_size;
}
assert(chunk_len || buffer->data_len == 64);
// if (!(!chunk_len || buffer->data_len == ((chunk_len & 63u) ? (chunk_len & 63u) : 64u))) {
// usb_warn("ooh off=%08x len=%08x chunk_off=%04x chunk_len=%04x data_len=%04x\n", (uint)transfer->offset, (uint)transfer->transfer_length, chunk_offset, chunk_len, buffer->data_len);
// }
assert(!chunk_len || buffer->data_len == ((chunk_len & 63u) ? (chunk_len & 63u) : 64u));
// zero buffer when we start a new buffer, so that the chunk callback never sees data it shouldn't (for partial chunks)
if (!chunk_offset) {
memset(transfer->chunk_buffer, 0, transfer->chunk_size);
}
memcpy(transfer->chunk_buffer + chunk_offset, buffer->data, buffer->data_len); // always safe to copy all
}
#ifndef NDEBUG
transfer->packet_handler_complete_expected = true;
#endif
// todo i think this is reasonable since 0 length chunk does nothing
if (chunk_len) {
assert(transfer->funcs && transfer->funcs->on_chunk);
if (transfer->funcs->on_chunk(chunk_len, transfer))
return;
}
usb_stream_packet_handler_complete(transfer);
}
static const struct usb_transfer_type _usb_stream_transfer_type = {
.on_packet = _usb_stream_packet_packet_handler
};
void usb_stream_setup_transfer(struct usb_stream_transfer *transfer, const struct usb_stream_transfer_funcs *funcs,
uint8_t *chunk_buffer, uint32_t chunk_size, uint32_t transfer_length,
usb_transfer_completed_func on_complete) {
transfer->funcs = funcs;
transfer->chunk_buffer = chunk_buffer;
assert(!(chunk_size & 63u)); // buffer should be a multiple of USB packet buffer size
transfer->chunk_size = chunk_size;
transfer->offset = 0;
// todo combine with residue?
transfer->transfer_length = transfer_length;
usb_reset_transfer(&transfer->core, &_usb_stream_transfer_type, on_complete);
usb_grow_transfer(&transfer->core, (transfer_length + 63) / 64);
}
void usb_stream_noop_on_packet_complete(__unused struct usb_stream_transfer *transfer) {
}
bool usb_stream_noop_on_chunk(uint32_t size, __unused struct usb_stream_transfer *transfer) {
return false;
}