Files
pebble/src/fw/services/normal/app_inbox_service.h
T

115 lines
5.1 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "applib/app_inbox.h"
#include "util/attributes.h"
// Design goals of this module:
//
// - Provide a generic mechanism to pass variable-length data from a kernel service to app.
// - Have the data be written directly into an app-provided buffer (in app space).
// - Data is chunked up in "messages".
// - Data must be contiguously stored for easy parsing (no circular buffer wrap-arounds).
// - Support writing a message, while having pending, unconsumed message(s) in the buffer.
// - Support starting to write a partial message, write some more and finally decide to cancel it.
// The partial message should not get delivered.
// - No race conditions can exist that could cause reading of an incomplete message.
// - Support for notifying the app when data has been dropped (not enough buffer space) and
// report the number of dropped messages.
//
// Non-goals:
// - Sharing the same buffer between multiple kernel services (1:1 service to buffer relation is OK)
// - Concurrently writing to the inbox from multiple tasks (failing the write up front when another
// task is currently in the process of writing a message is OK)
// - Preserve the ordering of when the dropped messages happened vs the received messages (it's OK
// to only report the number of dropped messages)
typedef enum {
AppInboxServiceTagInvalid = -1,
AppInboxServiceTagAppMessageReceiver,
#ifdef UNITTEST
AppInboxServiceTagUnitTest,
AppInboxServiceTagUnitTestAlt,
#endif
NumAppInboxServiceTag,
} AppInboxServiceTag;
typedef struct PACKED {
// Length of `data` payload (excluding the size of this header)
size_t length;
//! To give us some room for future changes. This structure ends up in a buffer that is sized by
//! the app, so we can't easily increase the size of this once shipped.
uint8_t padding[4];
uint8_t data[];
} AppInboxMessageHeader;
#ifndef UNITTEST
_Static_assert(sizeof(AppInboxMessageHeader) == 8,
"The size of AppInboxMessageHeader cannot grow beyond 8 bytes!");
#endif
//! To be called once at boot.
void app_inbox_service_init(void);
////////////////////////////////////////////////////////////////////////////////////////////////////
// Owner / Receiver (App) API
//! @param storage_size The size of the buffer (in app space). Note that a header will be appended
//! to the data of sizeof(AppInboxMessageHeader) bytes.
//! @note The event handler will be executed on the task that called this function.
//! @see app_inbox_create_and_register() for the applib invocation.
bool app_inbox_service_register(uint8_t *storage, size_t storage_size,
AppInboxMessageHandler message_handler,
AppInboxDroppedHandler dropped_handler, AppInboxServiceTag tag);
//! @return The number of messages that were dropped, plus the ones that were still waiting
//! to be consumed.
//! @see app_inbox_destroy_and_deregister() for the applib invocation.
uint32_t app_inbox_service_unregister_by_storage(uint8_t *storage);
void app_inbox_service_unregister_all(void);
////////////////////////////////////////////////////////////////////////////////////////////////////
// Sender (Kernel) API
//! @param required_free_length The length in bytes of the data that needs to be written. Note that
//! this should not include the size of the AppInboxMessageHeader. However, there must be at least
//! (required_free_length + sizeof(AppInboxMessageHeader)) bytes free in the buffer in order to
//! be able to write the message.
//! @param writer Reference to the writer, just for debugging.
//! @return True if the buffer is claimed successfully, false if not. If this function returns
//! true, you MUST call app_inbox_service_end() at some point. Inversely, if this functions returns
//! false, you MUST NOT call app_inbox_service_write() nor app_inbox_service_end() nor
//! app_inbox_service_cancel().
bool app_inbox_service_begin(AppInboxServiceTag tag, size_t required_free_length, void *writer);
//! @return True if the write was successful, false if not. If one write failed, successive writes
//! will also fail and `app_inbox_service_end` will not actually dispatch the (broken) message,
//! but instead just dispatch an event that data got dropped.
bool app_inbox_service_write(AppInboxServiceTag tag, const uint8_t *data, size_t length);
//! @return True is the entire message was written successfully, false if not. If a partial write
//! failed, the "dropped handler" will be invoked.
bool app_inbox_service_end(AppInboxServiceTag tag);
void app_inbox_service_cancel(AppInboxServiceTag tag);