forked from DangerousPrototypes/BusPirate5-firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
usb_tx.c
303 lines (260 loc) · 9.88 KB
/
usb_tx.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
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
#include <stdio.h>
#include "pico/stdlib.h"
#include "pirate.h"
#include "queue.h"
#include "hardware/dma.h"
//#include "buf.h"
#include "hardware/uart.h"
#include "hardware/irq.h"
#include "usb_rx.h"
#include "usb_tx.h"
#include "tusb.h"
#include "bio.h"
#include "system_config.h"
#include "debug.h"
// USB TX:
// This file contains the code for the user interface ring buffer
// and IO method. USB is the normal IO, but running a chip under debug
// on a USB attached device is a nightmare. BP_DEBUG_ENABLED In the /platform/ folder
// configuration file enables a UART debugging option. All user interface IO
// will be routed to one of the two UARTs (selectable) on the Bus Pirate buffered IO pins
// UART debug mode is way over engineered using DMA et al, and has some predelection for bugs
// in status bar updates
// TODO: rework all the TX stuff into a nice struct with clearer naming
queue_t tx_fifo;
#define TX_FIFO_LENGTH_IN_BITS 10 // 2^n buffer size. 2^3=8, 2^9=512
#define TX_FIFO_LENGTH_IN_BYTES (0x0001<<TX_FIFO_LENGTH_IN_BITS)
char tx_buf[TX_FIFO_LENGTH_IN_BYTES] __attribute__((aligned(2048)));
char tx_sb_buf[1024];
uint16_t tx_sb_buf_cnt=0;
uint16_t tx_sb_buf_index=0;
bool tx_sb_buf_ready=false;
void tx_fifo_init(void)
{
queue2_init(&tx_fifo, tx_buf, TX_FIFO_LENGTH_IN_BYTES); //buffer size must be 2^n for queue AND DMA rollover
}
void tx_sb_start(uint32_t len)
{
tx_sb_buf_cnt=len;
tx_sb_buf_ready=true;
}
void tx_fifo_service(void)
{
//state machine:
#define IDLE 0
#define STATUSBAR_DELAY 1
#define STATUSBAR_TX 2
static uint8_t tx_state=IDLE;
uint16_t bytes_available;
char data[64];
uint8_t i=0;
if(system_config.terminal_usb_enable)
{ // is tinyUSB CDC ready?
if(tud_cdc_write_available()<64)
{
return;
}
}
switch(tx_state)
{
case IDLE:
queue_available_bytes(&tx_fifo, &bytes_available);
if(bytes_available)
{
i=0;
while(queue2_try_remove(&tx_fifo, &data[i]))
{
i++;
if(i>=64) break;
}
break; //break out of switch and continue below
}
//status bar update is ready
if(tx_sb_buf_ready)
{
tx_state=STATUSBAR_DELAY;
tx_sb_buf_index=0;
return; //return for next state
}
return; //nothing, just return
break;
case STATUSBAR_DELAY:
// test: check that no bytes in tx_fifo minimum 2 cycles in a row
// prevent the status bar from being wiped out by the VT100 setup commands
// that might be pending in the TX FIFO
queue_available_bytes(&tx_fifo, &bytes_available);
tx_state=(bytes_available ? IDLE : STATUSBAR_TX);
return; //return for next cycle
break;
case STATUSBAR_TX:
//read out 64 bytes into data at a time until complete
//TODO: pass a pointer to the array cause this is ineffecient
i=0;
while(tx_sb_buf_index<tx_sb_buf_cnt)
{
data[i]=tx_sb_buf[tx_sb_buf_index];
tx_sb_buf_index++;
i++;
if(tx_sb_buf_index>=tx_sb_buf_cnt)
{
tx_sb_buf_ready=false;
tx_state=IDLE; //done, next cycle go to idle
system_config.terminal_ansi_statusbar_update=true; //after first draw of status bar, then allow updates by core1 service loop
break;
}
if(i>=64)
{
break;
}
}
break;
default:
tx_state=IDLE;
break;
}
//if(i==0) return; //safety check
//write to terminal usb
if(system_config.terminal_usb_enable)
{
tud_cdc_write(&data, i);
tud_cdc_write_flush();
if(system_config.terminal_uart_enable) tud_task(); //makes it nicer if we service when the UART is enabled
}
//write to terminal debug uart
if(system_config.terminal_uart_enable)
{
for(uint8_t j=0; j<i; j++)
{
uart_putc(debug_uart[system_config.terminal_uart_number].uart, data[j]);
}
}
return;
}
void tx_fifo_put(char *c)
{
queue2_add_blocking(&tx_fifo, c);
}
#if 0
//This is old TX stuff uing DMA + UART
// leaving it here for reference
char tx_sb_buf[1024];
uint16_t tx_sb_buf_cnt=0;
bool tx_sb_buf_ready=false;
uint8_t cnt=0;
int chan;
int chan_sb;
bool chan_busy=false;
uint16_t bytes_available;
bool chan_caller_sb=false;
void tx_fifo_init(void)
{
queue2_init(&tx_fifo, tx_buf, TX_FIFO_LENGTH_IN_BYTES); //buffer size must be 2^n for queue AND DMA rollover
// Get a free channel, panic() if there are none
chan=dma_claim_unused_channel(true);
chan_sb=dma_claim_unused_channel(true);
dma_channel_config c=dma_channel_get_default_config(chan);
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
channel_config_set_dreq(&c, DREQ_UART1_TX);
channel_config_set_read_increment(&c, true);
channel_config_set_ring(&c, false, TX_FIFO_LENGTH_IN_BITS); // ring size =2^n 2^3=8, 2^9=512
channel_config_set_write_increment(&c, false);
//channel_config_set_irq_quiet(&c,true);
// chain to the fifo DMA channel
//channel_config_set_chain_to(&c, chan_sb);
dma_channel_configure(
chan, // Channel to be configured
&c, // The configuration we just created
&uart_get_hw(BP_DEBUG_UART)->dr, // The initial write address
tx_fifo.data, // The initial read address
0, // Number of transfers; in this case each is 1 byte.
false // Start immediately.
);
c=dma_channel_get_default_config(chan_sb);
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
channel_config_set_dreq(&c, DREQ_UART1_TX);
channel_config_set_read_increment(&c, true);
channel_config_set_write_increment(&c, false);
dma_channel_configure(
chan_sb, // Channel to be configured
&c, // The configuration we just created
&uart_get_hw(BP_DEBUG_UART)->dr, // The initial write address
tx_sb_buf, // The initial read address
0, // Number of transfers; in this case each is 1 byte.
false // Start immediately.
);
// Tell the DMA to raise IRQ line 0 when the channel finishes a block
dma_channel_set_irq0_enabled(chan, true);
dma_channel_set_irq0_enabled(chan_sb, true);
// Configure the processor to run dma_handler() when DMA IRQ 0 is asserted
irq_set_exclusive_handler(DMA_IRQ_0, tx_fifo_handler);
irq_set_enabled(DMA_IRQ_0, true);
}
void tx_fifo_service(void)
{
//if busy, return here to avoid the spin lock in the byte check
if(chan_busy || dma_channel_is_busy(chan) || dma_channel_is_busy(chan_sb)) return;
// TX FIFO has priority here
// This prevents the status bar from being wiped out by the VT100 setup commands
// that might be pending in the TX FIFO
queue_available_bytes(&tx_fifo, &bytes_available);
if(bytes_available)
{
chan_busy=true;
chan_caller_sb=false;
dma_channel_set_trans_count(chan, bytes_available, true);
// Wait blocking and update pointer for non-interrupt debugging
//dma_channel_wait_for_finish_blocking(chan);
//queue_update_read_pointer(&tx_fifo, &bytes_available);
return;
}
if(tx_sb_buf_ready)
{
chan_busy=true;
chan_caller_sb=true;
dma_channel_set_read_addr(chan_sb, tx_sb_buf, false );
dma_channel_set_trans_count(chan_sb, tx_sb_buf_cnt, true);
return;
}
}
// DMA interrupt handler for UART debug mode
void tx_fifo_handler(void)
{
//dma_channel_get_irq0_status()
if(dma_channel_get_irq0_status(chan_sb)) //called from status bar transfer
{
tx_sb_buf_cnt=0;
tx_sb_buf_ready=false;
}
else //called from fifo transfer
{
// DMA copy complete, free up the bytes consumed in the ring buffer
queue_update_read_pointer(&tx_fifo, &bytes_available);
}
//see if there are any other transfers pending
//if more data just start right now!
queue_available_bytes_unsafe(&tx_fifo, &bytes_available);
if(bytes_available)
{
dma_hw->ints0 = 1u << chan_sb;
dma_hw->ints0 = 1u << chan; // Disable interrupt, terrible documentation on this...
chan_caller_sb=false;
dma_channel_set_trans_count(chan, bytes_available, true);
}
else if(tx_sb_buf_ready)
{
dma_hw->ints0 = 1u << chan; // Disable interrupt, terrible documentation on this...
dma_hw->ints0 = 1u << chan_sb;
dma_channel_set_read_addr(chan_sb, tx_sb_buf, false );
dma_channel_set_trans_count(chan_sb, tx_sb_buf_cnt, true);
return;
}
else
{
dma_hw->ints0 = 1u << chan; // Disable interrupt, terrible documentation on this...
dma_hw->ints0 = 1u << chan_sb;
//dma_irqn_acknowledge_channel(0,chan);
//dma_irqn_acknowledge_channel(0,chan_sb);
chan_busy=false;
}
}
#endif