queue: convert to Linux compatible list implementation

Convert from the queue implementation to a Linux compatible list
implementation. Get rid of This way we can avoid dynamic memory
allocation altogether.

As the struct gs_host_frame is not placed into the data segment,
requirements for the static memory grows. On the low end processors
the heap reserved in the linker file is too big, resulting in a linker
error. On STM32F042 and STM32F072 set a HEAP size of 0 bytes.
This commit is contained in:
Marc Kleine-Budde 2022-11-11 22:45:52 +01:00
parent 57d9900f5b
commit 35d6cb8941
4 changed files with 99 additions and 51 deletions

View File

@ -170,7 +170,7 @@ populate_ldscript(CPU_FAMILY STM32F042X6
RAM_START 0x20000000
RAM_SIZE 6k
STACK_SIZE 1k
HEAP_SIZE 1k
HEAP_SIZE 0k
)
populate_ldscript(CPU_FAMILY STM32F072XB
@ -179,7 +179,7 @@ populate_ldscript(CPU_FAMILY STM32F072XB
RAM_START 0x20000000
RAM_SIZE 16k
STACK_SIZE 2k
HEAP_SIZE 1k
HEAP_SIZE 0k
)
populate_ldscript(CPU_FAMILY STM32F407XE

View File

@ -33,7 +33,7 @@ THE SOFTWARE.
#include "config.h"
#include "gs_usb.h"
#include "led.h"
#include "queue.h"
#include "list.h"
#include "usbd_def.h"
/* Define these here so they can be referenced in other files */
@ -48,6 +48,11 @@ THE SOFTWARE.
extern USBD_ClassTypeDef USBD_GS_CAN;
struct gs_host_frame_object {
struct list_head list;
struct gs_host_frame frame;
};
typedef struct {
uint8_t ep0_buf[CAN_CMD_PACKET_SIZE];
@ -55,10 +60,11 @@ typedef struct {
USBD_SetupReqTypedef last_setup_request;
queue_t *q_frame_pool;
queue_t *q_from_host;
struct list_head list_frame_pool;
struct list_head list_from_host;
struct list_head list_to_host;
struct gs_host_frame *from_host_buf;
struct gs_host_frame_object *from_host_buf;
can_data_t channels[NUM_CAN_CHANNEL];
@ -70,7 +76,7 @@ typedef struct {
bool pad_pkts_to_max_pkt_size;
struct gs_host_frame msgbuf[CAN_QUEUE_SIZE];
struct gs_host_frame_object msgbuf[CAN_QUEUE_SIZE];
} USBD_GS_CAN_HandleTypeDef __attribute__ ((aligned (4)));
#if defined(STM32F0)
@ -86,7 +92,7 @@ typedef struct {
# define USB_RX_FIFO_SIZE ((256U / 4U) + 1U)
#endif
uint8_t USBD_GS_CAN_Init(USBD_GS_CAN_HandleTypeDef *hcan, USBD_HandleTypeDef *pdev, queue_t *q_frame_pool, queue_t *q_from_host, led_data_t *leds);
uint8_t USBD_GS_CAN_Init(USBD_GS_CAN_HandleTypeDef *hcan, USBD_HandleTypeDef *pdev, led_data_t *leds);
void USBD_GS_CAN_SuspendCallback(USBD_HandleTypeDef *pdev);
void USBD_GS_CAN_ResumeCallback(USBD_HandleTypeDef *pdev);
bool USBD_GS_CAN_TxReady(USBD_HandleTypeDef *pdev);

View File

@ -35,7 +35,6 @@ THE SOFTWARE.
#include "gs_usb.h"
#include "hal_include.h"
#include "led.h"
#include "queue.h"
#include "timer.h"
#include "usbd_conf.h"
#include "usbd_core.h"
@ -52,10 +51,6 @@ static USBD_GS_CAN_HandleTypeDef hGS_CAN;
static USBD_HandleTypeDef hUSB = {0};
static led_data_t hLED = {0};
static queue_t *q_frame_pool = NULL;
static queue_t *q_from_host = NULL;
static queue_t *q_to_host = NULL;
int main(void)
{
can_data_t *channel = &hGS_CAN.channels[0];
@ -82,19 +77,17 @@ int main(void)
can_init(channel, CAN_INTERFACE);
can_disable(channel);
INIT_LIST_HEAD(&hGS_CAN.list_frame_pool);
INIT_LIST_HEAD(&hGS_CAN.list_from_host);
INIT_LIST_HEAD(&hGS_CAN.list_to_host);
q_frame_pool = queue_create(CAN_QUEUE_SIZE);
q_from_host = queue_create(CAN_QUEUE_SIZE);
q_to_host = queue_create(CAN_QUEUE_SIZE);
assert_basic(q_frame_pool && q_from_host && q_to_host);
for (unsigned i=0; i<CAN_QUEUE_SIZE; i++) {
queue_push_back(q_frame_pool, &hGS_CAN.msgbuf[i]);
for (unsigned i = 0; i < ARRAY_SIZE(hGS_CAN.msgbuf); i++) {
list_add_tail(&hGS_CAN.msgbuf[i].list, &hGS_CAN.list_frame_pool);
}
USBD_Init(&hUSB, (USBD_DescriptorsTypeDef*)&FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUSB, &USBD_GS_CAN);
USBD_GS_CAN_Init(&hGS_CAN, &hUSB, q_frame_pool, q_from_host, &hLED);
USBD_GS_CAN_Init(&hGS_CAN, &hUSB, &hLED);
USBD_Start(&hUSB);
#ifdef CAN_S_GPIO_Port
@ -102,19 +95,32 @@ int main(void)
#endif
while (1) {
struct gs_host_frame *frame = queue_pop_front(hGS_CAN.q_from_host);
if (frame != 0) { // send can message from host
struct gs_host_frame_object *frame_object;
bool was_irq_enabled = disable_irq();
frame_object = list_first_entry_or_null(&hGS_CAN.list_from_host,
struct gs_host_frame_object,
list);
if (frame_object) { // send CAN message from host
struct gs_host_frame *frame = &frame_object->frame;
list_del(&frame_object->list);
restore_irq(was_irq_enabled);
if (can_send(channel, frame)) {
// Echo sent frame back to host
frame->flags = 0x0;
frame->reserved = 0x0;
frame->timestamp_us = timer_get();
queue_push_back(q_to_host, frame);
list_add_tail_locked(&frame_object->list, &hGS_CAN.list_to_host);
led_indicate_trx(&hLED, led_tx);
} else {
queue_push_front(hGS_CAN.q_from_host, frame); // retry later
list_add_locked(&frame_object->list, &hGS_CAN.list_from_host);
}
} else {
restore_irq(was_irq_enabled);
}
if (USBD_GS_CAN_TxReady(&hUSB)) {
@ -122,9 +128,16 @@ int main(void)
}
if (can_is_rx_pending(channel)) {
struct gs_host_frame *frame = queue_pop_front(hGS_CAN.q_frame_pool);
if (frame != 0)
{
bool was_irq_enabled = disable_irq();
frame_object = list_first_entry_or_null(&hGS_CAN.list_frame_pool,
struct gs_host_frame_object,
list);
if (frame_object) {
struct gs_host_frame *frame = &frame_object->frame;
list_del(&frame_object->list);
restore_irq(was_irq_enabled);
if (can_receive(channel, frame)) {
frame->timestamp_us = timer_get();
@ -133,14 +146,14 @@ int main(void)
frame->flags = 0;
frame->reserved = 0;
queue_push_back(q_to_host, frame);
list_add_tail_locked(&frame_object->list, &hGS_CAN.list_to_host);
led_indicate_trx(&hLED, led_rx);
} else {
list_add_tail_locked(&frame_object->list, &hGS_CAN.list_frame_pool);
}
else
{
queue_push_back(hGS_CAN.q_frame_pool, frame);
}
} else {
restore_irq(was_irq_enabled);
}
// If there are frames to receive, don't report any error frames. The
// best we can localize the errors to is "after the last successfully
@ -148,16 +161,27 @@ int main(void)
// to report even if multiple pass by.
} else {
uint32_t can_err = can_get_error_status(channel);
struct gs_host_frame *frame = queue_pop_front(hGS_CAN.q_frame_pool);
if (frame != 0) {
bool was_irq_enabled = disable_irq();
frame_object = list_first_entry_or_null(&hGS_CAN.list_frame_pool,
struct gs_host_frame_object,
list);
if (frame_object) {
struct gs_host_frame *frame = &frame_object->frame;
list_del(&frame_object->list);
restore_irq(was_irq_enabled);
frame->timestamp_us = timer_get();
if (can_parse_error_status(can_err, last_can_error_status, channel, frame)) {
queue_push_back(q_to_host, frame);
list_add_tail_locked(&frame_object->list, &hGS_CAN.list_to_host);
last_can_error_status = can_err;
} else {
queue_push_back(hGS_CAN.q_frame_pool, frame);
list_add_tail_locked(&frame_object->list, &hGS_CAN.list_frame_pool);
}
} else {
restore_irq(was_irq_enabled);
}
}
@ -283,14 +307,22 @@ void SystemClock_Config(void)
void send_to_host(void)
{
struct gs_host_frame *frame = queue_pop_front(q_to_host);
struct gs_host_frame_object *frame_object;
if (!frame)
bool was_irq_enabled = disable_irq();
frame_object = list_first_entry_or_null(&hGS_CAN.list_to_host,
struct gs_host_frame_object,
list);
if (!frame_object) {
restore_irq(was_irq_enabled);
return;
}
list_del(&frame_object->list);
restore_irq(was_irq_enabled);
if (USBD_GS_CAN_SendFrame(&hUSB, frame) == USBD_OK) {
queue_push_back(hGS_CAN.q_frame_pool, frame);
if (USBD_GS_CAN_SendFrame(&hUSB, &frame_object->frame) == USBD_OK) {
list_add_tail_locked(&frame_object->list, &hGS_CAN.list_frame_pool);
} else {
queue_push_front(q_to_host, frame);
list_add_locked(&frame_object->list, &hGS_CAN.list_to_host);
}
}

View File

@ -33,7 +33,6 @@ THE SOFTWARE.
#include "gs_usb.h"
#include "hal_include.h"
#include "led.h"
#include "queue.h"
#include "timer.h"
#include "usbd_core.h"
#include "usbd_ctlreq.h"
@ -268,7 +267,9 @@ static const struct gs_device_bt_const USBD_GS_CAN_btconst = {
static inline uint8_t USBD_GS_CAN_PrepareReceive(USBD_HandleTypeDef *pdev)
{
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData;
return USBD_LL_PrepareReceive(pdev, GSUSB_ENDPOINT_OUT, (uint8_t*)hcan->from_host_buf, sizeof(*hcan->from_host_buf));
struct gs_host_frame *frame = &hcan->from_host_buf->frame;
return USBD_LL_PrepareReceive(pdev, GSUSB_ENDPOINT_OUT, (uint8_t *)frame, sizeof(*frame));
}
/* It's unclear from the documentation, but it appears that the USB library is
@ -280,13 +281,10 @@ static inline uint8_t USBD_GS_CAN_PrepareReceive(USBD_HandleTypeDef *pdev)
* within other calls, which means the USB interrupt is already disabled and we
* don't have any other interrupts to worry about. */
uint8_t USBD_GS_CAN_Init(USBD_GS_CAN_HandleTypeDef *hcan, USBD_HandleTypeDef *pdev, queue_t *q_frame_pool, queue_t *q_from_host, led_data_t *leds)
uint8_t USBD_GS_CAN_Init(USBD_GS_CAN_HandleTypeDef *hcan, USBD_HandleTypeDef *pdev, led_data_t *leds)
{
hcan->q_frame_pool = q_frame_pool;
hcan->q_from_host = q_from_host;
hcan->leds = leds;
pdev->pClassData = hcan;
hcan->from_host_buf = NULL;
return USBD_OK;
}
@ -600,14 +598,23 @@ static uint8_t USBD_GS_CAN_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum) {
return USBD_OK;
}
bool was_irq_enabled = disable_irq();
// Enqueue the frame we just received.
queue_push_back(hcan->q_from_host, hcan->from_host_buf);
list_add_tail(&hcan->from_host_buf->list, &hcan->list_from_host);
// Grab a buffer for the next frame from the pool.
hcan->from_host_buf = queue_pop_front(hcan->q_frame_pool);
hcan->from_host_buf = list_first_entry_or_null(&hcan->list_frame_pool,
struct gs_host_frame_object,
list);
if (hcan->from_host_buf) {
list_del(&hcan->from_host_buf->list);
restore_irq(was_irq_enabled);
// We got a buffer! Get ready to receive from the USB host into it.
USBD_GS_CAN_PrepareReceive(pdev);
} else {
restore_irq(was_irq_enabled);
// gs_can has no way to drop packets. If we just drop this one, gs_can
// will fill up its queue of packets awaiting ACKs and then hang. Instead,
// wait to call PrepareReceive until we have a frame to receive into.
@ -627,8 +634,11 @@ bool USBD_GS_CAN_TxReady(USBD_HandleTypeDef *pdev)
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData;
bool was_irq_enabled = disable_irq();
if (!hcan->from_host_buf) {
hcan->from_host_buf = queue_pop_front(hcan->q_frame_pool);
hcan->from_host_buf = list_first_entry_or_null(&hcan->list_frame_pool,
struct gs_host_frame_object,
list);
if (hcan->from_host_buf) {
list_del(&hcan->from_host_buf->list);
USBD_GS_CAN_PrepareReceive(pdev);
}
}