mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
fs_getfilep()/fs_putfilep() were renamed to file_get()/file_put(). The PTP clock paths still used the old names, breaking the PTP_CLOCK build. Signed-off-by: raiden00pl <raiden00@railab.me>
483 lines
13 KiB
C
483 lines
13 KiB
C
/****************************************************************************
|
|
* drivers/timers/ptp_clock.c
|
|
*
|
|
* 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 <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <nuttx/debug.h>
|
|
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/mutex.h>
|
|
#include <nuttx/timers/ptp_clock.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* This structure describes the state of the upper half driver */
|
|
|
|
struct ptp_upperhalf_s
|
|
{
|
|
FAR struct ptp_lowerhalf_s *lower; /* The handle of lower half driver */
|
|
mutex_t lock; /* Manages exclusive access to file operations */
|
|
long max_adj; /* The maximum frequency adjustment */
|
|
long adj_freq; /* remembers the frequency adjustment */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static ssize_t ptp_clock_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static int ptp_clock_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct file_operations g_ptp_clock_file_ops =
|
|
{
|
|
NULL, /* open */
|
|
NULL, /* close */
|
|
ptp_clock_read, /* read */
|
|
NULL, /* write */
|
|
NULL, /* seek */
|
|
ptp_clock_ioctl, /* ioctl */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static ssize_t ptp_clock_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline long ptp_clock_scaled_ppm_to_ppb(long ppm)
|
|
{
|
|
/* The 'freq' field in the 'struct timex' is in parts per
|
|
* million, but with a 16 bit binary fractional field.
|
|
*
|
|
* We want to calculate
|
|
*
|
|
* ppb = scaled_ppm * 1000 / 2^16
|
|
*
|
|
* which simplifies to
|
|
*
|
|
* ppb = scaled_ppm * 125 / 2^13
|
|
*/
|
|
|
|
int64_t ppb = 1 + ppm;
|
|
|
|
ppb *= 125;
|
|
ppb >>= 13;
|
|
return (long)ppb;
|
|
}
|
|
|
|
static int ptp_clock_adjtime(FAR struct ptp_lowerhalf_s *lower,
|
|
FAR struct timex *tx)
|
|
{
|
|
FAR struct ptp_upperhalf_s *upper = lower->upper;
|
|
int ret = -ENOTSUP;
|
|
|
|
if (tx->modes & ADJ_SETOFFSET)
|
|
{
|
|
struct timespec ts;
|
|
int64_t delta;
|
|
|
|
ts.tv_sec = tx->time.tv_sec;
|
|
ts.tv_nsec = tx->time.tv_usec;
|
|
if (!(tx->modes & ADJ_NANO))
|
|
{
|
|
ts.tv_nsec *= 1000;
|
|
}
|
|
|
|
if ((unsigned long)ts.tv_nsec >= NSEC_PER_SEC)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
delta = ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
|
|
ret = lower->ops->adjtime(lower, delta);
|
|
}
|
|
else if ((tx->modes & ADJ_FREQUENCY) && lower->ops->adjfine != NULL)
|
|
{
|
|
long ppb = ptp_clock_scaled_ppm_to_ppb(tx->freq);
|
|
|
|
if (ppb > upper->max_adj || ppb < -upper->max_adj)
|
|
{
|
|
return -ERANGE;
|
|
}
|
|
|
|
ret = lower->ops->adjfine(lower, ppb);
|
|
upper->adj_freq = tx->freq;
|
|
}
|
|
else if ((tx->modes & ADJ_OFFSET) &&
|
|
lower->ops->adjphase != NULL)
|
|
{
|
|
int32_t offset = tx->offset;
|
|
|
|
if (!(tx->modes & ADJ_NANO))
|
|
{
|
|
offset *= NSEC_PER_USEC;
|
|
}
|
|
|
|
ret = lower->ops->adjphase(lower, offset);
|
|
}
|
|
else if (tx->modes == 0)
|
|
{
|
|
tx->freq = upper->adj_freq;
|
|
ret = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ptp_clock_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
FAR struct ptp_upperhalf_s *upper = filep->f_inode->i_private;
|
|
FAR struct ptp_lowerhalf_s *lower = upper->lower;
|
|
int ret = -ENOTTY;
|
|
int i;
|
|
|
|
nxmutex_lock(&upper->lock);
|
|
switch (cmd)
|
|
{
|
|
case PTP_CLOCK_SETTIME:
|
|
{
|
|
ret = -ENOTSUP;
|
|
if (lower->ops->settime)
|
|
{
|
|
ret = lower->ops->settime(lower,
|
|
(FAR const struct timespec *)(uintptr_t)arg);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PTP_CLOCK_GETTIME:
|
|
{
|
|
ret = -ENOTSUP;
|
|
if (lower->ops->gettime)
|
|
{
|
|
ret = lower->ops->gettime(lower,
|
|
(FAR struct timespec *)(uintptr_t)arg, NULL);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PTP_CLOCK_GETRES:
|
|
{
|
|
ret = -ENOTSUP;
|
|
if (lower->ops->getres)
|
|
{
|
|
ret = lower->ops->getres(lower,
|
|
(FAR struct timespec *)(uintptr_t)arg);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PTP_CLOCK_ADJTIME:
|
|
{
|
|
ret = ptp_clock_adjtime(lower, (FAR struct timex *)(uintptr_t)arg);
|
|
}
|
|
break;
|
|
|
|
case PTP_CLOCK_GETCAPS:
|
|
case PTP_CLOCK_GETCAPS2:
|
|
{
|
|
FAR struct ptp_clock_caps *caps = (FAR struct ptp_clock_caps *)
|
|
(uintptr_t)arg;
|
|
|
|
memset(caps, 0, sizeof(*caps));
|
|
caps->max_adj = upper->max_adj;
|
|
caps->cross_timestamping = lower->ops->getcrosststamp != NULL;
|
|
caps->adjust_phase = lower->ops->adjphase != NULL;
|
|
ret = OK;
|
|
}
|
|
break;
|
|
|
|
case PTP_SYS_OFFSET_PRECISE:
|
|
case PTP_SYS_OFFSET_PRECISE2:
|
|
{
|
|
FAR struct ptp_sys_offset_precise *preoff =
|
|
(FAR struct ptp_sys_offset_precise *)(uintptr_t)arg;
|
|
struct system_device_crosststamp xtstamp;
|
|
|
|
if (!lower->ops->getcrosststamp)
|
|
{
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
ret = lower->ops->getcrosststamp(lower, &xtstamp);
|
|
if (ret != 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
memset(preoff, 0, sizeof(*preoff));
|
|
preoff->device.sec = xtstamp.device.tv_sec;
|
|
preoff->device.nsec = xtstamp.device.tv_nsec;
|
|
preoff->sys_realtime.sec = xtstamp.realtime.tv_sec;
|
|
preoff->sys_realtime.nsec = xtstamp.realtime.tv_nsec;
|
|
preoff->sys_monoraw.sec = xtstamp.monoraw.tv_sec;
|
|
preoff->sys_monoraw.nsec = xtstamp.monoraw.tv_nsec;
|
|
}
|
|
break;
|
|
|
|
case PTP_SYS_OFFSET_EXTENDED:
|
|
case PTP_SYS_OFFSET_EXTENDED2:
|
|
{
|
|
FAR struct ptp_sys_offset_extended *extoff =
|
|
(FAR struct ptp_sys_offset_extended *)(uintptr_t)arg;
|
|
struct ptp_system_timestamp sts;
|
|
struct timespec ts;
|
|
|
|
if (!lower->ops->gettime)
|
|
{
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
if (extoff->n_samples > PTP_MAX_SAMPLES ||
|
|
extoff->rsv[0] || extoff->rsv[1] || extoff->rsv[2])
|
|
{
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < extoff->n_samples; i++)
|
|
{
|
|
ret = lower->ops->gettime(lower, &ts, &sts);
|
|
if (ret < 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
extoff->ts[i][0].sec = sts.pre_ts.tv_sec;
|
|
extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec;
|
|
extoff->ts[i][1].sec = ts.tv_sec;
|
|
extoff->ts[i][1].nsec = ts.tv_nsec;
|
|
extoff->ts[i][2].sec = sts.post_ts.tv_sec;
|
|
extoff->ts[i][2].nsec = sts.post_ts.tv_nsec;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PTP_SYS_OFFSET:
|
|
case PTP_SYS_OFFSET2:
|
|
{
|
|
FAR struct ptp_sys_offset *sysoff =
|
|
(FAR struct ptp_sys_offset *)(uintptr_t)arg;
|
|
FAR struct ptp_clock_time *pct;
|
|
struct timespec ts;
|
|
|
|
if (lower->ops->gettime == NULL)
|
|
{
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
if (sysoff->n_samples > PTP_MAX_SAMPLES)
|
|
{
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
pct = &sysoff->ts[0];
|
|
for (i = 0; i < sysoff->n_samples; i++)
|
|
{
|
|
nxclock_gettime(CLOCK_REALTIME, &ts);
|
|
pct->sec = ts.tv_sec;
|
|
pct->nsec = ts.tv_nsec;
|
|
pct++;
|
|
ret = lower->ops->gettime(lower, &ts, NULL);
|
|
if (ret < 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pct->sec = ts.tv_sec;
|
|
pct->nsec = ts.tv_nsec;
|
|
pct++;
|
|
}
|
|
|
|
nxclock_gettime(CLOCK_REALTIME, &ts);
|
|
pct->sec = ts.tv_sec;
|
|
pct->nsec = ts.tv_nsec;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
if (lower->ops->control)
|
|
{
|
|
ret = lower->ops->control(lower, cmd, arg);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
nxmutex_unlock(&upper->lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ptp_clockid_to_filep
|
|
*
|
|
* Description:
|
|
* Convert clockid to struct filep.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ptp_clockid_to_filep(clockid_t clock_id, FAR struct file **filep)
|
|
{
|
|
FAR const struct file_operations *ops;
|
|
int ret;
|
|
|
|
if ((clock_id & CLOCK_MASK) != CLOCK_FD)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = clock_id >> CLOCK_SHIFT;
|
|
if (ret >= 0)
|
|
{
|
|
ret = file_get(ret, filep);
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ops = (*filep)->f_inode->u.i_ops;
|
|
if (ops != &g_ptp_clock_file_ops)
|
|
{
|
|
file_put(*filep);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ptp_clock_register
|
|
*
|
|
* Description:
|
|
* This function binds an instance of a "lower half" ptp driver with the
|
|
* "upper half" ptp device and registers that device so that can be used
|
|
* by application code.
|
|
*
|
|
* Input Parameters:
|
|
* lower - A pointer to an instance of lower half ptp driver. This
|
|
* instance is bound to the ptp driver and must persists as long
|
|
* as the driver persists.
|
|
* mxa_adj - The maximum frequency adjustment in parts per billion.
|
|
* devno - The user specifies number of device. ex: /dev/ptpX.
|
|
*
|
|
* Returned Value:
|
|
* OK if the driver was successfully registered; A negated errno value is
|
|
* returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ptp_clock_register(FAR struct ptp_lowerhalf_s *lower, int32_t max_adj,
|
|
int devno)
|
|
{
|
|
FAR struct ptp_upperhalf_s *upper;
|
|
char path[16];
|
|
int ret;
|
|
|
|
DEBUGASSERT(lower != NULL);
|
|
|
|
/* Allocate the upper-half data structure */
|
|
|
|
upper = kmm_zalloc(sizeof(struct ptp_upperhalf_s));
|
|
if (!upper)
|
|
{
|
|
ptperr("ERROR: Allocation failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
upper->lower = lower;
|
|
upper->max_adj = max_adj;
|
|
lower->upper = upper;
|
|
nxmutex_init(&upper->lock);
|
|
|
|
snprintf(path, sizeof(path), "/dev/ptp%d", devno);
|
|
ptpinfo("Registering %s\n", path);
|
|
ret = register_driver(path, &g_ptp_clock_file_ops, 0666, upper);
|
|
if (ret < 0)
|
|
{
|
|
nxmutex_destroy(&upper->lock);
|
|
kmm_free(upper);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ptp_clock_unregister
|
|
*
|
|
* Description:
|
|
* This function unregister character node and release all resource about
|
|
* upper half driver.
|
|
*
|
|
* Input Parameters:
|
|
* lower - A pointer to an instance of lower half ptp driver. This
|
|
* instance is bound to the ptp driver and must persists as long
|
|
* as the driver persists.
|
|
* devno - The user specifies which device of this type, from 0.
|
|
****************************************************************************/
|
|
|
|
void ptp_clock_unregister(FAR struct ptp_lowerhalf_s *lower, int devno)
|
|
{
|
|
FAR struct ptp_upperhalf_s *upper = lower->upper;
|
|
|
|
if (upper != NULL)
|
|
{
|
|
char path[16];
|
|
|
|
snprintf(path, sizeof(path), "/dev/ptp%d", devno);
|
|
unregister_driver(path);
|
|
nxmutex_destroy(&upper->lock);
|
|
kmm_free(upper);
|
|
}
|
|
}
|