6a3c2aded6
* Simplify EINTR/ECANCEL error handling 1. Add semaphore uninterruptible wait function 2 .Replace semaphore wait loop with a single uninterruptible wait 3. Replace all sem_xxx to nxsem_xxx * Unify the void cast usage 1. Remove void cast for function because many place ignore the returned value witout cast 2. Replace void cast for variable with UNUSED macro
421 lines
13 KiB
C
421 lines
13 KiB
C
/****************************************************************************
|
|
* drivers/timers/rpmsg_rtc.c
|
|
*
|
|
* Copyright (C) 2017 Pinecone Inc. All rights reserved.
|
|
* Author: Guiding Li <liguiding@pinecone.net>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <nuttx/clock.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/rptun/openamp.h>
|
|
#include <nuttx/semaphore.h>
|
|
#include <nuttx/timers/rpmsg_rtc.h>
|
|
|
|
#include <errno.h>
|
|
#include <string.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
|
|
|
|
/****************************************************************************
|
|
* 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 sec;
|
|
int32_t 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
|
|
|
|
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;
|
|
FAR const char *cpuname;
|
|
|
|
#ifdef CONFIG_RTC_ALARM
|
|
struct lower_setalarm_s alarminfo[CONFIG_RTC_NALARMS];
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static void rpmsg_rtc_device_created(FAR struct rpmsg_device *rdev,
|
|
FAR void *priv);
|
|
static void rpmsg_rtc_device_destroy(FAR struct rpmsg_device *rdev,
|
|
FAR void *priv);
|
|
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
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct rtc_ops_s g_rpmsg_rtc_ops =
|
|
{
|
|
.rdtime = rpmsg_rtc_rdtime,
|
|
.settime = rpmsg_rtc_settime,
|
|
.havesettime = rpmsg_rtc_havesettime,
|
|
#ifdef CONFIG_RTC_ALARM
|
|
.setalarm = rpmsg_rtc_setalarm,
|
|
.setrelative = rpmsg_rtc_setrelative,
|
|
.cancelalarm = rpmsg_rtc_cancelalarm,
|
|
.rdalarm = rpmsg_rtc_rdalarm,
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static void rpmsg_rtc_device_created(FAR struct rpmsg_device *rdev,
|
|
FAR void *priv)
|
|
{
|
|
FAR struct rpmsg_rtc_lowerhalf_s *lower = priv;
|
|
|
|
if (strcmp(lower->cpuname, rpmsg_get_cpuname(rdev)) == 0)
|
|
{
|
|
lower->ept.priv = lower;
|
|
|
|
rpmsg_create_ept(&lower->ept, rdev, RPMSG_RTC_EPT_NAME,
|
|
RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
|
|
rpmsg_rtc_ept_cb, NULL);
|
|
}
|
|
}
|
|
|
|
static void rpmsg_rtc_device_destroy(FAR struct rpmsg_device *rdev,
|
|
FAR void *priv)
|
|
{
|
|
FAR struct rpmsg_rtc_lowerhalf_s *lower = priv;
|
|
|
|
if (strcmp(lower->cpuname, rpmsg_get_cpuname(rdev)) == 0)
|
|
{
|
|
rpmsg_destroy_ept(&lower->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 =
|
|
(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;
|
|
|
|
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)
|
|
{
|
|
FAR struct rpmsg_rtc_cookie_s cookie;
|
|
int ret;
|
|
|
|
nxsem_init(&cookie.sem, 0, 0);
|
|
nxsem_setprotocol(&cookie.sem, SEM_PRIO_NONE);
|
|
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 =
|
|
{
|
|
.sec = mktime((FAR struct tm *)rtctime),
|
|
.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 =
|
|
{
|
|
.sec = mktime((FAR struct tm *)&alarminfo->time),
|
|
.nsec = alarminfo->time.tm_nsec,
|
|
.id = alarminfo->id,
|
|
};
|
|
|
|
int ret;
|
|
|
|
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 =
|
|
{
|
|
.id = relinfo->id,
|
|
.cb = relinfo->cb,
|
|
.priv = relinfo->priv,
|
|
};
|
|
|
|
time_t time;
|
|
|
|
rpmsg_rtc_rdtime(lower, &alarminfo.time);
|
|
time = mktime((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 =
|
|
{
|
|
.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
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsg_rtc_initialize
|
|
*
|
|
* Description:
|
|
*
|
|
* Take remote core RTC as external RTC hardware through rpmsg.
|
|
*
|
|
* Input Parameters:
|
|
* cpuname - current cpu name
|
|
* minor - device minor number
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) on success; a negated errno on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
FAR struct rtc_lowerhalf_s *rpmsg_rtc_initialize(FAR const char *cpuname,
|
|
int minor)
|
|
{
|
|
FAR struct rpmsg_rtc_lowerhalf_s *lower;
|
|
|
|
lower = kmm_zalloc(sizeof(*lower));
|
|
if (lower)
|
|
{
|
|
lower->ops = &g_rpmsg_rtc_ops;
|
|
lower->cpuname = cpuname;
|
|
|
|
rpmsg_register_callback(lower,
|
|
rpmsg_rtc_device_created,
|
|
rpmsg_rtc_device_destroy,
|
|
NULL);
|
|
|
|
rtc_initialize(minor, (FAR struct rtc_lowerhalf_s *)lower);
|
|
}
|
|
|
|
return (FAR struct rtc_lowerhalf_s *)lower;
|
|
}
|