| /**************************************************************************** |
| * drivers/note/noterpmsg_driver.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you 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. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/note/note_driver.h> |
| #include <nuttx/rpmsg/rpmsg.h> |
| #include <nuttx/sched_note.h> |
| #include <nuttx/wqueue.h> |
| #include <nuttx/spinlock.h> |
| |
| #include "noterpmsg.h" |
| |
| /**************************************************************************** |
| * Pre-processor definitions |
| ****************************************************************************/ |
| |
| #define NOTE_RPMSG_WORK_DELAY MSEC2TICK(CONFIG_DRIVERS_NOTERPMSG_WORK_DELAY) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct noterpmsg_driver_s |
| { |
| struct note_driver_s driver; |
| volatile size_t head; |
| volatile size_t tail; |
| uint8_t buffer[CONFIG_DRIVERS_NOTERPMSG_BUFSIZE]; |
| struct work_s work; |
| struct rpmsg_endpoint ept; |
| spinlock_t lock; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static void noterpmsg_add(FAR struct note_driver_s *driver, |
| FAR const void *note, size_t notelen); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static const struct note_driver_ops_s g_noterpmsg_ops = |
| { |
| noterpmsg_add |
| }; |
| |
| struct noterpmsg_driver_s g_noterpmsg_driver = |
| { |
| { |
| #ifdef CONFIG_SCHED_INSTRUMENTATION_FILTER |
| "rpmsg", |
| { |
| { |
| CONFIG_SCHED_INSTRUMENTATION_FILTER_DEFAULT_MODE, |
| # ifdef CONFIG_SMP |
| CONFIG_SCHED_INSTRUMENTATION_CPUSET |
| # endif |
| }, |
| }, |
| #endif |
| &g_noterpmsg_ops |
| }, |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| static inline size_t noterpmsg_next(FAR struct noterpmsg_driver_s *drv, |
| size_t pos, size_t offset) |
| { |
| pos += offset; |
| if (pos >= CONFIG_DRIVERS_NOTERPMSG_BUFSIZE) |
| { |
| pos -= CONFIG_DRIVERS_NOTERPMSG_BUFSIZE; |
| } |
| |
| return pos; |
| } |
| |
| static inline size_t noterpmsg_length(FAR struct noterpmsg_driver_s *drv) |
| { |
| size_t head = drv->head; |
| size_t tail = drv->tail; |
| |
| if (tail > head) |
| { |
| head += CONFIG_DRIVERS_NOTERPMSG_BUFSIZE; |
| } |
| |
| return head - tail; |
| } |
| |
| static inline void noterpmsg_remove(FAR struct noterpmsg_driver_s *drv) |
| { |
| size_t tail = drv->tail; |
| uint8_t notelen = drv->buffer[tail]; |
| |
| DEBUGASSERT(notelen <= noterpmsg_length(drv)); |
| drv->tail = noterpmsg_next(drv, tail, notelen); |
| } |
| |
| static bool noterpmsg_transfer(FAR struct noterpmsg_driver_s *drv, |
| bool wait) |
| { |
| for (; ; ) |
| { |
| FAR uint8_t *buffer; |
| uint32_t space; |
| size_t len; |
| |
| len = noterpmsg_length(drv); |
| if (len == 0) |
| { |
| return true; |
| } |
| |
| buffer = rpmsg_get_tx_payload_buffer(&drv->ept, &space, wait); |
| if (buffer == NULL) |
| { |
| return false; |
| } |
| |
| if (space < len) |
| { |
| /* Find the len of large entire note data */ |
| |
| size_t pos = drv->tail; |
| uint8_t notelen = drv->buffer[pos]; |
| |
| len = 0; |
| while (len + notelen <= space) |
| { |
| pos = noterpmsg_next(drv, pos, notelen); |
| len += notelen; |
| notelen = drv->buffer[pos]; |
| } |
| } |
| |
| space = CONFIG_DRIVERS_NOTERPMSG_BUFSIZE - drv->tail; |
| space = space < len ? space : len; |
| |
| memcpy(buffer, drv->buffer + drv->tail, space); |
| memcpy(buffer + space, drv->buffer, len - space); |
| |
| if (rpmsg_send_nocopy(&drv->ept, buffer, len) < 0) |
| { |
| rpmsg_release_tx_buffer(&drv->ept, buffer); |
| } |
| |
| drv->tail = noterpmsg_next(drv, drv->tail, len); |
| } |
| } |
| |
| static void noterpmsg_work(FAR void *priv) |
| { |
| FAR struct noterpmsg_driver_s *drv = priv; |
| irqstate_t flags = spin_lock_irqsave_notrace(&drv->lock); |
| |
| if (!noterpmsg_transfer(drv, false)) |
| { |
| work_queue(HPWORK, &drv->work, noterpmsg_work, drv, |
| NOTE_RPMSG_WORK_DELAY); |
| } |
| |
| spin_unlock_irqrestore_notrace(&drv->lock, flags); |
| } |
| |
| static void noterpmsg_add(FAR struct note_driver_s *driver, |
| FAR const void *note, size_t notelen) |
| { |
| FAR struct noterpmsg_driver_s *drv = |
| (FAR struct noterpmsg_driver_s *)driver; |
| irqstate_t flags; |
| size_t space; |
| |
| flags = spin_lock_irqsave_notrace(&drv->lock); |
| |
| space = CONFIG_DRIVERS_NOTERPMSG_BUFSIZE - noterpmsg_length(drv); |
| if (space < notelen) |
| { |
| if (!up_interrupt_context() && !sched_idletask()) |
| { |
| noterpmsg_transfer(drv, true); |
| } |
| else |
| { |
| /* Overwrite */ |
| |
| do |
| { |
| noterpmsg_remove(drv); |
| space = CONFIG_DRIVERS_NOTERPMSG_BUFSIZE - |
| noterpmsg_length(drv); |
| } |
| while (space < notelen); |
| } |
| } |
| |
| space = CONFIG_DRIVERS_NOTERPMSG_BUFSIZE - drv->head; |
| space = space < notelen ? space : notelen; |
| |
| memcpy(drv->buffer + drv->head, note, space); |
| memcpy(drv->buffer, note + space, notelen - space); |
| |
| drv->head = noterpmsg_next(drv, drv->head, notelen); |
| |
| if (work_available(&drv->work)) |
| { |
| work_queue(HPWORK, &drv->work, noterpmsg_work, drv, |
| NOTE_RPMSG_WORK_DELAY); |
| } |
| |
| spin_unlock_irqrestore_notrace(&drv->lock, flags); |
| } |
| |
| static int noterpmsg_ept_cb(FAR struct rpmsg_endpoint *ept, |
| FAR void *data, size_t len, uint32_t src, |
| FAR void *priv) |
| { |
| return 0; |
| } |
| |
| static void noterpmsg_device_created(FAR struct rpmsg_device *rdev, |
| FAR void *priv) |
| { |
| FAR struct noterpmsg_driver_s *drv = priv; |
| int ret; |
| |
| if (strcmp(CONFIG_DRIVERS_NOTERPMSG_SERVER_NAME, |
| rpmsg_get_cpuname(rdev)) == 0) |
| { |
| drv->ept.priv = drv; |
| spin_lock_init(&drv->lock); |
| |
| ret = rpmsg_create_ept(&drv->ept, rdev, NOTERPMSG_EPT_NAME, |
| RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, |
| noterpmsg_ept_cb, NULL); |
| if (ret >= 0) |
| { |
| work_queue(HPWORK, &drv->work, noterpmsg_work, drv, 0); |
| } |
| } |
| } |
| |
| static void noterpmsg_device_destroy(FAR struct rpmsg_device *rdev, |
| FAR void *priv) |
| { |
| FAR struct noterpmsg_driver_s *drv = priv; |
| |
| if (strcmp(CONFIG_DRIVERS_NOTERPMSG_SERVER_NAME, |
| rpmsg_get_cpuname(rdev)) == 0) |
| { |
| rpmsg_destroy_ept(&drv->ept); |
| } |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: noterpmsg_init |
| * |
| * Description: |
| * Register a rmpsg channel to note. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * Zero on success. A negated errno value is returned on a failure. |
| * |
| ****************************************************************************/ |
| |
| int noterpmsg_init(void) |
| { |
| return rpmsg_register_callback(&g_noterpmsg_driver, |
| noterpmsg_device_created, |
| noterpmsg_device_destroy, |
| NULL, |
| NULL); |
| } |