diff --git a/core/embed/io/display/fb_queue/fb_queue.c b/core/embed/io/display/fb_queue/fb_queue.c index c4c7d32756..70bbaad4c5 100644 --- a/core/embed/io/display/fb_queue/fb_queue.c +++ b/core/embed/io/display/fb_queue/fb_queue.c @@ -1,165 +1,94 @@ #ifdef KERNEL_MODE +#include + #include #include "fb_queue.h" -int16_t fb_queue_get_for_copy(frame_buffer_queue_t *queue) { - irq_key_t key = irq_lock(); - int16_t wix = queue->wix; - if (queue->entry[wix] != FB_STATE_PREPARING) { - // No refresh needed as the frame buffer is not in - // the state to be copied to the display - irq_unlock(key); - return -1; - } - - irq_unlock(key); - return wix; -} - -int16_t fb_queue_get_for_write(frame_buffer_queue_t *queue) { - frame_buffer_state_t state; - - // We have to wait if the buffer was passed for copying - // to the interrupt handler - do { - irq_key_t key = irq_lock(); - state = queue->entry[queue->wix]; - irq_unlock(key); - - } while (state == FB_STATE_READY || state == FB_STATE_COPYING); - - irq_key_t key = irq_lock(); - queue->entry[queue->wix] = FB_STATE_PREPARING; - irq_unlock(key); - - return queue->wix; -} - -int16_t fb_queue_get_for_transfer(frame_buffer_queue_t *queue) { - irq_key_t key = irq_lock(); - - if (queue->rix >= FRAME_BUFFER_COUNT) { - // This is an invalid state, and we should never get here - irq_unlock(key); - return -1; - } - - switch (queue->entry[queue->rix]) { - case FB_STATE_EMPTY: - case FB_STATE_PREPARING: - // No new frame queued - - case FB_STATE_COPYING: - // Currently we are copying a data to the display. - - irq_unlock(key); - return -1; - break; - - case FB_STATE_READY: - // Now it's proper time to copy the data to the display - queue->entry[queue->rix] = FB_STATE_COPYING; - irq_unlock(key); - return queue->rix; - - // NOTE: when copying is done, this queue slot is marked empty - break; - - default: - // This is an invalid state, and we should never get here - irq_unlock(key); - return -1; - break; - } -} - -bool fb_queue_set_done(frame_buffer_queue_t *queue) { - irq_key_t key = irq_lock(); - if (queue->rix >= FRAME_BUFFER_COUNT) { - // This is an invalid state, and we should never get here - irq_unlock(key); - return false; - } - - if (queue->entry[queue->rix] == FB_STATE_COPYING) { - queue->entry[queue->rix] = FB_STATE_EMPTY; - queue->rix = (queue->rix + 1) % FRAME_BUFFER_COUNT; - irq_unlock(key); - return true; - } - - irq_unlock(key); - return false; -} - -bool fb_queue_set_switched(frame_buffer_queue_t *queue) { - irq_key_t key = irq_lock(); - if (queue->rix >= FRAME_BUFFER_COUNT) { - // This is an invalid state, and we should never get here - irq_unlock(key); - return false; - } - - if (queue->entry[queue->rix] == FB_STATE_COPYING) { - if (queue->aix >= 0) { - queue->entry[queue->aix] = FB_STATE_EMPTY; - } - queue->aix = queue->rix; - queue->rix = (queue->rix + 1) % FRAME_BUFFER_COUNT; - irq_unlock(key); - return true; - } - - irq_unlock(key); - return false; -} - -bool fb_queue_set_ready_for_transfer(frame_buffer_queue_t *queue) { - irq_key_t key = irq_lock(); - if (queue->wix >= FRAME_BUFFER_COUNT) { - // This is an invalid state, and we should never get here - irq_unlock(key); - return false; - } - - if (queue->entry[queue->rix] == FB_STATE_PREPARING) { - queue->entry[queue->rix] = FB_STATE_READY; - queue->wix = (queue->wix + 1) % FRAME_BUFFER_COUNT; - irq_unlock(key); - return true; - } - - irq_unlock(key); - return false; -} - -void fb_queue_reset(frame_buffer_queue_t *queue) { - irq_key_t key = irq_lock(); - // Reset the buffer queue so we can eventually continue - // safely in thread mode - queue->wix = 0; - queue->rix = 0; +// Initializes the queue and make it empty +// Clear peeked flag +void fb_queue_reset(fb_queue_t* queue) { + memset(queue, 0, sizeof(fb_queue_t)); for (int i = 0; i < FRAME_BUFFER_COUNT; i++) { - queue->entry[i] = FB_STATE_EMPTY; + queue->entries[i].index = -1; } - irq_unlock(key); } -bool fb_queue_is_processed(frame_buffer_queue_t *queue) { - irq_key_t key = irq_lock(); - for (int i = 0; i < FRAME_BUFFER_COUNT; i++) { - frame_buffer_state_t state = queue->entry[i]; - if (state == FB_STATE_READY || - (state == FB_STATE_COPYING && i != queue->aix)) { - irq_unlock(key); - return false; - } +// Inserts a new element to the tail of the queue +bool fb_queue_put(fb_queue_t* queue, int16_t index) { + irq_key_t irq_key = irq_lock(); + + // check if the queue is full + if (queue->entries[queue->wix].index != -1) { + irq_unlock(irq_key); + return false; } - irq_unlock(key); + queue->entries[queue->wix].index = index; + queue->wix = (queue->wix + 1) % FRAME_BUFFER_COUNT; + + irq_unlock(irq_key); + return true; } +// Removes an element from the queue head, returns -1 if the queue is empty +// Clear peeked flag +int16_t fb_queue_take(fb_queue_t* queue) { + irq_key_t irq_key = irq_lock(); + + if (queue->entries[queue->rix].index == -1) { + irq_unlock(irq_key); + return -1; + } + + queue->peaked = false; + int16_t index = queue->entries[queue->rix].index; + queue->entries[queue->rix].index = -1; + queue->rix = (queue->rix + 1) % FRAME_BUFFER_COUNT; + + irq_unlock(irq_key); + return index; +} +// Returns true if the queue is empty +bool fb_queue_empty(fb_queue_t* queue) { + irq_key_t irq_key = irq_lock(); + + if (queue->entries[queue->rix].index == -1) { + irq_unlock(irq_key); + return true; + } + + irq_unlock(irq_key); + return false; +} + +// Waits until the queue is not empty +void fb_queue_wait(fb_queue_t* queue) { + while (fb_queue_empty(queue)) + ; +} + +// Returns the head of the queue (or -1 if the queue is empty) +// Set peeked flag if the queue is not empty +int16_t fb_queue_peek(fb_queue_t* queue) { + irq_key_t irq_key = irq_lock(); + + if (queue->entries[queue->rix].index == -1) { + irq_unlock(irq_key); + return -1; + } + + int16_t index = queue->entries[queue->rix].index; + queue->peaked = true; + + irq_unlock(irq_key); + + return index; +} + +// Return if the head was already peeked +bool fb_queue_peeked(fb_queue_t* queue) { return queue->peaked; } + #endif diff --git a/core/embed/io/display/fb_queue/fb_queue.h b/core/embed/io/display/fb_queue/fb_queue.h index f189d940c8..5558eeb846 100644 --- a/core/embed/io/display/fb_queue/fb_queue.h +++ b/core/embed/io/display/fb_queue/fb_queue.h @@ -27,26 +27,14 @@ #define FRAME_BUFFER_COUNT 2 // Each frame buffer can be in one of the following states: -typedef enum { - // The frame buffer is empty and can be written to - FB_STATE_EMPTY = 0, - // The frame buffer pass passed to application - FB_STATE_PREPARING = 1, - // The frame buffer was written to and is ready - // to be copied to the display - FB_STATE_READY = 2, - // The frame buffer is currently being copied to - // the display - FB_STATE_COPYING = 3, - -} frame_buffer_state_t; +typedef struct { + int16_t index; +} fb_queue_entry; typedef struct { // Queue entries - frame_buffer_state_t entry[FRAME_BUFFER_COUNT]; - // Active index - // (accessed & updated in the context of the interrupt handlers - int16_t aix; + fb_queue_entry entries[FRAME_BUFFER_COUNT]; + // Read index // (accessed & updated in the context of the interrupt handlers uint8_t rix; @@ -54,31 +42,30 @@ typedef struct { // (accessed & updated in context of the main thread) uint8_t wix; -} frame_buffer_queue_t; + // Flag indicating that the head of the queue has been peaked + bool peaked; -// Get the frame buffer index for copying to display -// Call from main thread only -int16_t fb_queue_get_for_copy(frame_buffer_queue_t *queue); +} fb_queue_t; -// Get the frame buffer index for writing -// Call from main thread only -int16_t fb_queue_get_for_write(frame_buffer_queue_t *queue); +// Initializes the queue and make it empty +// Clear peeked flag +void fb_queue_reset(fb_queue_t* queue); -// Get the frame buffer index for transfer -int16_t fb_queue_get_for_transfer(frame_buffer_queue_t *queue); +// Inserts a new element to the tail of the queue +bool fb_queue_put(fb_queue_t* queue, int16_t index); -// Mark the frame buffer as done, thus no longer used -bool fb_queue_set_done(frame_buffer_queue_t *queue); +// Removes an element from the queue head, returns -1 if the queue is empty +// Clear peeked flag +int16_t fb_queue_take(fb_queue_t* queue); +// Returns true if the queue is empty +bool fb_queue_empty(fb_queue_t* queue); -// Mark the frame buffer as switched, thus actively used by display -bool fb_queue_set_switched(frame_buffer_queue_t *queue); +// Waits until the queue is not empty +void fb_queue_wait(fb_queue_t* queue); -// Mark the frame buffer as ready to be copied to the display -// Call from main thread only -bool fb_queue_set_ready_for_transfer(frame_buffer_queue_t *queue); +// Returns the head of the queue (or -1 if the queue is empty) +// Set peeked flag if the queue is not empty +int16_t fb_queue_peek(fb_queue_t* queue); -// Reset the queue state -void fb_queue_reset(frame_buffer_queue_t *queue); - -// Check if all frame buffers are processed -bool fb_queue_is_processed(frame_buffer_queue_t *queue); +// Return if the head was already peeked +bool fb_queue_peeked(fb_queue_t* queue);