Files
nuttx/drivers/timers/rpmsg_rtc.c
T
dongjiuzhu1 3c486b98dd fs/timerfd: implement TFD_TIMER_CANCEL_ON_SET to detect clock changes
Implement Linux-compatible TFD_TIMER_CANCEL_ON_SET flag for timerfd to
allow applications to detect discontinuous changes to CLOCK_REALTIME.

Background:
According to Linux timerfd_create(2) man page, when a timerfd is created
with CLOCK_REALTIME and TFD_TIMER_CANCEL_ON_SET flag is specified, the
read() operation should fail with ECANCELED if the real-time clock
undergoes a discontinuous change. This allows applications to detect
and respond to system time changes.

Implementation:
1. Add clock notifier infrastructure (clock_notifier.h/c) to notify
   interested parties when system time changes
2. Implement TFD_TIMER_CANCEL_ON_SET flag support in timerfd
3. Register timerfd with clock notifier when flag is set
4. Cancel timer and return ECANCELED when clock change is detected
5. Notify clock changes in:
   - clock_settime()
   - clock_initialize()
   - clock_timekeeping_set_wall_time()
   - rpmsg_rtc time synchronization

Changes include:
- New files: include/nuttx/clock_notifier.h, sched/clock/clock_notifier.c
- Modified: fs/vfs/fs_timerfd.c to handle TFD_TIMER_CANCEL_ON_SET
- Modified: clock subsystem to call notifier chain on time changes
- Modified: rpmsg_rtc to notify time changes during sync

Use case example:
Applications using timerfd with absolute time can now detect when
system time is adjusted (e.g., NTP sync, manual time change) and
take appropriate action such as recalculating timeouts or updating
scheduled events.

Reference: https://man7.org/linux/man-pages/man2/timerfd_create.2.html

Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
2026-01-30 17:20:24 +08:00

869 lines
25 KiB
C

