diff --git a/config/printer-creality-ender5-2019.cfg b/config/printer-creality-ender5-2019.cfg
index 0130321ba7bf..cf0d4bed09a8 100644
--- a/config/printer-creality-ender5-2019.cfg
+++ b/config/printer-creality-ender5-2019.cfg
@@ -1,10 +1,12 @@
# This file contains common pin mappings for the 2019 Creality
# Ender 5. To use this config, the firmware should be compiled for the
-# AVR atmega1284p.
+# AVR atmega1284p. This also works for the v1.1.5 silent boards.
# Note, a number of Melzi boards are shipped with a bootloader that
# requires the following command to flash the board:
# avrdude -p atmega1284p -c arduino -b 57600 -P /dev/ttyUSB0 -U out/klipper.elf.hex
+# For v1.1.5 silent boards, the following command is used:
+# avrdude -p atmega1284p -c arduino -P /dev/ttyUSB0 -b 115200 -U flash:w:out/klipper.elf.hex
# If the above command does not work and "make flash" does not work
# then one may need to flash a bootloader to the board - see the
# Klipper docs/Bootloaders.md file for more information.
@@ -80,6 +82,8 @@ pin: PB4
[mcu]
serial: /dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0
+# Silent boards tend to have the exact same serial ID, except without USB2.0, using USB instead.
+# e.g. /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
[printer]
kinematics: cartesian
diff --git a/docs/Config_Changes.md b/docs/Config_Changes.md
index 063af8ac390f..6d83238d0295 100644
--- a/docs/Config_Changes.md
+++ b/docs/Config_Changes.md
@@ -8,6 +8,15 @@ All dates in this document are approximate.
## Changes
+20230826: If `safe_distance` is set or calculated to be 0 in `[dual_carriage]`,
+the carriages proximity checks will be disabled as per documentation. A user
+may wish to configure `safe_distance` explicitly to prevent accidental crashes
+of the carriages with each other. Additionally, the homing order of the primary
+and the dual carriage is changed in some configurations (certain configurations
+when both carriages home in the same direction, see
+[[dual_carriage] configuration reference](./Config_Reference.md#dual_carriage)
+for more details).
+
20230810: The flash-sdcard.sh script now supports both variants of the
Bigtreetech SKR-3, STM32H743 and STM32H723. For this, the original tag
of btt-skr-3 now has changed to be either btt-skr-3-h743 or btt-skr-3-h723.
diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md
index 25e7cc46e6fb..241391834e0f 100644
--- a/docs/Config_Reference.md
+++ b/docs/Config_Reference.md
@@ -2065,6 +2065,14 @@ in this section (CARRIAGE=0 will return activation to the primary carriage).
Dual carriage support is typically combined with extra extruders - the
SET_DUAL_CARRIAGE command is often called at the same time as the
ACTIVATE_EXTRUDER command. Be sure to park the carriages during deactivation.
+Note that during G28 homing, typically the primary carriage is homed first
+followed by the carriage defined in the `[dual_carriage]` config section.
+However, the `[dual_carriage]` carriage will be homed first if both carriages
+home in a positive direction and the [dual_carriage] carriage has a
+`position_endstop` greater than the primary carriage, or if both carriages home
+in a negative direction and the `[dual_carriage]` carriage has a
+`position_endstop` less than the primary carriage.
+
Additionally, one could use "SET_DUAL_CARRIAGE CARRIAGE=1 MODE=COPY" or
"SET_DUAL_CARRIAGE CARRIAGE=1 MODE=MIRROR" commands to activate either copying
or mirroring mode of the dual carriage, in which case it will follow the
diff --git a/docs/RPi_microcontroller.md b/docs/RPi_microcontroller.md
index 4e3c057fe41b..96ac5626f9a0 100644
--- a/docs/RPi_microcontroller.md
+++ b/docs/RPi_microcontroller.md
@@ -203,7 +203,7 @@ channels need to be enabled you can use `pwm-2chan`:
# Enable pwmchip sysfs interface
dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4
```
-This example additionaly enables PWM1 and routes it to gpio13.
+This example additionally enables PWM1 and routes it to gpio13.
The overlay does not expose the pwm line on sysfs on boot and needs to
be exported by echo'ing the number of the pwm channel to
diff --git a/docs/Sponsors.md b/docs/Sponsors.md
index 7376cb31ca62..a226bb57bb5c 100644
--- a/docs/Sponsors.md
+++ b/docs/Sponsors.md
@@ -6,7 +6,7 @@ sponsors.
## BIGTREETECH
-[](https://bigtree-tech.com/collections/all-products)
+[](https://bigtree-tech.com/collections/all-products)
BIGTREETECH is the official mainboard sponsor of Klipper. BIGTREETECH
is committed to developing innovative and competitive products to
@@ -16,7 +16,8 @@ serve the 3D printing community better. Follow them on
## Sponsors
-[](https://obico.io/klipper.html?source=klipper_sponsor)
+[](https://obico.io/klipper.html?source=klipper_sponsor)
+[](https://peopoly.net)
## Klipper Developers
diff --git a/docs/img/sponsors/peopoly-logo.png b/docs/img/sponsors/peopoly-logo.png
new file mode 100644
index 000000000000..bcf1bd9ce613
Binary files /dev/null and b/docs/img/sponsors/peopoly-logo.png differ
diff --git a/klippy/chelper/serialqueue.c b/klippy/chelper/serialqueue.c
index b6500fe621d5..e6810933aabc 100644
--- a/klippy/chelper/serialqueue.c
+++ b/klippy/chelper/serialqueue.c
@@ -62,6 +62,7 @@ struct serialqueue {
int ready_bytes, upcoming_bytes, need_ack_bytes, last_ack_bytes;
uint64_t need_kick_clock;
struct list_head notify_queue;
+ double last_write_fail_time;
// Received messages
struct list_head receive_queue;
// Fastreader support
@@ -376,8 +377,16 @@ do_write(struct serialqueue *sq, void *buf, int buflen)
int ret = write(sq->serial_fd, &cf, sizeof(cf));
if (ret < 0) {
report_errno("can write", ret);
+ double curtime = get_monotonic();
+ if (!sq->last_write_fail_time) {
+ sq->last_write_fail_time = curtime;
+ } else if (curtime > sq->last_write_fail_time + 10.0) {
+ errorf("Halting reads due to CAN write errors.");
+ pollreactor_do_exit(sq->pr);
+ }
return;
}
+ sq->last_write_fail_time = 0.0;
buf += size;
buflen -= size;
}
diff --git a/klippy/kinematics/idex_modes.py b/klippy/kinematics/idex_modes.py
index b54f96579c9b..2ce91afe85a5 100644
--- a/klippy/kinematics/idex_modes.py
+++ b/klippy/kinematics/idex_modes.py
@@ -65,7 +65,13 @@ def toggle_active_dc_rail(self, index, override_rail=False):
kin.update_limits(self.axis, target_dc.get_rail().get_range())
def home(self, homing_state):
kin = self.printer.lookup_object('toolhead').get_kinematics()
- for i, dc_rail in enumerate(self.dc):
+ enumerated_dcs = list(enumerate(self.dc))
+ if (self.get_dc_order(0, 1) > 0) != \
+ self.dc[0].get_rail().get_homing_info().positive_dir:
+ # The second carriage must home first, because the carriages home in
+ # the same direction and the first carriage homes on the second one
+ enumerated_dcs.reverse()
+ for i, dc_rail in enumerated_dcs:
self.toggle_active_dc_rail(i, override_rail=True)
kin.home_axis(homing_state, self.axis, dc_rail.get_rail())
# Restore the original rails ordering
@@ -78,9 +84,15 @@ def get_kin_range(self, toolhead, mode):
axes_pos = [dc.get_axis_position(pos) for dc in self.dc]
dc0_rail = self.dc[0].get_rail()
dc1_rail = self.dc[1].get_rail()
- range_min = dc0_rail.position_min
- range_max = dc0_rail.position_max
+ if mode != PRIMARY or self.dc[0].is_active():
+ range_min = dc0_rail.position_min
+ range_max = dc0_rail.position_max
+ else:
+ range_min = dc1_rail.position_min
+ range_max = dc1_rail.position_max
safe_dist = self.safe_dist
+ if not safe_dist:
+ return (range_min, range_max)
if mode == COPY:
range_min = max(range_min,
@@ -88,7 +100,7 @@ def get_kin_range(self, toolhead, mode):
range_max = min(range_max,
axes_pos[0] - axes_pos[1] + dc1_rail.position_max)
elif mode == MIRROR:
- if dc0_rail.get_homing_info().positive_dir:
+ if self.get_dc_order(0, 1) > 0:
range_min = max(range_min,
0.5 * (sum(axes_pos) + safe_dist))
range_max = min(range_max,
@@ -102,14 +114,39 @@ def get_kin_range(self, toolhead, mode):
# mode == PRIMARY
active_idx = 1 if self.dc[1].is_active() else 0
inactive_idx = 1 - active_idx
- if active_idx:
- range_min = dc1_rail.position_min
- range_max = dc1_rail.position_max
- if self.dc[active_idx].get_rail().get_homing_info().positive_dir:
+ if self.get_dc_order(active_idx, inactive_idx) > 0:
range_min = max(range_min, axes_pos[inactive_idx] + safe_dist)
else:
range_max = min(range_max, axes_pos[inactive_idx] - safe_dist)
+ if range_min > range_max:
+ # During multi-MCU homing it is possible that the carriage
+ # position will end up below position_min or above position_max
+ # if position_endstop is too close to the rail motion ends due
+ # to inherent latencies of the data transmission between MCUs.
+ # This can result in an invalid range_min > range_max range
+ # in certain modes, which may confuse the kinematics code.
+ # So, return an empty range instead, which will correctly
+ # block the carriage motion until a different mode is selected
+ # which actually permits carriage motion.
+ return (range_min, range_min)
return (range_min, range_max)
+ def get_dc_order(self, first, second):
+ if first == second:
+ return 0
+ # Check the relative order of the first and second carriages and
+ # return -1 if the first carriage position is always smaller
+ # than the second one and 1 otherwise
+ first_rail = self.dc[first].get_rail()
+ second_rail = self.dc[second].get_rail()
+ first_homing_info = first_rail.get_homing_info()
+ second_homing_info = second_rail.get_homing_info()
+ if first_homing_info.positive_dir != second_homing_info.positive_dir:
+ # Carriages home away from each other
+ return 1 if first_homing_info.positive_dir else -1
+ # Carriages home in the same direction
+ if first_rail.position_endstop > second_rail.position_endstop:
+ return 1
+ return -1
def activate_dc_mode(self, index, mode):
toolhead = self.printer.lookup_object('toolhead')
toolhead.flush_step_generation()
diff --git a/lib/README b/lib/README
index b3bd95e563ce..e981df59f78d 100644
--- a/lib/README
+++ b/lib/README
@@ -167,7 +167,7 @@ used to upload firmware to devices flashed with the CanBoot bootloader.
The can2040 directory contains code from:
https://github.com/KevinOConnor/can2040
-revision d1190afcaa6245c20da28199d06e453d2e743099.
+version v1.6.0 (af3d21e5d61b8408c63fbdfb0aceb21d69d91693)
The Huada HC32F460 directory contains code from:
https://www.hdsc.com.cn/Category83-1490
diff --git a/lib/can2040/can2040.c b/lib/can2040/can2040.c
index 926893d94c0a..c2bd0061a57c 100644
--- a/lib/can2040/can2040.c
+++ b/lib/can2040/can2040.c
@@ -1,6 +1,6 @@
// Software CANbus implementation for rp2040
//
-// Copyright (C) 2022 Kevin O'Connor
+// Copyright (C) 2022,2023 Kevin O'Connor
//
// This file may be distributed under the terms of the GNU GPLv3 license.
@@ -318,6 +318,14 @@ pio_irq_set(struct can2040 *cd, uint32_t sm_irqs)
pio_hw->inte0 = sm_irqs | SI_RX_DATA;
}
+// Completely disable host irqs
+static void
+pio_irq_disable(struct can2040 *cd)
+{
+ pio_hw_t *pio_hw = cd->pio_hw;
+ pio_hw->inte0 = 0;
+}
+
// Return current host irq mask
static uint32_t
pio_irq_get(struct can2040 *cd)
@@ -662,6 +670,7 @@ tx_schedule_transmit(struct can2040 *cd)
pio_signal_set_txpending(cd);
}
cd->tx_state = TS_QUEUED;
+ cd->stats.tx_attempt++;
struct can2040_transmit *qt = &cd->tx_queue[tx_qpos(cd, tx_pull_pos)];
pio_tx_send(cd, qt->stuffed_data, qt->stuffed_words);
return 0;
@@ -721,6 +730,7 @@ report_callback_error(struct can2040 *cd, uint32_t error_code)
static void
report_callback_rx_msg(struct can2040 *cd)
{
+ cd->stats.rx_total++;
cd->rx_cb(cd, CAN2040_NOTIFY_RX, &cd->parse_msg);
}
@@ -729,6 +739,7 @@ static void
report_callback_tx_msg(struct can2040 *cd)
{
writel(&cd->tx_pull_pos, cd->tx_pull_pos + 1);
+ cd->stats.tx_total++;
cd->rx_cb(cd, CAN2040_NOTIFY_TX, &cd->parse_msg);
}
@@ -748,11 +759,11 @@ report_handle_eof(struct can2040 *cd)
pio_match_clear(cd);
}
-// Check if in an rx message is being processed
+// Check if message being processed is an rx message (not self feedback from tx)
static int
-report_is_rx_eof_pending(struct can2040 *cd)
+report_is_not_in_tx(struct can2040 *cd)
{
- return cd->report_state == RS_NEED_RX_EOF;
+ return !(cd->report_state & RS_NEED_TX_ACK);
}
// Parser found a new message start
@@ -817,7 +828,7 @@ report_note_eof_success(struct can2040 *cd)
// Parser found unexpected data on input
static void
-report_note_parse_error(struct can2040 *cd)
+report_note_discarding(struct can2040 *cd)
{
if (cd->report_state != RS_IDLE) {
cd->report_state = RS_IDLE;
@@ -880,7 +891,7 @@ report_line_txpending(struct can2040 *cd)
return;
}
// Tx request from can2040_transmit(), report_note_eof_success(),
- // or report_note_parse_error().
+ // or report_note_discarding().
uint32_t check_txpending = tx_schedule_transmit(cd);
pio_irq_set(cd, (pio_irqs & ~SI_TXPENDING) | check_txpending);
}
@@ -896,6 +907,13 @@ enum {
MS_CRC, MS_ACK, MS_EOF0, MS_EOF1, MS_DISCARD
};
+// Reset any bits in the incoming parsing state
+static void
+data_state_clear_bits(struct can2040 *cd)
+{
+ cd->raw_bit_count = cd->unstuf.stuffed_bits = cd->unstuf.count_stuff = 0;
+}
+
// Transition to the next parsing state
static void
data_state_go_next(struct can2040 *cd, uint32_t state, uint32_t num_bits)
@@ -908,23 +926,35 @@ data_state_go_next(struct can2040 *cd, uint32_t state, uint32_t num_bits)
static void
data_state_go_discard(struct can2040 *cd)
{
- report_note_parse_error(cd);
-
if (pio_rx_check_stall(cd)) {
// CPU couldn't keep up for some read data - must reset pio state
- cd->raw_bit_count = cd->unstuf.count_stuff = 0;
+ data_state_clear_bits(cd);
pio_sm_setup(cd);
report_callback_error(cd, 0);
}
data_state_go_next(cd, MS_DISCARD, 32);
+
+ // Clear report state and update hw irqs after transition to MS_DISCARD
+ report_note_discarding(cd);
+}
+
+// Note a data parse error and transition to discard state
+static void
+data_state_go_error(struct can2040 *cd)
+{
+ cd->stats.parse_error++;
+ data_state_go_discard(cd);
}
// Received six dominant bits on the line
static void
data_state_line_error(struct can2040 *cd)
{
- data_state_go_discard(cd);
+ if (cd->parse_state == MS_DISCARD)
+ data_state_go_discard(cd);
+ else
+ data_state_go_error(cd);
}
// Received six unexpected passive bits on the line
@@ -933,7 +963,7 @@ data_state_line_passive(struct can2040 *cd)
{
if (cd->parse_state != MS_DISCARD && cd->parse_state != MS_START) {
// Bitstuff error
- data_state_go_discard(cd);
+ data_state_go_error(cd);
return;
}
@@ -941,8 +971,7 @@ data_state_line_passive(struct can2040 *cd)
uint32_t dom_bits = ~stuffed_bits;
if (!dom_bits) {
// Counter overflow in "sync" state machine - reset it
- cd->unstuf.stuffed_bits = 0;
- cd->raw_bit_count = cd->unstuf.count_stuff = 0;
+ data_state_clear_bits(cd);
pio_sm_setup(cd);
data_state_go_discard(cd);
return;
@@ -972,7 +1001,7 @@ data_state_go_crc(struct can2040 *cd)
int ret = report_note_crc_start(cd);
if (ret) {
- data_state_go_discard(cd);
+ data_state_go_error(cd);
return;
}
data_state_go_next(cd, MS_CRC, 16);
@@ -1065,7 +1094,7 @@ static void
data_state_update_crc(struct can2040 *cd, uint32_t data)
{
if (((cd->parse_crc << 1) | 1) != data) {
- data_state_go_discard(cd);
+ data_state_go_error(cd);
return;
}
@@ -1083,7 +1112,7 @@ data_state_update_ack(struct can2040 *cd, uint32_t data)
// data_state_line_passive()
unstuf_restore_state(&cd->unstuf, (cd->parse_crc_bits << 2) | data);
- data_state_go_discard(cd);
+ data_state_go_error(cd);
return;
}
report_note_ack_success(cd);
@@ -1095,7 +1124,7 @@ static void
data_state_update_eof0(struct can2040 *cd, uint32_t data)
{
if (data != 0x0f || pio_rx_check_stall(cd)) {
- data_state_go_discard(cd);
+ data_state_go_error(cd);
return;
}
unstuf_clear_state(&cd->unstuf);
@@ -1106,14 +1135,17 @@ data_state_update_eof0(struct can2040 *cd, uint32_t data)
static void
data_state_update_eof1(struct can2040 *cd, uint32_t data)
{
- if (data >= 0x1c || (data >= 0x18 && report_is_rx_eof_pending(cd)))
- // Message is considered fully transmitted
+ if (data == 0x1f) {
+ // Success
report_note_eof_success(cd);
-
- if (data == 0x1f)
data_state_go_next(cd, MS_START, 1);
- else
+ } else if (data >= 0x1c || (data >= 0x18 && report_is_not_in_tx(cd))) {
+ // Message fully transmitted - followed by "overload frame"
+ report_note_eof_success(cd);
data_state_go_discard(cd);
+ } else {
+ data_state_go_error(cd);
+ }
}
// Handle data received while in MS_DISCARD state
@@ -1310,13 +1342,28 @@ can2040_start(struct can2040 *cd, uint32_t sys_clock, uint32_t bitrate
{
cd->gpio_rx = gpio_rx;
cd->gpio_tx = gpio_tx;
+ data_state_clear_bits(cd);
pio_setup(cd, sys_clock, bitrate);
data_state_go_discard(cd);
}
-// API function to stop and uninitialize can2040 code
+// API function to stop can2040 code
void
-can2040_shutdown(struct can2040 *cd)
+can2040_stop(struct can2040 *cd)
{
- // XXX
+ pio_irq_disable(cd);
+ pio_sm_setup(cd);
+}
+
+// API function to access can2040 statistics
+void
+can2040_get_statistics(struct can2040 *cd, struct can2040_stats *stats)
+{
+ for (;;) {
+ memcpy(stats, &cd->stats, sizeof(*stats));
+ if (memcmp(stats, &cd->stats, sizeof(*stats)) == 0)
+ // Successfully copied data
+ return;
+ // Raced with irq handler update - retry copy
+ }
}
diff --git a/lib/can2040/can2040.h b/lib/can2040/can2040.h
index fc0bdd62784c..7dbee1162555 100644
--- a/lib/can2040/can2040.h
+++ b/lib/can2040/can2040.h
@@ -26,11 +26,18 @@ struct can2040;
typedef void (*can2040_rx_cb)(struct can2040 *cd, uint32_t notify
, struct can2040_msg *msg);
+struct can2040_stats {
+ uint32_t rx_total, tx_total;
+ uint32_t tx_attempt;
+ uint32_t parse_error;
+};
+
void can2040_setup(struct can2040 *cd, uint32_t pio_num);
void can2040_callback_config(struct can2040 *cd, can2040_rx_cb rx_cb);
void can2040_start(struct can2040 *cd, uint32_t sys_clock, uint32_t bitrate
, uint32_t gpio_rx, uint32_t gpio_tx);
-void can2040_shutdown(struct can2040 *cd);
+void can2040_stop(struct can2040 *cd);
+void can2040_get_statistics(struct can2040 *cd, struct can2040_stats *stats);
void can2040_pio_irq_handler(struct can2040 *cd);
int can2040_check_transmit(struct can2040 *cd);
int can2040_transmit(struct can2040 *cd, struct can2040_msg *msg);
@@ -56,6 +63,7 @@ struct can2040 {
void *pio_hw;
uint32_t gpio_rx, gpio_tx;
can2040_rx_cb rx_cb;
+ struct can2040_stats stats;
// Bit unstuffing
struct can2040_bitunstuffer unstuf;
diff --git a/src/generic/usb_canbus.c b/src/generic/usb_canbus.c
index d776d45f248a..9c2893bf231b 100644
--- a/src/generic/usb_canbus.c
+++ b/src/generic/usb_canbus.c
@@ -104,20 +104,18 @@ struct gs_host_frame {
static struct usbcan_data {
struct task_wake wake;
- // Canbus data from host
- union {
- struct gs_host_frame host_frame;
- uint8_t rx_frame_pad[USB_CDC_EP_BULK_OUT_SIZE];
- };
- uint8_t host_status;
-
// Canbus data routed locally
uint8_t notify_local, usb_send_busy;
uint32_t assigned_id;
+ // Canbus data from host
+ uint8_t host_status;
+ uint32_t host_pull_pos, host_push_pos;
+ struct gs_host_frame host_frames[16];
+
// Data from physical canbus interface
- uint32_t pull_pos, push_pos;
- struct canbus_msg queue[32];
+ uint32_t canhw_pull_pos, canhw_push_pos;
+ struct canbus_msg canhw_queue[32];
} UsbCan;
enum {
@@ -139,16 +137,16 @@ void
canbus_process_data(struct canbus_msg *msg)
{
// Add to admin command queue
- uint32_t pushp = UsbCan.push_pos;
- if (pushp - UsbCan.pull_pos >= ARRAY_SIZE(UsbCan.queue))
+ uint32_t pushp = UsbCan.canhw_push_pos;
+ if (pushp - UsbCan.canhw_pull_pos >= ARRAY_SIZE(UsbCan.canhw_queue))
// No space - drop message
return;
if (UsbCan.assigned_id && (msg->id & ~1) == UsbCan.assigned_id)
// Id reserved for local
return;
- uint32_t pos = pushp % ARRAY_SIZE(UsbCan.queue);
- memcpy(&UsbCan.queue[pos], msg, sizeof(*msg));
- UsbCan.push_pos = pushp + 1;
+ uint32_t pos = pushp % ARRAY_SIZE(UsbCan.canhw_queue);
+ memcpy(&UsbCan.canhw_queue[pos], msg, sizeof(*msg));
+ UsbCan.canhw_push_pos = pushp + 1;
usb_notify_bulk_out();
}
@@ -167,24 +165,43 @@ send_frame(struct canbus_msg *msg)
// Send any pending hw frames to host
static void
-drain_hw_queue(void)
+drain_canhw_queue(void)
{
- uint32_t pull_pos = UsbCan.pull_pos;
+ uint32_t pull_pos = UsbCan.canhw_pull_pos;
for (;;) {
- uint32_t push_pos = readl(&UsbCan.push_pos);
+ uint32_t push_pos = readl(&UsbCan.canhw_push_pos);
if (push_pos == pull_pos) {
// No more data to send
UsbCan.usb_send_busy = 0;
return;
}
- uint32_t pos = pull_pos % ARRAY_SIZE(UsbCan.queue);
- int ret = send_frame(&UsbCan.queue[pos]);
+ uint32_t pos = pull_pos % ARRAY_SIZE(UsbCan.canhw_queue);
+ int ret = send_frame(&UsbCan.canhw_queue[pos]);
if (ret < 0) {
// USB is busy - retry later
UsbCan.usb_send_busy = 1;
return;
}
- UsbCan.pull_pos = pull_pos = pull_pos + 1;
+ UsbCan.canhw_pull_pos = pull_pos = pull_pos + 1;
+ }
+}
+
+// Fill local queue with any USB messages sent from host
+static void
+fill_usb_host_queue(void)
+{
+ uint32_t pull_pos = UsbCan.host_pull_pos, push_pos = UsbCan.host_push_pos;
+ for (;;) {
+ if (push_pos - pull_pos >= ARRAY_SIZE(UsbCan.host_frames))
+ // No more space in queue
+ break;
+ uint32_t pushp = push_pos % ARRAY_SIZE(UsbCan.host_frames);
+ struct gs_host_frame *gs = &UsbCan.host_frames[pushp];
+ int ret = usb_read_bulk_out(gs, sizeof(*gs));
+ if (ret <= 0)
+ // No more messages ready
+ break;
+ UsbCan.host_push_pos = push_pos = push_pos + 1;
}
}
@@ -195,13 +212,19 @@ usbcan_task(void)
return;
// Send any pending hw frames to host
- drain_hw_queue();
+ drain_canhw_queue();
+
+ // Fill local queue with any USB messages arriving from host
+ fill_usb_host_queue();
+ // Route messages received from host
+ uint32_t pull_pos = UsbCan.host_pull_pos, push_pos = UsbCan.host_push_pos;
+ uint32_t pullp = pull_pos % ARRAY_SIZE(UsbCan.host_frames);
+ struct gs_host_frame *gs = &UsbCan.host_frames[pullp];
for (;;) {
// See if previous host frame needs to be transmitted
uint_fast8_t host_status = UsbCan.host_status;
if (host_status & (HS_TX_HW | HS_TX_LOCAL)) {
- struct gs_host_frame *gs = &UsbCan.host_frame;
struct canbus_msg msg;
msg.id = gs->can_id;
msg.dlc = gs->can_dlc;
@@ -221,23 +244,22 @@ usbcan_task(void)
// Send any previous echo frames
if (host_status) {
- if (UsbCan.usb_send_busy)
+ if (UsbCan.notify_local || UsbCan.usb_send_busy)
// Don't send echo frame until other traffic is sent
- return;
- int ret = usb_send_bulk_in(&UsbCan.host_frame
- , sizeof(UsbCan.host_frame));
+ break;
+ int ret = usb_send_bulk_in(gs, sizeof(*gs));
if (ret < 0)
return;
UsbCan.host_status = 0;
+ UsbCan.host_pull_pos = pull_pos = pull_pos + 1;
}
- // Read next frame from host
- int ret = usb_read_bulk_out(&UsbCan.host_frame
- , USB_CDC_EP_BULK_OUT_SIZE);
- if (ret <= 0)
+ // Process next frame from host
+ if (pull_pos == push_pos)
// No frame available - no more work to be done
break;
- uint32_t id = UsbCan.host_frame.can_id;
+ gs = &UsbCan.host_frames[pull_pos % ARRAY_SIZE(UsbCan.host_frames)];
+ uint32_t id = gs->can_id;
UsbCan.host_status = HS_TX_ECHO | HS_TX_HW;
if (id == CANBUS_ID_ADMIN)
UsbCan.host_status = HS_TX_ECHO | HS_TX_HW | HS_TX_LOCAL;
@@ -245,6 +267,7 @@ usbcan_task(void)
UsbCan.host_status = HS_TX_ECHO | HS_TX_LOCAL;
}
+ // Wake up local message response handling (if usb is not busy)
if (UsbCan.notify_local && !UsbCan.usb_send_busy)
canserial_notify_tx();
}
@@ -258,6 +281,8 @@ canbus_send(struct canbus_msg *msg)
int ret = send_frame(msg);
if (ret < 0)
goto retry_later;
+ if (UsbCan.notify_local && UsbCan.host_status)
+ canbus_notify_tx();
UsbCan.notify_local = 0;
return msg->dlc;
retry_later:
diff --git a/src/rp2040/usbserial.c b/src/rp2040/usbserial.c
index e63e590dc640..61d04d1891a5 100644
--- a/src/rp2040/usbserial.c
+++ b/src/rp2040/usbserial.c
@@ -1,6 +1,6 @@
// Hardware interface to USB on rp2040
//
-// Copyright (C) 2021 Kevin O'Connor
+// Copyright (C) 2021-2023 Kevin O'Connor
//
// This file may be distributed under the terms of the GNU GPLv3 license.
@@ -26,52 +26,46 @@
#define DPBUF_SIZE 64
+// Get the offset of a given endpoint's base buffer
static uint32_t
usb_buf_offset(uint32_t ep)
{
return 0x100 + ep * DPBUF_SIZE * 2;
}
-static int_fast8_t
-usb_write_packet(uint32_t ep, const void *data, uint_fast8_t len)
+// Obtain a pointer to an endpoint buffer
+static void*
+usb_buf_addr(uint32_t ep, int bufnum)
{
- // Check if there is room for this packet
- uint32_t epb = usb_dpram->ep_buf_ctrl[ep].in;
- if (epb & (USB_BUF_CTRL_AVAIL|USB_BUF_CTRL_FULL))
- return -1;
- uint32_t pid = (epb ^ USB_BUF_CTRL_DATA1_PID) & USB_BUF_CTRL_DATA1_PID;
- uint32_t new_epb = USB_BUF_CTRL_FULL | USB_BUF_CTRL_LAST | pid | len;
- usb_dpram->ep_buf_ctrl[ep].in = new_epb;
- // Copy the packet to the hw buffer
- void *addr = (void*)usb_dpram + usb_buf_offset(ep);
- barrier();
- memcpy(addr, data, len);
- barrier();
- // Inform the USB hardware of the available packet
- usb_dpram->ep_buf_ctrl[ep].in = new_epb | USB_BUF_CTRL_AVAIL;
- return len;
+ return (void*)usb_dpram + usb_buf_offset(ep) + bufnum * DPBUF_SIZE;
}
-static int_fast8_t
-usb_read_packet(uint32_t ep, void *data, uint_fast8_t max_len)
+// Return a pointer to the ep_buf_ctrl register for an endpoint
+static volatile uint16_t *
+lookup_epbufctrl(uint32_t ep, int is_rx, int bufnum)
+{
+ volatile uint16_t *epbp;
+ if (is_rx)
+ epbp = (void*)&usb_dpram->ep_buf_ctrl[ep].out;
+ else
+ epbp = (void*)&usb_dpram->ep_buf_ctrl[ep].in;
+ return &epbp[bufnum];
+}
+
+// Determine the next transfer PID id from the last PID
+static uint32_t
+next_data_pid(uint32_t epb)
+{
+ return (epb ^ USB_BUF_CTRL_DATA1_PID) & USB_BUF_CTRL_DATA1_PID;
+}
+
+// Extract the number of bytes in an rx buffer
+static uint32_t
+get_rx_count(uint32_t epb, uint32_t max_len)
{
- // Check if there is a packet ready
- uint32_t epb = usb_dpram->ep_buf_ctrl[ep].out;
- if ((epb & (USB_BUF_CTRL_AVAIL|USB_BUF_CTRL_FULL)) != USB_BUF_CTRL_FULL)
- return -1;
- // Copy the packet to the given buffer
- uint32_t pid = (epb ^ USB_BUF_CTRL_DATA1_PID) & USB_BUF_CTRL_DATA1_PID;
- uint32_t new_epb = USB_BUF_CTRL_LAST | pid | DPBUF_SIZE;
- usb_dpram->ep_buf_ctrl[ep].out = new_epb;
uint32_t c = epb & USB_BUF_CTRL_LEN_MASK;
if (c > max_len)
c = max_len;
- void *addr = (void*)usb_dpram + usb_buf_offset(ep);
- barrier();
- memcpy(data, addr, c);
- barrier();
- // Notify the USB hardware that the space is now available
- usb_dpram->ep_buf_ctrl[ep].out = new_epb | USB_BUF_CTRL_AVAIL;
return c;
}
@@ -80,16 +74,57 @@ usb_read_packet(uint32_t ep, void *data, uint_fast8_t max_len)
* Interface
****************************************************************/
+static uint32_t bulk_out_push_count;
+
int_fast8_t
usb_read_bulk_out(void *data, uint_fast8_t max_len)
{
- return usb_read_packet(USB_CDC_EP_BULK_OUT, data, max_len);
+ // Check if there is a packet ready
+ uint32_t bopc = bulk_out_push_count, bufnum = bopc & 1;
+ uint32_t ep = USB_CDC_EP_BULK_OUT;
+ volatile uint16_t *epbp = lookup_epbufctrl(ep, 1, bufnum);
+ uint32_t epb = *epbp;
+ if ((epb & (USB_BUF_CTRL_AVAIL|USB_BUF_CTRL_FULL)) != USB_BUF_CTRL_FULL)
+ return -1;
+ // Determine the next packet header
+ bulk_out_push_count = bopc + 1;
+ uint32_t pid = bufnum ? USB_BUF_CTRL_DATA1_PID : 0;
+ uint32_t new_epb = USB_BUF_CTRL_LAST | pid | DPBUF_SIZE;
+ *epbp = new_epb;
+ barrier();
+ // Copy the packet to the given buffer
+ uint32_t c = get_rx_count(epb, max_len);
+ memcpy(data, usb_buf_addr(ep, bufnum), c);
+ // Notify the USB hardware that the space is now available
+ barrier();
+ *epbp = new_epb | USB_BUF_CTRL_AVAIL;
+ return c;
}
+static uint32_t bulk_in_pop_count;
+
int_fast8_t
usb_send_bulk_in(void *data, uint_fast8_t len)
{
- return usb_write_packet(USB_CDC_EP_BULK_IN, data, len);
+ // Check if there is room for this packet
+ uint32_t bipc = bulk_in_pop_count, bufnum = bipc & 1;
+ uint32_t ep = USB_CDC_EP_BULK_IN;
+ volatile uint16_t *epbp = lookup_epbufctrl(ep, 0, bufnum);
+ uint32_t epb = *epbp;
+ if (epb & (USB_BUF_CTRL_AVAIL|USB_BUF_CTRL_FULL))
+ return -1;
+ // Determine the next packet header
+ bulk_in_pop_count = bipc + 1;
+ uint32_t pid = bufnum ? USB_BUF_CTRL_DATA1_PID : 0;
+ uint32_t new_epb = USB_BUF_CTRL_FULL | USB_BUF_CTRL_LAST | pid | len;
+ *epbp = new_epb;
+ barrier();
+ // Copy the packet to the hw buffer
+ memcpy(usb_buf_addr(ep, bufnum), data, len);
+ // Inform the USB hardware of the available packet
+ barrier();
+ *epbp = new_epb | USB_BUF_CTRL_AVAIL;
+ return len;
}
int_fast8_t
@@ -118,19 +153,51 @@ usb_read_ep0_setup(void *data, uint_fast8_t max_len)
int_fast8_t
usb_read_ep0(void *data, uint_fast8_t max_len)
{
+ // Check if there is a packet ready
+ uint32_t ep = 0;
if (usb_hw->intr & USB_INTR_SETUP_REQ_BITS)
// Early end of transmission
return -2;
- return usb_read_packet(0, data, max_len);
+ volatile uint16_t *epbp = lookup_epbufctrl(ep, 1, 0);
+ uint32_t epb = *epbp;
+ if ((epb & (USB_BUF_CTRL_AVAIL|USB_BUF_CTRL_FULL)) != USB_BUF_CTRL_FULL)
+ return -1;
+ // Determine the next packet header
+ uint32_t new_epb = USB_BUF_CTRL_LAST | next_data_pid(epb) | DPBUF_SIZE;
+ *epbp = new_epb;
+ barrier();
+ // Copy the packet to the given buffer
+ uint32_t c = get_rx_count(epb, max_len);
+ memcpy(data, usb_buf_addr(ep, 0), c);
+ // Notify the USB hardware that the space is now available
+ barrier();
+ *epbp = new_epb | USB_BUF_CTRL_AVAIL;
+ return c;
}
int_fast8_t
usb_send_ep0(const void *data, uint_fast8_t len)
{
+ // Check if there is room for this packet
+ uint32_t ep = 0;
if (usb_hw->intr & USB_INTR_SETUP_REQ_BITS || usb_hw->buf_status & 2)
// Early end of transmission
return -2;
- return usb_write_packet(0, data, len);
+ volatile uint16_t *epbp = lookup_epbufctrl(ep, 0, 0);
+ uint32_t epb = *epbp;
+ if (epb & (USB_BUF_CTRL_AVAIL|USB_BUF_CTRL_FULL))
+ return -1;
+ // Determine the next packet header
+ uint32_t pid = next_data_pid(epb);
+ uint32_t new_epb = USB_BUF_CTRL_FULL | USB_BUF_CTRL_LAST | pid | len;
+ *epbp = new_epb;
+ barrier();
+ // Copy the packet to the hw buffer
+ memcpy(usb_buf_addr(ep, 0), data, len);
+ // Inform the USB hardware of the available packet
+ barrier();
+ *epbp = new_epb | USB_BUF_CTRL_AVAIL;
+ return len;
}
void
@@ -156,9 +223,13 @@ usb_set_address(uint_fast8_t addr)
void
usb_set_configure(void)
{
- usb_dpram->ep_buf_ctrl[USB_CDC_EP_BULK_IN].in = USB_BUF_CTRL_DATA1_PID;
- usb_dpram->ep_buf_ctrl[USB_CDC_EP_BULK_OUT].out = (
- USB_BUF_CTRL_AVAIL | USB_BUF_CTRL_LAST | DPBUF_SIZE);
+ bulk_in_pop_count = 0;
+ usb_dpram->ep_buf_ctrl[USB_CDC_EP_BULK_IN].in = 0;
+
+ bulk_out_push_count = 0;
+ uint32_t epb0 = USB_BUF_CTRL_AVAIL | USB_BUF_CTRL_LAST | DPBUF_SIZE;
+ uint32_t epb1 = epb0 | USB_BUF_CTRL_DATA1_PID;
+ usb_dpram->ep_buf_ctrl[USB_CDC_EP_BULK_OUT].out = epb0 | (epb1 << 16);
}
@@ -290,10 +361,12 @@ endpoint_setup(void)
usb_dpram->ep_ctrl[USB_CDC_EP_ACM-1].in = ep_acm;
// BULK
uint32_t ep_out = (EP_CTRL_ENABLE_BITS | usb_buf_offset(USB_CDC_EP_BULK_OUT)
+ | EP_CTRL_DOUBLE_BUFFERED_BITS
| EP_CTRL_INTERRUPT_PER_BUFFER
| (USB_ENDPOINT_XFER_BULK << EP_CTRL_BUFFER_TYPE_LSB));
usb_dpram->ep_ctrl[USB_CDC_EP_BULK_OUT-1].out = ep_out;
uint32_t ep_in = (EP_CTRL_ENABLE_BITS | usb_buf_offset(USB_CDC_EP_BULK_IN)
+ | EP_CTRL_DOUBLE_BUFFERED_BITS
| EP_CTRL_INTERRUPT_PER_BUFFER
| (USB_ENDPOINT_XFER_BULK << EP_CTRL_BUFFER_TYPE_LSB));
usb_dpram->ep_ctrl[USB_CDC_EP_BULK_IN-1].in = ep_in;
diff --git a/src/stm32/usbfs.c b/src/stm32/usbfs.c
index fda2ce9d299c..ad2e7b3eb56e 100644
--- a/src/stm32/usbfs.c
+++ b/src/stm32/usbfs.c
@@ -1,6 +1,6 @@
// Hardware interface to "fullspeed USB controller"
//
-// Copyright (C) 2018-2021 Kevin O'Connor
+// Copyright (C) 2018-2023 Kevin O'Connor
//
// This file may be distributed under the terms of the GNU GPLv3 license.
@@ -48,6 +48,12 @@
#define USB_CNTR_FRES USB_CNTR_USBRST
#endif
+// Some chip variants do not define these fields
+#ifndef USB_EP_DTOG_TX_Pos
+#define USB_EP_DTOG_TX_Pos 6
+#define USB_EP_DTOG_RX_Pos 14
+#endif
+
/****************************************************************
* USB transfer memory
@@ -55,52 +61,50 @@
// Layout of the USB transfer memory
#define EPM ((epmword_t*)USB_PMAADDR)
-#define EPM_EP_DESC(ep) (&EPM[(ep) * (8 / WSIZE)])
+#define EPM_EP_DESC(ep, bufnum) (&EPM[((ep)*2 + (bufnum)) * (4 / WSIZE)])
#define EPM_BUF_OFFSET 0x10
#define EPM_EP_BUF_SIZE (64 / WSIZE + 1)
-#define EPM_EP_TX_BUF(ep) (&EPM[EPM_BUF_OFFSET + (ep)*2*EPM_EP_BUF_SIZE])
-#define EPM_EP_RX_BUF(ep) (&EPM[EPM_BUF_OFFSET + (1+(ep)*2)*EPM_EP_BUF_SIZE])
+#define EPM_EP_BUF(ep, bufnum) \
+ (&EPM[EPM_BUF_OFFSET + ((ep)*2 + (bufnum)) * EPM_EP_BUF_SIZE])
+#define BUFTX 0
+#define BUFRX 1
// Configure the usb descriptor for an endpoint
static void
-epm_ep_desc_setup(int ep, int rx_size)
+epm_ep_desc_setup(int ep, int bufnum, int rx_size)
{
- uint32_t addr_tx = (EPM_EP_TX_BUF(ep) - EPM) * WSIZE, count_tx = 0;
- uint32_t addr_rx = (EPM_EP_RX_BUF(ep) - EPM) * WSIZE;
+ uint32_t addr = (EPM_EP_BUF(ep, bufnum) - EPM) * WSIZE;
uint32_t count_rx = (rx_size <= 30 ? DIV_ROUND_UP(rx_size, 2) << 10
: ((DIV_ROUND_UP(rx_size, 32) - 1) << 10) | 0x8000);
- epmword_t *desc = EPM_EP_DESC(ep);
+ epmword_t *desc = EPM_EP_DESC(ep, bufnum);
if (WSIZE == 2) {
- desc[0] = addr_tx;
- desc[1] = count_tx;
- desc[2] = addr_rx;
- desc[3] = count_rx;
+ desc[0] = addr;
+ desc[1] = count_rx;
} else {
- desc[0] = addr_tx | (count_tx << 16);
- desc[1] = addr_rx | (count_rx << 16);
+ *desc = addr | (count_rx << 16);
}
}
// Return number of read bytes on an rx endpoint
static uint32_t
-epm_get_ep_count_rx(int ep)
+epm_get_ep_count_rx(int ep, int bufnum)
{
- epmword_t *desc = EPM_EP_DESC(ep);
+ epmword_t *desc = EPM_EP_DESC(ep, bufnum);
if (WSIZE == 2)
- return desc[3] & 0x3ff;
- return (desc[1] >> 16) & 0x3ff;
+ return desc[1] & 0x3ff;
+ return (*desc >> 16) & 0x3ff;
}
// Set number of bytes ready to be transmitted on a tx endpoint
static void
-epm_set_ep_count_tx(int ep, uint32_t count)
+epm_set_ep_count_tx(int ep, int bufnum, uint32_t count)
{
- epmword_t *desc = EPM_EP_DESC(ep);
+ epmword_t *desc = EPM_EP_DESC(ep, bufnum);
if (WSIZE == 2) {
desc[1] = count;
} else {
- uint32_t addr_tx = (EPM_EP_TX_BUF(ep) - EPM) * WSIZE;
- desc[0] = addr_tx | (count << 16);
+ uint32_t addr_tx = (EPM_EP_BUF(ep, bufnum) - EPM) * WSIZE;
+ *desc = addr_tx | (count << 16);
}
}
@@ -108,18 +112,22 @@ epm_set_ep_count_tx(int ep, uint32_t count)
static void
btable_configure(void)
{
- epm_ep_desc_setup(0, USB_CDC_EP0_SIZE);
- epm_ep_desc_setup(USB_CDC_EP_ACM, 0);
- epm_ep_desc_setup(USB_CDC_EP_BULK_OUT, USB_CDC_EP_BULK_OUT_SIZE);
- epm_ep_desc_setup(USB_CDC_EP_BULK_IN, 0);
+ epm_ep_desc_setup(0, BUFTX, 0);
+ epm_ep_desc_setup(0, BUFRX, USB_CDC_EP0_SIZE);
+ epm_ep_desc_setup(USB_CDC_EP_ACM, BUFTX, 0);
+ epm_ep_desc_setup(USB_CDC_EP_ACM, BUFRX, 0);
+ epm_ep_desc_setup(USB_CDC_EP_BULK_OUT, 0, USB_CDC_EP_BULK_OUT_SIZE);
+ epm_ep_desc_setup(USB_CDC_EP_BULK_OUT, 1, USB_CDC_EP_BULK_OUT_SIZE);
+ epm_ep_desc_setup(USB_CDC_EP_BULK_IN, 0, 0);
+ epm_ep_desc_setup(USB_CDC_EP_BULK_IN, 1, 0);
}
// Read a packet stored in dedicated usb memory
static uint32_t
-btable_read_packet(int ep, uint8_t *dest, int max_len)
+btable_read_packet(int ep, int bufnum, uint8_t *dest, int max_len)
{
- epmword_t *src = EPM_EP_RX_BUF(ep);
- uint32_t count = epm_get_ep_count_rx(ep);
+ epmword_t *src = EPM_EP_BUF(ep, bufnum);
+ uint32_t count = epm_get_ep_count_rx(ep, bufnum);
if (count > max_len)
count = max_len;
int i;
@@ -145,9 +153,9 @@ btable_read_packet(int ep, uint8_t *dest, int max_len)
// Write a packet to dedicated usb memory
static void
-btable_write_packet(int ep, const uint8_t *src, int count)
+btable_write_packet(int ep, int bufnum, const uint8_t *src, int count)
{
- epmword_t *dest = EPM_EP_TX_BUF(ep);
+ epmword_t *dest = EPM_EP_BUF(ep, bufnum);
int i;
for (i=0; i> (USB_EP_DTOG_RX_Pos - USB_EP_DTOG_TX_Pos)) ^ epr)
+ & USB_EP_DTOG_TX);
}
@@ -202,39 +211,77 @@ set_stat_rxtx_bits(uint32_t epr, uint32_t bits)
* USB interface
****************************************************************/
+static uint32_t bulk_out_pop_count, bulk_out_push_flag;
+
int_fast8_t
usb_read_bulk_out(void *data, uint_fast8_t max_len)
{
- uint32_t epr = USB_EPR[USB_CDC_EP_BULK_OUT];
- if ((epr & USB_EPRX_STAT) == USB_EP_RX_VALID)
+ if (readl(&bulk_out_push_flag))
// No data ready
return -1;
- uint32_t count = btable_read_packet(USB_CDC_EP_BULK_OUT, data, max_len);
- USB_EPR[USB_CDC_EP_BULK_OUT] = set_stat_rx_bits(epr, USB_EP_RX_VALID);
+ uint32_t ep = USB_CDC_EP_BULK_OUT;
+ int bufnum = bulk_out_pop_count & 1;
+ bulk_out_pop_count++;
+ uint32_t count = btable_read_packet(ep, bufnum, data, max_len);
+ writel(&bulk_out_push_flag, USB_EP_DTOG_TX);
+
+ // Check if irq handler pulled another packet before push flag update
+ uint32_t epr = USB_EPR[ep];
+ if (epr_is_dbuf_blocking(epr) && readl(&bulk_out_push_flag)) {
+ // Second packet was already read - must notify hardware
+ writel(&bulk_out_push_flag, 0);
+ USB_EPR[ep] = calc_epr_bits(epr, 0, 0) | USB_EP_DTOG_TX;
+ }
+
return count;
}
+static uint32_t bulk_in_push_pos, bulk_in_pop_flag;
+#define BI_START 2
+
int_fast8_t
usb_send_bulk_in(void *data, uint_fast8_t len)
{
- uint32_t epr = USB_EPR[USB_CDC_EP_BULK_IN];
- if ((epr & USB_EPTX_STAT) != USB_EP_TX_NAK)
+ if (readl(&bulk_in_pop_flag))
// No buffer space available
return -1;
- btable_write_packet(USB_CDC_EP_BULK_IN, data, len);
- USB_EPR[USB_CDC_EP_BULK_IN] = set_stat_tx_bits(epr, USB_EP_TX_VALID);
+ uint32_t ep = USB_CDC_EP_BULK_IN;
+ uint32_t bipp = bulk_in_push_pos, bufnum = bipp & 1;
+ bulk_in_push_pos = bipp ^ 1;
+ btable_write_packet(ep, bufnum, data, len);
+ writel(&bulk_in_pop_flag, USB_EP_DTOG_RX);
+
+ // Check if hardware needs to be notified
+ uint32_t epr = USB_EPR[ep];
+ if (epr_is_dbuf_blocking(epr) && readl(&bulk_in_pop_flag)) {
+ writel(&bulk_in_pop_flag, 0);
+ if (unlikely(bipp & BI_START)) {
+ // Two packets are always sent when starting in double
+ // buffering mode, so wait for second packet before starting.
+ if (bipp == (BI_START | 1)) {
+ bulk_in_push_pos = 0;
+ writel(&bulk_in_pop_flag, USB_EP_KIND); // Dummy flag
+ USB_EPR[ep] = calc_epr_bits(epr, USB_EPTX_STAT
+ , USB_EP_TX_VALID);
+ }
+ } else {
+ USB_EPR[ep] = calc_epr_bits(epr, 0, 0) | USB_EP_DTOG_RX;
+ }
+ }
+
return len;
}
int_fast8_t
usb_read_ep0(void *data, uint_fast8_t max_len)
{
- uint32_t epr = USB_EPR[0];
+ uint32_t ep = 0, epr = USB_EPR[ep];
if ((epr & USB_EPRX_STAT) != USB_EP_RX_NAK)
// No data ready
return -1;
- uint32_t count = btable_read_packet(0, data, max_len);
- USB_EPR[0] = set_stat_rxtx_bits(epr, USB_EP_RX_VALID | USB_EP_TX_NAK);
+ uint32_t count = btable_read_packet(ep, BUFRX, data, max_len);
+ USB_EPR[ep] = calc_epr_bits(epr, USB_EPRX_STAT | USB_EPTX_STAT
+ , USB_EP_RX_VALID | USB_EP_TX_NAK);
return count;
}
@@ -247,23 +294,24 @@ usb_read_ep0_setup(void *data, uint_fast8_t max_len)
int_fast8_t
usb_send_ep0(const void *data, uint_fast8_t len)
{
- uint32_t epr = USB_EPR[0];
+ uint32_t ep = 0, epr = USB_EPR[ep];
if ((epr & USB_EPRX_STAT) != USB_EP_RX_VALID)
// Transfer interrupted
return -2;
if ((epr & USB_EPTX_STAT) != USB_EP_TX_NAK)
// No buffer space available
return -1;
- btable_write_packet(0, data, len);
- USB_EPR[0] = set_stat_tx_bits(epr, USB_EP_TX_VALID);
+ btable_write_packet(ep, BUFTX, data, len);
+ USB_EPR[ep] = calc_epr_bits(epr, USB_EPTX_STAT, USB_EP_TX_VALID);
return len;
}
void
usb_stall_ep0(void)
{
- USB_EPR[0] = set_stat_rxtx_bits(USB_EPR[0]
- , USB_EP_RX_STALL | USB_EP_TX_STALL);
+ uint32_t ep = 0, epr = USB_EPR[ep];
+ USB_EPR[ep] = calc_epr_bits(epr, USB_EPRX_STAT | USB_EPTX_STAT
+ , USB_EP_RX_STALL | USB_EP_TX_STALL);
}
static uint8_t set_address;
@@ -278,6 +326,13 @@ usb_set_address(uint_fast8_t addr)
void
usb_set_configure(void)
{
+ uint32_t ep = USB_CDC_EP_BULK_OUT;
+ bulk_out_pop_count = 0;
+ USB_EPR[ep] = calc_epr_bits(USB_EPR[ep], USB_EPRX_STAT, USB_EP_RX_VALID);
+
+ ep = USB_CDC_EP_BULK_IN;
+ bulk_in_push_pos = BI_START;
+ writel(&bulk_in_pop_flag, 0);
}
@@ -289,13 +344,22 @@ usb_set_configure(void)
static void
usb_reset(void)
{
- USB_EPR[0] = 0 | USB_EP_CONTROL | USB_EP_RX_VALID | USB_EP_TX_NAK;
- USB_EPR[USB_CDC_EP_ACM] = (USB_CDC_EP_ACM | USB_EP_INTERRUPT
- | USB_EP_RX_NAK | USB_EP_TX_NAK);
- USB_EPR[USB_CDC_EP_BULK_OUT] = (USB_CDC_EP_BULK_OUT | USB_EP_BULK
- | USB_EP_RX_VALID | USB_EP_TX_NAK);
- USB_EPR[USB_CDC_EP_BULK_IN] = (USB_CDC_EP_BULK_IN | USB_EP_BULK
- | USB_EP_RX_NAK | USB_EP_TX_NAK);
+ uint32_t ep = 0;
+ USB_EPR[ep] = 0 | USB_EP_CONTROL | USB_EP_RX_VALID | USB_EP_TX_NAK;
+
+ ep = USB_CDC_EP_ACM;
+ USB_EPR[ep] = (USB_CDC_EP_ACM | USB_EP_INTERRUPT
+ | USB_EP_RX_NAK | USB_EP_TX_NAK);
+
+ ep = USB_CDC_EP_BULK_OUT;
+ USB_EPR[ep] = (USB_CDC_EP_BULK_OUT | USB_EP_BULK | USB_EP_KIND
+ | USB_EP_RX_NAK | USB_EP_DTOG_TX);
+ bulk_out_push_flag = USB_EP_DTOG_TX;
+
+ ep = USB_CDC_EP_BULK_IN;
+ USB_EPR[ep] = (USB_CDC_EP_BULK_IN | USB_EP_BULK | USB_EP_KIND
+ | USB_EP_TX_NAK);
+ bulk_in_pop_flag = USB_EP_DTOG_RX;
USB->CNTR = USB_CNTR_CTRM | USB_CNTR_RESETM;
USB->DADDR = USB_DADDR_EF;
@@ -308,20 +372,25 @@ USB_IRQHandler(void)
uint32_t istr = USB->ISTR;
if (istr & USB_ISTR_CTR) {
// Endpoint activity
- uint32_t ep = istr & USB_ISTR_EP_ID;
- uint32_t epr = USB_EPR[ep];
- USB_EPR[ep] = epr & EPR_RWBITS;
- if (ep == 0) {
+ uint32_t ep = istr & USB_ISTR_EP_ID, epr = USB_EPR[ep];
+ if (ep == USB_CDC_EP_BULK_OUT) {
+ USB_EPR[ep] = (calc_epr_bits(epr, USB_EP_CTR_RX | USB_EP_CTR_TX, 0)
+ | bulk_out_push_flag);
+ bulk_out_push_flag = 0;
+ usb_notify_bulk_out();
+ } else if (ep == USB_CDC_EP_BULK_IN) {
+ USB_EPR[ep] = (calc_epr_bits(epr, USB_EP_CTR_RX | USB_EP_CTR_TX, 0)
+ | bulk_in_pop_flag);
+ bulk_in_pop_flag = 0;
+ usb_notify_bulk_in();
+ } else if (ep == 0) {
+ USB_EPR[ep] = calc_epr_bits(epr, USB_EP_CTR_RX | USB_EP_CTR_TX, 0);
usb_notify_ep0();
if (epr & USB_EP_CTR_TX && set_address) {
// Apply address after last "in" message transmitted
USB->DADDR = set_address;
set_address = 0;
}
- } else if (ep == USB_CDC_EP_BULK_OUT) {
- usb_notify_bulk_out();
- } else if (ep == USB_CDC_EP_BULK_IN) {
- usb_notify_bulk_in();
}
}
if (istr & USB_ISTR_RESET) {