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) {