/****************************************************************************
* drivers/timers/rpmsg_rtc.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/config.h>
#include <nuttx/nuttx.h>
#include <nuttx/list.h>
#include <nuttx/clock.h>
#include <nuttx/clock_notifier.h>
#include <nuttx/kmalloc.h>
#include <nuttx/rpmsg/rpmsg.h>
#include <nuttx/mutex.h>
#include <nuttx/spinlock.h>
#include <nuttx/timers/rpmsg_rtc.h>
#include <nuttx/timers/arch_rtc.h>
#include <errno.h>
#include <string.h>
#include "clock/clock.h"
/****************************************************************************
* Pre-processor definitions
****************************************************************************/
#define RPMSG_RTC_EPT_NAME "rpmsg-rtc"
#define RPMSG_RTC_SET 0
#define RPMSG_RTC_GET 1
#define RPMSG_RTC_ALARM_SET 2
#define RPMSG_RTC_ALARM_CANCEL 3
#define RPMSG_RTC_ALARM_FIRE 4
#define RPMSG_RTC_SYNC 5
/****************************************************************************
* Private Types
****************************************************************************/
begin_packed_struct struct rpmsg_rtc_header_s
{
uint32_t command;
int32_t result;
uint64_t cookie;
} end_packed_struct;
begin_packed_struct struct rpmsg_rtc_set_s
{
struct rpmsg_rtc_header_s header;
int64_t base_sec;
int64_t sec;
int32_t nsec;
int32_t base_nsec;
} end_packed_struct;
#define rpmsg_rtc_get_s rpmsg_rtc_set_s
begin_packed_struct struct rpmsg_rtc_alarm_set_s
{
struct rpmsg_rtc_header_s header;
int64_t sec;
int32_t nsec;
int32_t id;
} end_packed_struct;
begin_packed_struct struct rpmsg_rtc_alarm_cancel_s
{
struct rpmsg_rtc_header_s header;
int32_t id;
} end_packed_struct;
#define rpmsg_rtc_alarm_fire_s rpmsg_rtc_alarm_cancel_s
#ifndef CONFIG_RTC_RPMSG_SERVER
struct rpmsg_rtc_cookie_s
{
FAR struct rpmsg_rtc_header_s *msg;
sem_t sem;
};
/* This is the private type for the RTC state. It must be cast compatible
* with struct rtc_lowerhalf_s.
*/
struct rpmsg_rtc_lowerhalf_s
{
/* This is the contained reference to the read-only, lower-half
* operations vtable (which may lie in FLASH or ROM)
*/
FAR const struct rtc_ops_s *ops;
/* Data following is private to this driver and not visible outside of
* this file.
*/
struct rpmsg_endpoint ept;
#ifdef CONFIG_RTC_ALARM
struct lower_setalarm_s alarminfo[CONFIG_RTC_NALARMS];
#endif
};
#else
struct rpmsg_rtc_server_s
{
FAR struct rtc_ops_s *ops;
FAR struct rtc_lowerhalf_s *lower;
struct list_node list;
mutex_t lock;
};
struct rpmsg_rtc_client_s
{
struct list_node node;
struct rpmsg_endpoint ept;
};
#endif
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
#ifndef CONFIG_RTC_RPMSG_SERVER
static bool rpmsg_rtc_device_ns_match(FAR struct rpmsg_device *rdev,
FAR void *priv, FAR const char *name,
uint32_t dest);
static void rpmsg_rtc_device_ns_bind(FAR struct rpmsg_device *rdev,
FAR void *priv, FAR const char *name,
uint32_t dest);
static void rpmsg_rtc_alarm_fire_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len, uint32_t src, FAR void *priv);
static int rpmsg_rtc_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data,
size_t len, uint32_t src, FAR void *priv);
static int rpmsg_rtc_send_recv(FAR struct rpmsg_rtc_lowerhalf_s *lower,
uint32_t command, FAR struct rpmsg_rtc_header_s *msg, int len);
static int rpmsg_rtc_rdtime(FAR struct rtc_lowerhalf_s *lower,
FAR struct rtc_time *rtctime);
static int rpmsg_rtc_settime(FAR struct rtc_lowerhalf_s *lower,
FAR const struct rtc_time *rtctime);
static bool rpmsg_rtc_havesettime(FAR struct rtc_lowerhalf_s *lower);
#ifdef CONFIG_RTC_ALARM
static int rpmsg_rtc_setalarm(FAR struct rtc_lowerhalf_s *lower_,
FAR const struct lower_setalarm_s *alarminfo);
static int rpmsg_rtc_setrelative(FAR struct rtc_lowerhalf_s *lower,
FAR const struct lower_setrelative_s *relinfo);
static int rpmsg_rtc_cancelalarm(FAR struct rtc_lowerhalf_s *lower,
int alarmid);
static int rpmsg_rtc_rdalarm(FAR struct rtc_lowerhalf_s *lower_,
FAR struct lower_rdalarm_s *alarminfo);
#endif
#else
static int rpmsg_rtc_server_rdtime(FAR struct rtc_lowerhalf_s *lower,
FAR struct rtc_time *rtctime);
static int rpmsg_rtc_server_settime(FAR struct rtc_lowerhalf_s *lower,
FAR const struct rtc_time *rtctime);
static bool rpmsg_rtc_server_havesettime(FAR struct rtc_lowerhalf_s *lower);
#ifdef CONFIG_RTC_ALARM
static int rpmsg_rtc_server_setalarm(FAR struct rtc_lowerhalf_s *lower,
FAR const struct lower_setalarm_s *alarminfo);
static int rpmsg_rtc_server_setrelative(FAR struct rtc_lowerhalf_s *lower,
FAR const struct lower_setrelative_s *relinfo);
static int rpmsg_rtc_server_cancelalarm(FAR struct rtc_lowerhalf_s *lower,
int alarmid);
static int rpmsg_rtc_server_rdalarm(FAR struct rtc_lowerhalf_s *lower,
FAR struct lower_rdalarm_s *alarminfo);
#endif
#ifdef CONFIG_RTC_PERIODIC
static int rpmsg_rtc_server_setperiodic(FAR struct rtc_lowerhalf_s *lower,
FAR const struct lower_setperiodic_s *alarminfo);
static int rpmsg_rtc_server_cancelperiodic(
FAR struct rtc_lowerhalf_s *lower, int alarmid);
#endif
#ifdef CONFIG_RTC_IOCTL
static int rpmsg_rtc_server_ioctl(FAR struct rtc_lowerhalf_s *lower,
int cmd, unsigned long arg);
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
static int rpmsg_rtc_server_destroy(FAR struct rtc_lowerhalf_s *lower);
#endif
#endif
/****************************************************************************
* Private Data
****************************************************************************/
#ifndef CONFIG_RTC_RPMSG_SERVER
static const struct rtc_ops_s g_rpmsg_rtc_ops =
{
rpmsg_rtc_rdtime,
rpmsg_rtc_settime,
rpmsg_rtc_havesettime,
#ifdef CONFIG_RTC_ALARM
rpmsg_rtc_setalarm,
rpmsg_rtc_setrelative,
rpmsg_rtc_cancelalarm,
rpmsg_rtc_rdalarm,
#endif
};
#else
static struct rtc_ops_s g_rpmsg_rtc_server_ops =
{
rpmsg_rtc_server_rdtime,
rpmsg_rtc_server_settime,
rpmsg_rtc_server_havesettime,
#ifdef CONFIG_RTC_ALARM
rpmsg_rtc_server_setalarm,
rpmsg_rtc_server_setrelative,
rpmsg_rtc_server_cancelalarm,
rpmsg_rtc_server_rdalarm,
#endif
#ifdef CONFIG_RTC_PERIODIC
rpmsg_rtc_server_setperiodic,
rpmsg_rtc_server_cancelperiodic,
#endif
#ifdef CONFIG_RTC_IOCTL
rpmsg_rtc_server_ioctl,
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
rpmsg_rtc_server_destroy,
#endif
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
#ifndef CONFIG_RTC_RPMSG_SERVER
static bool rpmsg_rtc_device_ns_match(FAR struct rpmsg_device *rdev,
FAR void *priv, FAR const char *name,
uint32_t dest)
{
return !strcmp(name, RPMSG_RTC_EPT_NAME);
}
static void rpmsg_rtc_device_ns_bind(FAR struct rpmsg_device *rdev,
FAR void *priv, FAR const char *name,
uint32_t dest)
{
FAR struct rpmsg_rtc_lowerhalf_s *lower = priv;
lower->ept.priv = lower;
rpmsg_create_ept(&lower->ept, rdev, RPMSG_RTC_EPT_NAME,
RPMSG_ADDR_ANY, dest,
rpmsg_rtc_ept_cb, rpmsg_destroy_ept);
}
static void rpmsg_rtc_alarm_fire_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
#ifdef CONFIG_RTC_ALARM
FAR struct rpmsg_rtc_lowerhalf_s *lower = priv;
FAR struct rpmsg_rtc_alarm_fire_s *msg = data;
FAR struct lower_setalarm_s *alarminfo = &lower->alarminfo[msg->id];
alarminfo->cb(alarminfo->priv, alarminfo->id);
#endif
}
static int rpmsg_rtc_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data,
size_t len, uint32_t src, FAR void *priv)
{
FAR struct rpmsg_rtc_header_s *header = data;
FAR struct rpmsg_rtc_cookie_s *cookie =
(FAR struct rpmsg_rtc_cookie_s *)(uintptr_t)header->cookie;
switch (header->command)
{
case RPMSG_RTC_ALARM_FIRE:
rpmsg_rtc_alarm_fire_handler(ept, data, len, src, priv);
break;
case RPMSG_RTC_SYNC:
{
struct rpmsg_rtc_set_s *msg = data;
#ifdef CONFIG_RTC_RPMSG_SYNC_BASETIME
struct timespec ts;
irqstate_t flags;
flags = spin_lock_irqsave(&g_basetime_lock);
g_basetime.tv_sec = msg->base_sec;
g_basetime.tv_nsec = msg->base_nsec;
spin_unlock_irqrestore(&g_basetime_lock, flags);
clock_gettime(CLOCK_REALTIME, &ts);
clock_notifier_call_chain(CLOCK_REALTIME, &ts);
#else
struct timespec tp;
tp.tv_sec = msg->sec;
tp.tv_nsec = msg->nsec;
clock_synchronize(&tp);
#endif
}
break;
default:
if (cookie)
{
memcpy(cookie->msg, data, len);
nxsem_post(&cookie->sem);
}
break;
}
return 0;
}
static int rpmsg_rtc_send_recv(FAR struct rpmsg_rtc_lowerhalf_s *lower,
uint32_t command,
FAR struct rpmsg_rtc_header_s *msg, int len)
{
struct rpmsg_rtc_cookie_s cookie;
int ret;
nxsem_init(&cookie.sem, 0, 0);
cookie.msg = msg;
msg->command = command;
msg->result = -ENXIO;
msg->cookie = (uintptr_t)&cookie;
ret = rpmsg_send(&lower->ept, msg, len);
if (ret < 0)
{
goto fail;
}
ret = nxsem_wait_uninterruptible(&cookie.sem);
if (ret == 0)
{
ret = msg->result;
}
fail:
nxsem_destroy(&cookie.sem);
return ret;
}
static int rpmsg_rtc_rdtime(FAR struct rtc_lowerhalf_s *lower,
FAR struct rtc_time *rtctime)
{
struct rpmsg_rtc_get_s msg;
int ret;
ret = rpmsg_rtc_send_recv((FAR struct rpmsg_rtc_lowerhalf_s *)lower,
RPMSG_RTC_GET, (struct rpmsg_rtc_header_s *)&msg, sizeof(msg));
if (ret >= 0)
{
time_t time = msg.sec;
gmtime_r(&time, (FAR struct tm *)rtctime);
rtctime->tm_nsec = msg.nsec;
}
return ret;
}
static int rpmsg_rtc_settime(FAR struct rtc_lowerhalf_s *lower,
FAR const struct rtc_time *rtctime)
{
struct rpmsg_rtc_set_s msg;
msg.sec = timegm((FAR struct tm *)rtctime);
msg.nsec = rtctime->tm_nsec;
return rpmsg_rtc_send_recv((FAR struct rpmsg_rtc_lowerhalf_s *)lower,
RPMSG_RTC_SET, (struct rpmsg_rtc_header_s *)&msg, sizeof(msg));
}
static bool rpmsg_rtc_havesettime(FAR struct rtc_lowerhalf_s *lower)
{
return true;
}
#ifdef CONFIG_RTC_ALARM
static int rpmsg_rtc_setalarm(FAR struct rtc_lowerhalf_s *lower_,
FAR const struct lower_setalarm_s *alarminfo)
{
FAR struct rpmsg_rtc_lowerhalf_s *lower =
(FAR struct rpmsg_rtc_lowerhalf_s *)lower_;
struct rpmsg_rtc_alarm_set_s msg;
int ret;
msg.id = alarminfo->id;
msg.sec = timegm((FAR struct tm *)&alarminfo->time);
msg.nsec = alarminfo->time.tm_nsec;
ret = rpmsg_rtc_send_recv(lower, RPMSG_RTC_ALARM_SET,
(struct rpmsg_rtc_header_s *)&msg, sizeof(msg));
if (ret >= 0)
{
lower->alarminfo[alarminfo->id] = *alarminfo;
}
return ret;
}
static int
rpmsg_rtc_setrelative(FAR struct rtc_lowerhalf_s *lower,
FAR const struct lower_setrelative_s *relinfo)
{
struct lower_setalarm_s alarminfo;
time_t time;
alarminfo.id = relinfo->id;
alarminfo.cb = relinfo->cb;
alarminfo.priv = relinfo->priv;
rpmsg_rtc_rdtime(lower, &alarminfo.time);
time = timegm((FAR struct tm *)&alarminfo.time);
time = time + relinfo->reltime;
gmtime_r(&time, (FAR struct tm *)&alarminfo.time);
return rpmsg_rtc_setalarm(lower, &alarminfo);
}
static int rpmsg_rtc_cancelalarm(FAR struct rtc_lowerhalf_s *lower,
int alarmid)
{
struct rpmsg_rtc_alarm_cancel_s msg;
msg.id = alarmid;
return rpmsg_rtc_send_recv((FAR struct rpmsg_rtc_lowerhalf_s *)lower,
RPMSG_RTC_ALARM_CANCEL, (struct rpmsg_rtc_header_s *)&msg,
sizeof(msg));
}
static int rpmsg_rtc_rdalarm(FAR struct rtc_lowerhalf_s *lower_,
FAR struct lower_rdalarm_s *alarminfo)
{
FAR struct rpmsg_rtc_lowerhalf_s *lower =
(FAR struct rpmsg_rtc_lowerhalf_s *)lower_;
*alarminfo->time = lower->alarminfo[alarminfo->id].time;
return 0;
}
#endif
#else
static int rpmsg_rtc_server_rdtime(FAR struct rtc_lowerhalf_s *lower,
FAR struct rtc_time *rtctime)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
return server->lower->ops->rdtime(server->lower, rtctime);
}
static int rpmsg_rtc_server_settime(FAR struct rtc_lowerhalf_s *lower,
FAR const struct rtc_time *rtctime)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
FAR struct rpmsg_rtc_client_s *client;
FAR struct list_node *node;
struct rpmsg_rtc_set_s msg;
irqstate_t flags;
int ret;
ret = server->lower->ops->settime(server->lower, rtctime);
if (ret >= 0)
{
msg.sec = timegm((FAR struct tm *)rtctime);
msg.nsec = rtctime->tm_nsec;
msg.header.command = RPMSG_RTC_SYNC;
if (ret == 0)
{
struct timespec tp;
tp.tv_sec = msg.sec;
tp.tv_nsec = msg.nsec;
clock_synchronize(&tp);
ret = 1; /* Request the upper half skip clock synchronize */
}
flags = spin_lock_irqsave(&g_basetime_lock);
msg.base_sec = g_basetime.tv_sec;
msg.base_nsec = g_basetime.tv_nsec;
spin_unlock_irqrestore(&g_basetime_lock, flags);
nxmutex_lock(&server->lock);
list_for_every(&server->list, node)
{
client = (FAR struct rpmsg_rtc_client_s *)node;
rpmsg_send(&client->ept, &msg, sizeof(msg));
}
nxmutex_unlock(&server->lock);
}
return ret;
}
static bool rpmsg_rtc_server_havesettime(FAR struct rtc_lowerhalf_s *lower)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
return server->lower->ops->havesettime(server->lower);
}
#ifdef CONFIG_RTC_ALARM
static int rpmsg_rtc_server_setalarm(FAR struct rtc_lowerhalf_s *lower,
FAR const struct lower_setalarm_s *alarminfo)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
return server->lower->ops->setalarm(server->lower, alarminfo);
}
static int rpmsg_rtc_server_setrelative(FAR struct rtc_lowerhalf_s *lower,
FAR const struct lower_setrelative_s *relinfo)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
return server->lower->ops->setrelative(server->lower, relinfo);
}
static int rpmsg_rtc_server_cancelalarm(FAR struct rtc_lowerhalf_s *lower,
int alarmid)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
return server->lower->ops->cancelalarm(server->lower, alarmid);
}
static int rpmsg_rtc_server_rdalarm(FAR struct rtc_lowerhalf_s *lower,
FAR struct lower_rdalarm_s *alarminfo)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
return server->lower->ops->rdalarm(server->lower, alarminfo);
}
#endif
#ifdef CONFIG_RTC_PERIODIC
static int rpmsg_rtc_server_setperiodic(FAR struct rtc_lowerhalf_s *lower,
FAR const struct lower_setperiodic_s *alarminfo)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
return server->lower->ops->setperiodic(server->lower, alarminfo);
}
static int rpmsg_rtc_server_cancelperiodic(
FAR struct rtc_lowerhalf_s *lower, int alarmid)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
return server->lower->ops->cancelperiodic(server->lower, alarmid);
}
#endif
#ifdef CONFIG_RTC_IOCTL
static int rpmsg_rtc_server_ioctl(FAR struct rtc_lowerhalf_s *lower,
int cmd, unsigned long arg)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
if (server->lower->ops->ioctl != NULL)
{
return server->lower->ops->ioctl(server->lower, cmd, arg);
}
return -ENOTTY;
}
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
static int rpmsg_rtc_server_destroy(FAR struct rtc_lowerhalf_s *lower)
{
FAR struct rpmsg_rtc_server_s *server =
(FAR struct rpmsg_rtc_server_s *)lower;
FAR struct rpmsg_rtc_client_s *client;
FAR struct list_node *node;
FAR struct list_node *temp;
int ret;
ret = server->lower->ops->destroy(server->lower);
if (ret >= 0)
{
list_for_every_safe(&server->list, node, temp)
{
client = (FAR struct rpmsg_rtc_client_s *)node;
list_delete(&client->node);
rpmsg_destroy_ept(&client->ept);
kmm_free(client);
}
kmm_free(server);
}
return ret;
}
#endif
#ifdef CONFIG_RTC_ALARM
static void rpmsg_rtc_server_alarm_cb(FAR void *priv, int alarmid)
{
FAR struct rpmsg_rtc_client_s *client = priv;
struct rpmsg_rtc_alarm_fire_s msg;
msg.header.command = RPMSG_RTC_ALARM_FIRE;
msg.id = alarmid;
rpmsg_send(&client->ept, &msg, sizeof(msg));
}
#endif
static int rpmsg_rtc_server_ept_cb(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len, uint32_t src,
FAR void *priv)
{
FAR struct rpmsg_rtc_header_s *header = data;
switch (header->command)
{
case RPMSG_RTC_GET:
{
FAR struct rpmsg_rtc_get_s *msg = data;
struct rtc_time rtctime =
{
0
};
header->result = rpmsg_rtc_server_rdtime(priv, &rtctime);
msg->sec = timegm((FAR struct tm *)&rtctime);
msg->nsec = rtctime.tm_nsec;
return rpmsg_send(ept, msg, sizeof(*msg));
}
case RPMSG_RTC_SET:
{
FAR struct rpmsg_rtc_set_s *msg = data;
struct rtc_time rtctime =
{
0
};
time_t time = msg->sec;
gmtime_r(&time, (FAR struct tm *)&rtctime);
rtctime.tm_nsec = msg->nsec;
header->result = rpmsg_rtc_server_settime(priv, &rtctime);
return rpmsg_send(ept, msg, sizeof(*msg));
}
#ifdef CONFIG_RTC_ALARM
case RPMSG_RTC_ALARM_SET:
{
FAR struct rpmsg_rtc_client_s *client = container_of(ept,
struct rpmsg_rtc_client_s, ept);
FAR struct rpmsg_rtc_alarm_set_s *msg = data;
time_t time = msg->sec;
struct lower_setalarm_s alarminfo;
alarminfo.id = msg->id;
alarminfo.cb = rpmsg_rtc_server_alarm_cb;
alarminfo.priv = client;
gmtime_r(&time, (FAR struct tm *)&alarminfo.time);
alarminfo.time.tm_nsec = msg->nsec;
header->result = rpmsg_rtc_server_setalarm(priv, &alarminfo);
return rpmsg_send(ept, msg, sizeof(*msg));
}
case RPMSG_RTC_ALARM_CANCEL:
{
FAR struct rpmsg_rtc_alarm_cancel_s *msg = data;
header->result = rpmsg_rtc_server_cancelalarm(priv, msg->id);
return rpmsg_send(ept, msg, sizeof(*msg));
}
#endif
default:
header->result = -ENOSYS;
return rpmsg_send(ept, header, sizeof(*header));
}
}
static void rpmsg_rtc_server_ept_release(FAR struct rpmsg_endpoint *ept)
{
FAR struct rpmsg_rtc_client_s *client = container_of(ept,
struct rpmsg_rtc_client_s, ept);
FAR struct rpmsg_rtc_server_s *server = ept->priv;
nxmutex_lock(&server->lock);
if (list_in_list(&client->node))
{
list_delete(&client->node);
}
nxmutex_unlock(&server->lock);
kmm_free(client);
}
static void rpmsg_rtc_server_sync(FAR struct rpmsg_rtc_server_s *server,
FAR struct rpmsg_rtc_client_s *client)
{
struct rtc_time rtctime;
if (server->lower->ops->rdtime(server->lower, &rtctime) >= 0)
{
struct rpmsg_rtc_set_s msg;
msg.sec = timegm((FAR struct tm *)&rtctime);
msg.nsec = rtctime.tm_nsec;
msg.base_sec = g_basetime.tv_sec;
msg.base_nsec = g_basetime.tv_nsec;
msg.header.command = RPMSG_RTC_SYNC;
rpmsg_send(&client->ept, &msg, sizeof(msg));
}
}
static void rpmsg_rtc_server_ns_bound(FAR struct rpmsg_endpoint *ept)
{
FAR struct rpmsg_rtc_client_s *client;
FAR struct rpmsg_rtc_server_s *server;
client = container_of(ept, struct rpmsg_rtc_client_s, ept);
server = client->ept.priv;
nxmutex_lock(&server->lock);
list_add_tail(&server->list, &client->node);
nxmutex_unlock(&server->lock);
rpmsg_rtc_server_sync(server, client);
}
static void rpmsg_rtc_server_created(FAR struct rpmsg_device *rdev,
FAR void *priv)
{
FAR struct rpmsg_rtc_server_s *server = priv;
FAR struct rpmsg_rtc_client_s *client;
client = kmm_zalloc(sizeof(*client));
if (client == NULL)
{
return;
}
client->ept.priv = server;
client->ept.release_cb = rpmsg_rtc_server_ept_release;
client->ept.ns_bound_cb = rpmsg_rtc_server_ns_bound;
if (rpmsg_create_ept(&client->ept, rdev, RPMSG_RTC_EPT_NAME,
RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
rpmsg_rtc_server_ept_cb,
rpmsg_destroy_ept) < 0)
{
kmm_free(client);
return;
}
}
#endif
/****************************************************************************
* Name: rpmsg_rtc_initialize
*
* Description:
*
* Take remote core RTC as external RTC hardware through rpmsg.
*
* Returned Value:
* Return the lower half RTC driver instance on success;
* A NULL pointer on failure.
*
****************************************************************************/
#ifndef CONFIG_RTC_RPMSG_SERVER
FAR struct rtc_lowerhalf_s *rpmsg_rtc_initialize(void)
{
FAR struct rpmsg_rtc_lowerhalf_s *lower;
lower = kmm_zalloc(sizeof(*lower));
if (lower != NULL)
{
lower->ops = &g_rpmsg_rtc_ops;
rpmsg_register_callback(lower, NULL, NULL,
rpmsg_rtc_device_ns_match,
rpmsg_rtc_device_ns_bind);
}
return (FAR struct rtc_lowerhalf_s *)lower;
}
#else
/****************************************************************************
* Name: rpmsg_rtc_server_initialize
*
* Description:
* Sync RTC info to remote core without external RTC hardware through
* rpmsg.
*
* Returned Value:
* Return the lower half RTC driver instance on success;
* A NULL pointer on failure.
*
****************************************************************************/
FAR struct rtc_lowerhalf_s *rpmsg_rtc_server_initialize(
FAR struct rtc_lowerhalf_s *lower)
{
FAR struct rpmsg_rtc_server_s *server;
server = kmm_zalloc(sizeof(*server));
if (server)
{
server->ops = &g_rpmsg_rtc_server_ops;
server->lower = lower;
list_initialize(&server->list);
nxmutex_init(&server->lock);
if (rpmsg_register_callback(server, rpmsg_rtc_server_created, NULL,
NULL, NULL) < 0)
{
nxmutex_destroy(&server->lock);
kmm_free(server);
return NULL;
}
}
return (FAR struct rtc_lowerhalf_s *)server;
}
#endif