From 11ca1660069bbeaba478a8c8be5dbf2a3c3fb60c Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Sun, 14 Jun 2026 11:37:08 +0800 Subject: [dm][ptp] support Precision Time Protocol (PTP) IEEE 1588 clock Signed-off-by: GuEe-GUI <2991707448@qq.com> --- components/drivers/Kconfig | 1 + components/drivers/include/drivers/ptp.h | 201 +++++++ components/drivers/include/rtdevice.h | 4 + components/drivers/ptp/Kconfig | 8 + components/drivers/ptp/SConscript | 15 + components/drivers/ptp/ptp.c | 650 +++++++++++++++++++++ components/libc/compilers/common/ctime.c | 156 +++++ .../libc/compilers/common/include/sys/time.h | 8 + 8 files changed, 1043 insertions(+) create mode 100755 components/drivers/include/drivers/ptp.h create mode 100755 components/drivers/ptp/Kconfig create mode 100755 components/drivers/ptp/SConscript create mode 100755 components/drivers/ptp/ptp.c (limited to 'components') diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index efe7431a55..db0334f42b 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -21,6 +21,7 @@ rsource "touch/Kconfig" rsource "graphic/Kconfig" rsource "hwcrypto/Kconfig" rsource "wlan/Kconfig" +rsource "ptp/Kconfig" rsource "led/Kconfig" rsource "input/Kconfig" rsource "mailbox/Kconfig" diff --git a/components/drivers/include/drivers/ptp.h b/components/drivers/include/drivers/ptp.h new file mode 100755 index 0000000000..f12c2fea27 --- /dev/null +++ b/components/drivers/include/drivers/ptp.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-04-23 GuEe-GUI first version + */ + +#ifndef __PTP_H__ +#define __PTP_H__ + +#include +#include +#include + +struct rt_ptp_pin; +struct rt_ptp_ops; + +struct rt_ptp_clock +{ + struct rt_device parent; + + const struct rt_ptp_ops *ops; + + /* Maximum frequency adjustment in ppb (parts per billion), required when adjfreq is implemented */ + rt_int32_t max_freq; + rt_int32_t alarm_nr; + rt_int32_t pins_nr; + rt_int32_t extts_nr; + rt_int32_t perout_nr; + rt_int32_t pps; + + rt_list_t notifier_nodes; + struct rt_spinlock nodes_lock; + + struct rt_ptp_pin *pins; + struct rt_mutex pin_mutex; +}; +#define rt_device_to_ptp_clock(dev) rt_container_of(dev, struct rt_ptp_clock, parent) + +struct rt_ptp_clock_time +{ + rt_int64_t sec; + rt_int32_t nsec; +}; + +enum +{ + PTP_CLOCK_EV_ALARM, + PTP_CLOCK_EV_EXTTS, + PTP_CLOCK_EV_EXTOFF, + PTP_CLOCK_EV_PPS, + PTP_CLOCK_EV_PPSUSR, + + PTP_CLOCK_EV_MAX, +}; + +struct rt_ptp_clock_event +{ + rt_uint32_t type; + rt_uint32_t index; + + union + { + rt_uint64_t timestamp; + rt_int64_t offset; + struct rt_ptp_clock_time pps_times; + }; +}; + +struct rt_ptp_clock_notifier +{ + rt_list_t list; + rt_uint32_t event_mask; + + struct rt_ptp_clock *ptp; + void (*callback)(struct rt_ptp_clock_notifier *notifier, struct rt_ptp_clock_event *ev); + + void *priv; +}; + +enum +{ + PTP_PIN_FUNC_NONE, + PTP_PIN_FUNC_EXTTS, + PTP_PIN_FUNC_PEROUT, + PTP_PIN_FUNC_PHYSYNC, + + PTP_PIN_FUNC_MAX, +}; + +struct rt_ptp_pin +{ + char name[64]; + + rt_uint32_t func; + rt_uint32_t chan; +}; + +struct rt_ptp_request_extts +{ + rt_uint32_t chan; + +#define PTP_ENABLE_FEATURE RT_BIT(0) +#define PTP_RISING_EDGE RT_BIT(1) +#define PTP_FALLING_EDGE RT_BIT(2) +#define PTP_STRICT_FLAGS RT_BIT(3) +#define PTP_EXTTS_EDGES (PTP_RISING_EDGE | PTP_FALLING_EDGE) + rt_uint32_t flags; +}; + +struct rt_ptp_request_perout +{ + rt_uint32_t chan; + +#define PTP_PEROUT_ONE_SHOT RT_BIT(0) +#define PTP_PEROUT_DUTY_CYCLE RT_BIT(1) +#define PTP_PEROUT_PHASE RT_BIT(2) + rt_uint32_t flags; + + /* Desired period, zero means disable */ + struct rt_ptp_clock_time period; + /* The "on" time of the signal, must be lower than the period */ + struct rt_ptp_clock_time on; + /* Absolute start time or phase offset */ + struct rt_ptp_clock_time start_phase; +}; + +enum +{ + PTP_CLK_REQ_EXTTS, + PTP_CLK_REQ_PEROUT, + PTP_CLK_REQ_PPS, + + PTP_CLK_REQ_MAX, +}; + +struct rt_ptp_clock_request +{ + rt_uint32_t type; + + union + { + struct rt_ptp_request_extts extts; + struct rt_ptp_request_perout perout; + }; +}; + +struct rt_ptp_ops +{ + rt_err_t (*adjfreq)(struct rt_ptp_clock *ptp, rt_base_t freq); + rt_err_t (*adjphase)(struct rt_ptp_clock *ptp, rt_int32_t phase); + rt_err_t (*getmaxphase)(struct rt_ptp_clock *ptp, rt_int32_t *out_maxphase); + rt_err_t (*adjtime)(struct rt_ptp_clock *ptp, rt_int64_t delta); + rt_err_t (*gettime)(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts); + rt_err_t (*settime)(struct rt_ptp_clock *ptp, const struct rt_ptp_clock_time *ts); + rt_err_t (*enable)(struct rt_ptp_clock *ptp, struct rt_ptp_clock_request *request, rt_bool_t on); + rt_err_t (*verify)(struct rt_ptp_clock *ptp, rt_uint32_t pin, rt_uint32_t func, rt_uint32_t chan); + rt_err_t (*getsnapshot)(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts); +}; + +rt_err_t rt_ptp_clock_register(struct rt_ptp_clock *ptp); +rt_err_t rt_ptp_clock_unregister(struct rt_ptp_clock *ptp); + +rt_err_t rt_ptp_clock_notifier_register(struct rt_ptp_clock *ptp, + struct rt_ptp_clock_notifier *notifier); +rt_err_t rt_ptp_clock_notifier_unregister(struct rt_ptp_clock *ptp, + struct rt_ptp_clock_notifier *notifier); + +void rt_ptp_clock_event(struct rt_ptp_clock *ptp, struct rt_ptp_clock_event *ev); + +rt_err_t rt_ptp_adjfreq(struct rt_ptp_clock *ptp, rt_base_t freq); + +rt_err_t rt_ptp_adjphase(struct rt_ptp_clock *ptp, rt_int32_t phase); +rt_err_t rt_ptp_getmaxphase(struct rt_ptp_clock *ptp, rt_int32_t *out_maxphase); + +rt_err_t rt_ptp_adjtime(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts); + +rt_err_t rt_ptp_gettime(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts); +rt_err_t rt_ptp_settime(struct rt_ptp_clock *ptp, const struct rt_ptp_clock_time *ts); + +rt_err_t rt_ptp_request_extts(struct rt_ptp_clock *ptp, + struct rt_ptp_request_extts *extts); +rt_err_t rt_ptp_request_perout(struct rt_ptp_clock *ptp, + struct rt_ptp_request_perout *perout); +rt_err_t rt_ptp_enable_pps(struct rt_ptp_clock *ptp, rt_bool_t on); + +rt_err_t rt_ptp_get_pin_func(struct rt_ptp_clock *ptp, + rt_uint32_t pin, struct rt_ptp_pin *out_pin); +/* + * Assign pin function only updates pin routing metadata. + * Call rt_ptp_request_extts() or rt_ptp_request_perout() separately to enable hardware. + */ +rt_err_t rt_ptp_set_pin_func(struct rt_ptp_clock *ptp, + rt_uint32_t pin, rt_uint32_t func, rt_uint32_t chan); + +rt_err_t rt_ptp_getsnapshot(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts); + +#endif /* __PTP_H__ */ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index b416b5e928..e6eb43bc57 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -64,6 +64,10 @@ extern "C" { #include "drivers/led.h" #endif /* RT_USING_LED */ +#ifdef RT_USING_PTP +#include "drivers/ptp.h" +#endif /* RT_USING_PTP */ + #ifdef RT_USING_INPUT #include "drivers/input.h" #ifdef RT_INPUT_UAPI diff --git a/components/drivers/ptp/Kconfig b/components/drivers/ptp/Kconfig new file mode 100755 index 0000000000..b07d9ae686 --- /dev/null +++ b/components/drivers/ptp/Kconfig @@ -0,0 +1,8 @@ +menuconfig RT_USING_PTP + bool "Using Precision Time Protocol (PTP) IEEE 1588 clock support" + depends on RT_USING_DM + default n + +if RT_USING_PTP + osource "$(SOC_DM_PTP_DIR)/Kconfig" +endif diff --git a/components/drivers/ptp/SConscript b/components/drivers/ptp/SConscript new file mode 100755 index 0000000000..fb10e8a905 --- /dev/null +++ b/components/drivers/ptp/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_PTP']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['ptp.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/ptp/ptp.c b/components/drivers/ptp/ptp.c new file mode 100755 index 0000000000..bde7a50186 --- /dev/null +++ b/components/drivers/ptp/ptp.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-04-23 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "rtdm.ptp" +#define DBG_LVL DBG_INFO +#include + +#define NSEC_PER_SEC 1000000000L + +static struct rt_dm_ida ptp_ida = RT_DM_IDA_INIT(PTP); + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops _ptp_ops = +{ +}; +#endif + +rt_err_t rt_ptp_clock_register(struct rt_ptp_clock *ptp) +{ + rt_err_t err; + int device_id; + const char *dev_name; + char name[RT_NAME_MAX]; + + if (!ptp || !ptp->ops) + { + return -RT_EINVAL; + } + + if ((device_id = rt_dm_ida_alloc(&ptp_ida)) < 0) + { + return -RT_EFULL; + } + + rt_dm_dev_set_name(&ptp->parent, "ptp%u", device_id); + dev_name = rt_dm_dev_get_name(&ptp->parent); + + if (ptp->pins_nr) + { + if (!ptp->pins) + { + LOG_E("%s: found %d pin but pins is NULL", dev_name, ptp->pins_nr); + err = -RT_EINVAL; + goto _fail; + } + + if (!ptp->ops->verify) + { + LOG_E("%s: found %d pin but verify is not supported", dev_name, ptp->pins_nr); + err = -RT_EINVAL; + goto _fail; + } + + rt_snprintf(name, sizeof(name), "%s-pin", dev_name); + rt_mutex_init(&ptp->pin_mutex, name, RT_IPC_FLAG_PRIO); + } + + if ((ptp->perout_nr || ptp->extts_nr) && !ptp->ops->enable) + { + LOG_E("%s: perout(%u) or extts(%u) but enable is not supported", + dev_name, ptp->perout_nr, ptp->extts_nr); + err = -RT_EINVAL; + goto _free_mutex; + } + + if (ptp->ops->adjfreq && ptp->max_freq <= 0) + { + LOG_E("%s: adjfreq is supported but max_freq is not set", dev_name); + err = -RT_EINVAL; + goto _free_mutex; + } + + rt_list_init(&ptp->notifier_nodes); + rt_spin_lock_init(&ptp->nodes_lock); + + /* Just make a search interface */ + ptp->parent.type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + ptp->parent.ops = ptp->parent.ops ? : &_ptp_ops; +#endif + ptp->parent.master_id = ptp_ida.master_id; + ptp->parent.device_id = device_id; + + if ((err = rt_device_register(&ptp->parent, dev_name, RT_DEVICE_FLAG_DEACTIVATE))) + { + goto _free_mutex; + } + + return RT_EOK; + +_free_mutex: + if (ptp->pins_nr) + { + rt_mutex_detach(&ptp->pin_mutex); + } + +_fail: + rt_dm_ida_free(&ptp_ida, device_id); + + return err; +} + +rt_err_t rt_ptp_clock_unregister(struct rt_ptp_clock *ptp) +{ + struct rt_ptp_request_perout perout; + struct rt_ptp_request_extts extts; + + if (!ptp) + { + return -RT_EINVAL; + } + + if (ptp->parent.ref_count) + { + LOG_E("%s: there is %u user", + rt_dm_dev_get_name(&ptp->parent), ptp->parent.ref_count); + return -RT_EINVAL; + } + + if (!rt_list_isempty(&ptp->notifier_nodes)) + { + LOG_W("%s: notifier not unregister", rt_dm_dev_get_name(&ptp->parent)); + } + + rt_memset(&extts, 0, sizeof(extts)); + + for (int i = 0; i < ptp->extts_nr; ++i) + { + extts.chan = i; + rt_ptp_request_extts(ptp, &extts); + } + + rt_memset(&perout, 0, sizeof(perout)); + perout.flags = PTP_PEROUT_ONE_SHOT; + + for (int i = 0; i < ptp->perout_nr; ++i) + { + perout.chan = i; + rt_ptp_request_perout(ptp, &perout); + } + + rt_ptp_enable_pps(ptp, RT_FALSE); + + if (ptp->pins_nr) + { + rt_mutex_detach(&ptp->pin_mutex); + } + + rt_dm_ida_free(&ptp_ida, ptp->parent.device_id); + + return rt_device_unregister(&ptp->parent); +} + +rt_err_t rt_ptp_clock_notifier_register(struct rt_ptp_clock *ptp, + struct rt_ptp_clock_notifier *notifier) +{ + if (!ptp || !notifier) + { + return -RT_EINVAL; + } + + if (!notifier->event_mask) + { + LOG_E("%s: clock notifier event mask is empty, try to set 0~%u", + rt_dm_dev_get_name(&ptp->parent), PTP_CLOCK_EV_MAX - 1); + return -RT_EINVAL; + } + + notifier->ptp = ptp; + rt_list_init(¬ifier->list); + + rt_spin_lock(&ptp->nodes_lock); + rt_list_insert_after(&ptp->notifier_nodes, ¬ifier->list); + rt_spin_unlock(&ptp->nodes_lock); + + return RT_EOK; +} + +rt_err_t rt_ptp_clock_notifier_unregister(struct rt_ptp_clock *ptp, + struct rt_ptp_clock_notifier *notifier) +{ + if (!ptp || !notifier) + { + return -RT_EINVAL; + } + + rt_spin_lock(&ptp->nodes_lock); + rt_list_remove(¬ifier->list); + rt_spin_unlock(&ptp->nodes_lock); + + return RT_EOK; +} + +void rt_ptp_clock_event(struct rt_ptp_clock *ptp, struct rt_ptp_clock_event *ev) +{ + struct rt_ptp_clock_notifier *notifier, *next_notifier; + + RT_ASSERT(ptp != RT_NULL); + RT_ASSERT(ev->type < PTP_CLOCK_EV_MAX); + + rt_spin_lock(&ptp->nodes_lock); + + rt_list_for_each_entry_safe(notifier, next_notifier, &ptp->notifier_nodes, list) + { + if (notifier->event_mask & RT_BIT(ev->type)) + { + rt_spin_unlock(&ptp->nodes_lock); + + notifier->callback(notifier, ev); + + rt_spin_lock(&ptp->nodes_lock); + } + } + + rt_spin_unlock(&ptp->nodes_lock); +} + +rt_err_t rt_ptp_adjfreq(struct rt_ptp_clock *ptp, rt_base_t freq) +{ + if (!ptp) + { + return -RT_EINVAL; + } + + if (!ptp->ops->adjfreq) + { + return -RT_ENOSYS; + } + + if (ptp->max_freq <= 0) + { + if (freq != 0) + { + return -RT_EINVAL; + } + + return RT_EOK; + } + + if (freq > ptp->max_freq || freq < -ptp->max_freq) + { + return -RT_EINVAL; + } + + return ptp->ops->adjfreq(ptp, freq); +} + +rt_err_t rt_ptp_adjphase(struct rt_ptp_clock *ptp, rt_int32_t phase) +{ + rt_err_t err; + rt_int32_t maxphase; + + if (!ptp) + { + return -RT_EINVAL; + } + + if (!ptp->ops->adjphase) + { + return -RT_ENOSYS; + } + + if ((err = rt_ptp_getmaxphase(ptp, &maxphase))) + { + return err; + } + + if (phase > maxphase || phase < -maxphase) + { + return -RT_EINVAL; + } + + return ptp->ops->adjphase(ptp, phase); +} + +rt_err_t rt_ptp_getmaxphase(struct rt_ptp_clock *ptp, rt_int32_t *out_maxphase) +{ + if (!ptp || !out_maxphase) + { + return -RT_EINVAL; + } + + if (!ptp->ops->getmaxphase) + { + return -RT_ENOSYS; + } + + return ptp->ops->getmaxphase(ptp, out_maxphase); +} + +rt_err_t rt_ptp_adjtime(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts) +{ + rt_int64_t delta; + + if (!ptp || !ts) + { + return -RT_EINVAL; + } + + if (!ptp->ops->adjtime) + { + return -RT_ENOSYS; + } + + if (ts->nsec < 0 || (rt_int64_t)ts->nsec >= NSEC_PER_SEC) + { + return -RT_EINVAL; + } + + delta = ts->sec * NSEC_PER_SEC + (rt_int64_t)ts->nsec; + + return ptp->ops->adjtime(ptp, delta); +} + +rt_err_t rt_ptp_gettime(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts) +{ + if (!ptp || !ts) + { + return -RT_EINVAL; + } + + if (!ptp->ops->gettime) + { + return -RT_ENOSYS; + } + + return ptp->ops->gettime(ptp, ts); +} + +rt_err_t rt_ptp_settime(struct rt_ptp_clock *ptp, const struct rt_ptp_clock_time *ts) +{ + if (!ptp || !ts) + { + return -RT_EINVAL; + } + + if (!ptp->ops->settime) + { + return -RT_ENOSYS; + } + + return ptp->ops->settime(ptp, ts); +} + +rt_err_t rt_ptp_request_extts(struct rt_ptp_clock *ptp, + struct rt_ptp_request_extts *extts) +{ + rt_bool_t enable; + struct rt_ptp_clock_request request; + + if (!ptp || !extts) + { + return -RT_EINVAL; + } + + if (extts->chan >= ptp->extts_nr) + { + return -RT_EINVAL; + } + + if (extts->flags & PTP_STRICT_FLAGS) + { + /* Ensure one of the rising/falling edge bits is set */ + if ((extts->flags & PTP_ENABLE_FEATURE) && + (extts->flags & PTP_EXTTS_EDGES) == 0) + { + return -RT_EINVAL; + } + } + + if (!ptp->ops->enable) + { + return -RT_ENOSYS; + } + + request.type = PTP_CLK_REQ_EXTTS; + rt_memcpy(&request.extts, extts, sizeof(*extts)); + enable = !!(extts->flags & PTP_ENABLE_FEATURE); + + return ptp->ops->enable(ptp, &request, enable); +} + +rt_err_t rt_ptp_request_perout(struct rt_ptp_clock *ptp, + struct rt_ptp_request_perout *perout) +{ + rt_bool_t enable; + struct rt_ptp_clock_request request; + + if (!ptp || !perout) + { + return -RT_EINVAL; + } + + if (perout->chan >= ptp->perout_nr) + { + return -RT_EINVAL; + } + + if (perout->flags & PTP_STRICT_FLAGS) + { + if (perout->flags & PTP_PEROUT_DUTY_CYCLE) + { + /* The duty cycle must be subunitary */ + if (perout->on.sec > perout->period.sec || + (perout->on.sec == perout->period.sec && + perout->on.nsec > perout->period.nsec)) + { + return -RT_EINVAL; + } + } + + if (perout->flags & PTP_PEROUT_PHASE) + { + /* + * The phase should be specified modulo the period, + * therefore anything equal or larger than 1 period is invalid + */ + if (perout->start_phase.sec > perout->period.sec || + (perout->start_phase.sec == perout->period.sec && + perout->start_phase.nsec >= perout->period.nsec)) + { + return -RT_EINVAL; + } + } + } + + if (!ptp->ops->enable) + { + return -RT_ENOSYS; + } + + request.type = PTP_CLK_REQ_PEROUT; + rt_memcpy(&request.perout, perout, sizeof(*perout)); + enable = perout->period.sec || perout->period.nsec; + + return ptp->ops->enable(ptp, &request, enable); +} + +rt_err_t rt_ptp_enable_pps(struct rt_ptp_clock *ptp, rt_bool_t on) +{ + struct rt_ptp_clock_request request; + + if (!ptp) + { + return -RT_EINVAL; + } + + if (!ptp->ops->enable) + { + return -RT_ENOSYS; + } + + request.type = PTP_CLK_REQ_PPS; + + return ptp->ops->enable(ptp, &request, on); +} + +rt_err_t rt_ptp_get_pin_func(struct rt_ptp_clock *ptp, + rt_uint32_t pin, struct rt_ptp_pin *out_pin) +{ + if (!ptp || !out_pin) + { + return -RT_EINVAL; + } + + if (pin >= ptp->pins_nr) + { + if (!ptp->pins_nr) + { + return -RT_ENOSYS; + } + + return -RT_EINVAL; + } + + rt_mutex_take(&ptp->pin_mutex, RT_WAITING_FOREVER); + + rt_memcpy(out_pin, &ptp->pins[pin], sizeof(*out_pin)); + + rt_mutex_release(&ptp->pin_mutex); + + return RT_EOK; +} + +static rt_err_t ptp_disable_pin_func(struct rt_ptp_clock *ptp, + rt_uint32_t func, rt_uint32_t chan) +{ + rt_err_t err = RT_EOK; + struct rt_ptp_request_extts extts; + struct rt_ptp_request_perout perout; + + switch (func) + { + case PTP_PIN_FUNC_NONE: + case PTP_PIN_FUNC_PHYSYNC: + break; + + case PTP_PIN_FUNC_EXTTS: + rt_memset(&extts, 0, sizeof(extts)); + extts.chan = chan; + err = rt_ptp_request_extts(ptp, &extts); + break; + + case PTP_PIN_FUNC_PEROUT: + rt_memset(&perout, 0, sizeof(perout)); + perout.chan = chan; + err = rt_ptp_request_perout(ptp, &perout); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +static rt_err_t ptp_set_pin_func(struct rt_ptp_clock *ptp, + rt_uint32_t pin, rt_uint32_t func, rt_uint32_t chan) +{ + int i; + rt_err_t err; + struct rt_ptp_pin *pin1 = RT_NULL, *pin2 = &ptp->pins[pin]; + + for (i = 0; i < ptp->pins_nr; ++i) + { + if (ptp->pins[i].func == func && ptp->pins[i].chan == chan) + { + pin1 = &ptp->pins[i]; + break; + } + } + + /* Configure already, do nothing */ + if (pin1 && i == pin) + { + return RT_EOK; + } + + switch (func) + { + case PTP_PIN_FUNC_NONE: + break; + + case PTP_PIN_FUNC_EXTTS: + if (chan >= ptp->extts_nr) + { + return -RT_EINVAL; + } + break; + + case PTP_PIN_FUNC_PEROUT: + if (chan >= ptp->perout_nr) + { + return -RT_EINVAL; + } + break; + + case PTP_PIN_FUNC_PHYSYNC: + if (chan != 0) + { + return -RT_EINVAL; + } + break; + + default: + return -RT_EINVAL; + } + + if ((err = ptp->ops->verify(ptp, pin, func, chan))) + { + LOG_E("%s: pin %u function %u channel %u error = %s", + rt_dm_dev_get_name(&ptp->parent), pin, func, chan, rt_strerror(err)); + return err; + } + + /* Disable configure before, update new configure */ + if (pin1) + { + ptp_disable_pin_func(ptp, func, chan); + pin1->func = PTP_PIN_FUNC_NONE; + pin1->chan = 0; + } + + ptp_disable_pin_func(ptp, pin2->func, pin2->chan); + pin2->func = func; + pin2->chan = chan; + + return RT_EOK; +} + +rt_err_t rt_ptp_set_pin_func(struct rt_ptp_clock *ptp, + rt_uint32_t pin, rt_uint32_t func, rt_uint32_t chan) +{ + rt_err_t err; + + if (!ptp) + { + return -RT_EINVAL; + } + + if (pin >= ptp->pins_nr) + { + if (!ptp->pins_nr) + { + return -RT_ENOSYS; + } + + return -RT_EINVAL; + } + + if (func >= PTP_PIN_FUNC_MAX) + { + return -RT_EINVAL; + } + + rt_mutex_take(&ptp->pin_mutex, RT_WAITING_FOREVER); + + err = ptp_set_pin_func(ptp, pin, func, chan); + + rt_mutex_release(&ptp->pin_mutex); + + return err; +} + +rt_err_t rt_ptp_getsnapshot(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts) +{ + if (!ptp || !ts) + { + return -RT_EINVAL; + } + + if (!ptp->ops->getsnapshot) + { + return -RT_ENOSYS; + } + + return ptp->ops->getsnapshot(ptp, ts); +} diff --git a/components/libc/compilers/common/ctime.c b/components/libc/compilers/common/ctime.c index 3e1ea77fd7..60d5dc74a4 100644 --- a/components/libc/compilers/common/ctime.c +++ b/components/libc/compilers/common/ctime.c @@ -45,6 +45,13 @@ #ifdef RT_USING_CLOCK_TIME #include #endif +#ifdef RT_USING_PTP +#include +#ifdef RT_USING_POSIX_FS +#include +#include +#endif /* RT_USING_POSIX_FS */ +#endif /* RT_USING_PTP */ #define DBG_TAG "time" #define DBG_LVL DBG_INFO @@ -611,6 +618,134 @@ RTM_EXPORT(nanosleep); #if defined(RT_USING_POSIX_CLOCK) && defined(RT_USING_CLOCK_TIME) +#ifdef RT_USING_PTP + +static rt_bool_t _clockid_is_ptp_fd(clockid_t clockid) +{ + return (clockid & 7) == CLOCKFD; +} + +static struct rt_ptp_clock *_ptp_clock_from_clockid(clockid_t clockid) +{ + unsigned int fd; + struct rt_ptp_clock *ptp; + rt_device_t dev; + +#ifndef RT_USING_POSIX_FS + RT_UNUSED(fd); + RT_UNUSED(ptp); + RT_UNUSED(dev); + return RT_NULL; +#else + struct dfs_file *file; + + if (!_clockid_is_ptp_fd(clockid)) + { + return RT_NULL; + } + + fd = CLOCKID_TO_FD(clockid); + file = fd_get((int)fd); + if (!file || !file->vnode || !file->vnode->data) + { + return RT_NULL; + } + + dev = (rt_device_t)file->vnode->data; + if (dev->type != RT_Device_Class_Char) + { + return RT_NULL; + } + + if (rt_strncmp(rt_dm_dev_get_name(dev), "ptp", 3) != 0) + { + return RT_NULL; + } + + ptp = rt_device_to_ptp_clock(dev); + if (!ptp->ops || !ptp->ops->gettime) + { + return RT_NULL; + } + + return ptp; +#endif /* RT_USING_POSIX_FS */ +} + +static int _clock_gettime_ptp(clockid_t clockid, struct timespec *tp) +{ + struct rt_ptp_clock *ptp; + struct rt_ptp_clock_time ts; + rt_err_t err; + + ptp = _ptp_clock_from_clockid(clockid); + if (!ptp) + { + rt_set_errno(EINVAL); + return -1; + } + + err = rt_ptp_gettime(ptp, &ts); + if (err != RT_EOK) + { + rt_set_errno(EINVAL); + return -1; + } + + tp->tv_sec = (time_t)ts.sec; + tp->tv_nsec = ts.nsec; + + return 0; +} + +static int _clock_settime_ptp(clockid_t clockid, const struct timespec *tp) +{ + struct rt_ptp_clock *ptp; + struct rt_ptp_clock_time ts; + rt_err_t err; + + ptp = _ptp_clock_from_clockid(clockid); + if (!ptp) + { + rt_set_errno(EINVAL); + return -1; + } + + if (!ptp->ops->settime) + { + rt_set_errno(EPERM); + return -1; + } + + ts.sec = tp->tv_sec; + ts.nsec = (rt_int32_t)tp->tv_nsec; + + err = rt_ptp_settime(ptp, &ts); + if (err != RT_EOK) + { + rt_set_errno(EINVAL); + return -1; + } + + return 0; +} + +static int _clock_getres_ptp(clockid_t clockid, struct timespec *res) +{ + if (!_ptp_clock_from_clockid(clockid)) + { + rt_set_errno(EINVAL); + return -1; + } + + res->tv_sec = 0; + res->tv_nsec = 1; + + return 0; +} + +#endif /* RT_USING_PTP */ + int clock_getres(clockid_t clockid, struct timespec *res) { if (res == RT_NULL) @@ -619,6 +754,13 @@ int clock_getres(clockid_t clockid, struct timespec *res) return -1; } +#ifdef RT_USING_PTP + if (_clockid_is_ptp_fd(clockid)) + { + return _clock_getres_ptp(clockid, res); + } +#endif /* RT_USING_PTP */ + switch (clockid) { case CLOCK_REALTIME: // use RTC @@ -652,6 +794,13 @@ int clock_gettime(clockid_t clockid, struct timespec *tp) return -1; } +#ifdef RT_USING_PTP + if (_clockid_is_ptp_fd(clockid)) + { + return _clock_gettime_ptp(clockid, tp); + } +#endif /* RT_USING_PTP */ + switch (clockid) { case CLOCK_REALTIME: // use RTC @@ -750,6 +899,13 @@ int clock_settime(clockid_t clockid, const struct timespec *tp) return -1; } +#ifdef RT_USING_PTP + if (_clockid_is_ptp_fd(clockid)) + { + return _clock_settime_ptp(clockid, tp); + } +#endif /* RT_USING_PTP */ + switch (clockid) { #ifdef RT_USING_RTC diff --git a/components/libc/compilers/common/include/sys/time.h b/components/libc/compilers/common/include/sys/time.h index 817fe78676..8804433eeb 100644 --- a/components/libc/compilers/common/include/sys/time.h +++ b/components/libc/compilers/common/include/sys/time.h @@ -198,6 +198,14 @@ int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); #define CLOCK_TAI 11 // newlib says they don't have this definition, make the compiler happy #endif /* CLOCK_TAI */ +#ifdef RT_USING_PTP +#ifndef CLOCKFD +#define CLOCKFD 3 +#endif /* CLOCKFD */ +#define FD_TO_CLOCKID(fd) ((clockid_t)((~(clockid_t)(fd) << 3) | CLOCKFD)) +#define CLOCKID_TO_FD(clk) ((unsigned int)(~((unsigned int)(clk) >> 3))) +#endif /* RT_USING_PTP */ + #endif /* defined(RT_USING_POSIX_CLOCK) || defined (RT_USING_POSIX_TIMER) */ #ifdef RT_USING_POSIX_CLOCK -- cgit v1.3.1