diff --git a/README.md b/README.md
index 0104d1bcb584..bea9a030c3ef 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,25 @@
+# Changes
+M18/M84 supports individual axes(M18 X will only turn off steppers associated with X, if you are running CoreXY, X and Y will be turned off as they are connected)
+G28 supports conditional homing (G28 X0 will only home X if X has not been homed yet)
+PID Profiles: generate profiles with PID values for different temperatures and load them with a command without restarting
+Improved PID tuning algorithm and velocity based PID algorithm (https://github.com/Klipper3d/klipper/pull/5955)
+Filament Switch sensors support Marlins Runout Distance (can also be changed on the fly) (currently on the runout_test branch)
+Detection Length for Filament Motion Sensors can be changed on the fly (currently on the runout_test branch)
+Implementation of Marlins Cold Extrude
+Fixed some stuff in the stepper_enable code
+Added support for the run_on_error feature from the LED-Effect plugin (also requires our forked version of LED-Effect)
+Added jinja Loop-Controls
+Implemented controller_temperature_fan (look at the doc for explanation)
+Implemented "curve" control algorithm for temperature_fan (look at the doc for explanation)
+Other minor fixes and debug features that do not impact general use
+
+All commands and config options for the ffeatures should be documented in the docs, if I forgot something, feel free to open an issue and I will get to it ASAP
+
+Pretty much every normal klipper config is compatible, except for one thing: https://github.com/Klipper3d/klipper/pull/6307
+
+
+
+# Original README
Welcome to the Klipper project!
[![Klipper](docs/img/klipper-logo-small.png)](https://www.klipper3d.org/)
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/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/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) {