18
18
static uint8_t usb_rx_buf [USB_RX_BUF_SIZE ];
19
19
20
20
// Receive buffer head
21
- static uint8_t usb_rx_buf_head ;
21
+ static volatile uint8_t usb_rx_buf_head ;
22
22
23
23
// Receive buffer tail
24
- static uint8_t usb_rx_buf_tail ;
24
+ static volatile uint8_t usb_rx_buf_tail ;
25
25
26
26
// Number of bytes in receive buffer
27
27
static volatile uint8_t usb_rx_count ;
@@ -46,61 +46,80 @@ void mp_cdc_disable(uint8_t port)
46
46
47
47
void usb_rx_notify (void )
48
48
{
49
+ irqflags_t flags ;
49
50
if (mp_cdc_enabled ) {
50
51
while (udi_cdc_is_rx_ready ()) {
51
52
uint8_t c ;
52
- c = udi_cdc_getc ();
53
53
54
- if (c == interrupt_char ) {
55
- mp_keyboard_interrupt ();
56
- // Don't put the interrupt into the buffer, just continue.
57
- continue ;
54
+ // Introduce a critical section to avoid buffer corruption. We use
55
+ // cpu_irq_save instead of cpu_irq_disable because we don't know the
56
+ // current state of IRQs. They may have been turned off already and
57
+ // we don't want to accidentally turn them back on.
58
+ flags = cpu_irq_save ();
59
+ // If our buffer is full, then don't get another character otherwise
60
+ // we'll lose a previous character.
61
+ if (usb_rx_count >= USB_RX_BUF_SIZE ) {
62
+ cpu_irq_restore (flags );
63
+ break ;
58
64
}
59
65
60
- // Introducing critical section to avoid buffer corruption.
61
- cpu_irq_disable ();
62
-
63
- // The count of characters present in receive buffer is
64
- // incremented.
65
- usb_rx_count ++ ;
66
- usb_rx_buf [usb_rx_buf_tail ] = c ;
67
-
66
+ uint8_t current_tail = usb_rx_buf_tail ;
67
+ // Pretend we've received a character so that any nested calls to
68
+ // this function have to consider the spot we've reserved.
68
69
if ((USB_RX_BUF_SIZE - 1 ) == usb_rx_buf_tail ) {
69
70
// Reached the end of buffer, revert back to beginning of
70
71
// buffer.
71
72
usb_rx_buf_tail = 0x00 ;
72
73
} else {
73
74
usb_rx_buf_tail ++ ;
74
75
}
76
+ // The count of characters present in receive buffer is
77
+ // incremented.
78
+ usb_rx_count ++ ;
79
+ // WARNING(tannewt): This call can call us back with the next
80
+ // character!
81
+ c = udi_cdc_getc ();
82
+
83
+ if (c == interrupt_char ) {
84
+ // We consumed a character rather than adding it to the rx
85
+ // buffer so undo the modifications we made to count and the
86
+ // tail.
87
+ usb_rx_count -- ;
88
+ usb_rx_buf_tail = current_tail ;
89
+ cpu_irq_restore (flags );
90
+ mp_keyboard_interrupt ();
91
+ // Don't put the interrupt into the buffer, just continue.
92
+ continue ;
93
+ }
75
94
76
- cpu_irq_enable ();
95
+ // We put the next character where we expected regardless of whether
96
+ // the next character was already loaded in the buffer.
97
+ usb_rx_buf [current_tail ] = c ;
98
+
99
+ cpu_irq_restore (flags );
77
100
}
78
101
}
79
102
}
80
103
81
104
int receive_usb () {
82
- if (0 == usb_rx_count ) {
105
+ if (usb_rx_count == 0 ) {
83
106
return 0 ;
84
107
}
85
108
86
- if (USB_RX_BUF_SIZE <= usb_rx_count ) {
87
- // Bytes between head and tail are overwritten by new data. The
88
- // oldest data in buffer is the one to which the tail is pointing. So
89
- // reading operation should start from the tail.
90
- usb_rx_buf_head = usb_rx_buf_tail ;
91
-
92
- // This is a buffer overflow case.But still only the number of bytes
93
- // equivalent to full buffer size are useful.
94
- usb_rx_count = USB_RX_BUF_SIZE ;
95
- }
96
-
97
109
// Copy from head.
110
+ cpu_irq_disable ();
98
111
int data = usb_rx_buf [usb_rx_buf_head ];
99
112
usb_rx_buf_head ++ ;
100
113
usb_rx_count -- ;
101
114
if ((USB_RX_BUF_SIZE ) == usb_rx_buf_head ) {
102
115
usb_rx_buf_head = 0 ;
103
116
}
117
+ cpu_irq_enable ();
118
+
119
+ // Call usb_rx_notify if we just emptied a spot in the buffer.
120
+ if (usb_rx_count == USB_RX_BUF_SIZE - 1 ) {
121
+ usb_rx_notify ();
122
+ }
104
123
return data ;
105
124
}
106
125
0 commit comments