drivers/capture: add fake capture driver for testing and development

This commit introduces a software-based fake capture driver that simulates
a 10Hz square wave with 50% duty cycle, enabling development and testing
of capture-related applications without requiring real hardware.

Background:
The capture driver subsystem requires hardware support (timers with input
capture functionality) to measure external signal frequency and duty cycle.
During development, testing, or on platforms without capture hardware, it's
difficult to develop and test applications that use the capture API.

Problem:
Without a fake/simulator driver:
- Developers cannot test capture applications without specific hardware
- Continuous Integration (CI) systems cannot run capture-related tests
- Applications cannot be developed on platforms lacking capture hardware
- Testing edge notification features requires complex hardware setup
- Difficult to reproduce specific timing scenarios for debugging

Solution:
This commit provides a software-simulated capture driver that generates
predictable capture events using a watchdog timer. The fake driver
produces a square wave signal at 10Hz frequency with 50% duty cycle,
allowing applications to test the full capture API without hardware.

Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
This commit is contained in:
dongjiuzhu1
2025-10-24 11:45:56 +08:00
committed by Alan C. Assis
parent f6a9f8239d
commit 57217a7983
6 changed files with 396 additions and 0 deletions
+5
View File
@@ -51,6 +51,7 @@
#include <nuttx/syslog/syslog_console.h>
#include <nuttx/thermal.h>
#include <nuttx/timers/ptp_clock_dummy.h>
#include <nuttx/timers/capture.h>
#include <nuttx/trace.h>
#include <nuttx/usrsock/usrsock_rpmsg.h>
#include <nuttx/vhost/vhost.h>
@@ -298,5 +299,9 @@ void drivers_initialize(void)
ptp_clock_dummy_initialize(0);
#endif
#ifdef CONFIG_FAKE_CAPTURE
fake_capture_initialize(2);
#endif
drivers_trace_end();
}
+4
View File
@@ -84,6 +84,10 @@ endif()
if(CONFIG_CAPTURE)
list(APPEND SRCS capture.c)
if(CONFIG_FAKE_CAPTURE)
list(APPEND SRCS fake_capture.c)
endif()
endif()
if(CONFIG_GOLDFISH_TIMER)
+7
View File
@@ -102,6 +102,13 @@ config CAPTURE_NOTIFY
occurs. If the hardware will support notification, then this
configuration should be set to enable the capability.
config FAKE_CAPTURE
bool "Fake Capture Driver"
default n
depends on CAPTURE_NSIGNALS > 0
---help---
Enables a fake capture driver for testing purposes.
endif # CAPTURE
config TIMER
+5
View File
@@ -114,6 +114,11 @@ endif
ifeq ($(CONFIG_CAPTURE),y)
CSRCS += capture.c
ifeq ($(CONFIG_FAKE_CAPTURE),y)
CSRCS += fake_capture.c
endif
TMRDEPPATH = --dep-path timers
TMRVPATH = :timers
endif
+352
View File
@@ -0,0 +1,352 @@
/****************************************************************************
* drivers/timers/fake_capture.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 <nuttx/config.h>
#include <nuttx/kmalloc.h>
#include <nuttx/spinlock.h>
#include <nuttx/wdog.h>
#include <nuttx/timers/capture.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <debug.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define CAPTURE_DUTY_DEFAULT 50
#define CAPTURE_FREQ_DEFAULT 10
/****************************************************************************
* Private Types
****************************************************************************/
struct fake_capture_s
{
struct cap_lowerhalf_s lower; /* Lower half capture driver */
spinlock_t lock; /* For mutually exclusive access */
struct wdog_s wdog; /* For timing capture events */
bool high; /* Current edge state */
capture_notify_t cb; /* Capture event callback function pointer */
FAR void *priv; /* Pointer to private data */
enum cap_type_e type; /* Edge type for the capture channel */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int fake_capture_start(FAR struct cap_lowerhalf_s *lower);
static int fake_capture_stop(FAR struct cap_lowerhalf_s *lower);
static int fake_capture_getduty(FAR struct cap_lowerhalf_s *lower,
FAR uint8_t *duty);
static int fake_capture_getfreq(FAR struct cap_lowerhalf_s *lower,
FAR uint32_t *freq);
static int fake_capture_getedges(FAR struct cap_lowerhalf_s *lower,
FAR uint32_t *edges);
static int fake_capture_bind(FAR struct cap_lowerhalf_s *lower,
enum cap_type_e type, capture_notify_t cb,
FAR void *priv);
static int fake_capture_unbind(FAR struct cap_lowerhalf_s *lower);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct cap_ops_s g_fake_cap_ops =
{
.start = fake_capture_start,
.stop = fake_capture_stop,
.getduty = fake_capture_getduty,
.getfreq = fake_capture_getfreq,
.getedges = fake_capture_getedges,
.bind = fake_capture_bind,
.unbind = fake_capture_unbind,
};
/****************************************************************************
* Private Functions
****************************************************************************/
static void fake_capture_isr(wdparm_t arg)
{
FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)arg;
irqstate_t flags;
bool last;
flags = spin_lock_irqsave(&capture->lock);
last = capture->high;
capture->high = !capture->high;
if (capture->cb != NULL &&
(capture->type == CAP_TYPE_BOTH ||
(capture->type == CAP_TYPE_RISING && !last) ||
(capture->type == CAP_TYPE_FALLING && last)))
{
capture->cb(&capture->lower, capture->priv);
}
spin_unlock_irqrestore(&capture->lock, flags);
wd_start_next(&capture->wdog, MSEC2TICK(1000 / CAPTURE_FREQ_DEFAULT),
fake_capture_isr, (wdparm_t)capture);
}
/****************************************************************************
* Name: fake_capture_start
*
* Description:
* This function is a requirement of the upper-half driver. When called,
* enables the capture channel, interruption routine, sets the positive
* edge to trigger this interrupt and resets the frequency and duty
* values. The positive edge is always the first expected.
*
* Input Parameters:
* lower - Pointer to the capture channel lower-half data structure.
*
* Returned Value:
* Returns OK on success.
*
****************************************************************************/
static int fake_capture_start(FAR struct cap_lowerhalf_s *lower)
{
FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)lower;
wd_start(&capture->wdog, MSEC2TICK(1000 / CAPTURE_FREQ_DEFAULT),
fake_capture_isr, (wdparm_t)capture);
return OK;
}
/****************************************************************************
* Name: fake_capture_stop
*
* Description:
* This function is a requirement of the upper-half driver. When called,
* disables the capture channel and the interrupt routine associated.
*
* Input Parameters:
* lower - Pointer to the capture channel lower-half data structure.
*
* Returned Value:
* Returns OK on success.
*
****************************************************************************/
static int fake_capture_stop(FAR struct cap_lowerhalf_s *lower)
{
FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)lower;
wd_cancel(&capture->wdog);
return OK;
}
/****************************************************************************
* Name: fake_capture_getduty
*
* Description:
* This function is a requirement of the upper-half driver. Returns
* the last calculated duty cycle value.
*
* Input Parameters:
* lower - Pointer to the capture channel lower-half data structure.
* duty - uint8_t pointer where the duty cycle value is written.
*
* Returned Value:
* Returns OK on success.
*
****************************************************************************/
static int fake_capture_getduty(FAR struct cap_lowerhalf_s *lower,
FAR uint8_t *duty)
{
*duty = CAPTURE_DUTY_DEFAULT;
return OK;
}
/****************************************************************************
* Name: fake_capture_getfreq
*
* Description:
* This function is a requirement of the upper-half driver. Returns
* the last calculated frequency value.
*
* Input Parameters:
* lower - Pointer to the capture channel lower-half data structure.
* duty - uint8_t pointer where the frequency value is written.
*
* Returned Value:
* Returns OK on success.
*
****************************************************************************/
static int fake_capture_getfreq(FAR struct cap_lowerhalf_s *lower,
FAR uint32_t *freq)
{
*freq = CAPTURE_FREQ_DEFAULT;
return OK;
}
/****************************************************************************
* Name: fake_capture_getedges
*
* Description:
* This function is a requirement of the upper-half driver. Returns
* the current edge type configured for the capture channel.
*
* Input Parameters:
* lower - Pointer to the capture channel lower-half data structure.
* type - Pointer to the edge type variable to be updated.
*
* Returned Value:
* Returns OK on success.
*
****************************************************************************/
static int fake_capture_getedges(FAR struct cap_lowerhalf_s *lower,
FAR uint32_t *edges)
{
FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)lower;
irqstate_t flags = spin_lock_irqsave(&capture->lock);
*edges = capture->high;
spin_unlock_irqrestore(&capture->lock, flags);
return OK;
}
/****************************************************************************
* Name: fake_capture_bind
*
* Description:
* This function is used to bind a upper-half provided callback that will
* be invoked upon capture edge events.
*
* Input Parameters:
* lower - Pointer to the capture channel lower-half data structure.
* cb - The callback function pointer.
* priv - The private argument to be passed to the callback function.
* edge - The edge type that will trigger the callback.
*
* Returned Value:
* Returns OK on success.
*
****************************************************************************/
static int fake_capture_bind(FAR struct cap_lowerhalf_s *lower,
enum cap_type_e type, capture_notify_t cb,
FAR void *priv)
{
FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)lower;
irqstate_t flags = spin_lock_irqsave(&capture->lock);
capture->cb = cb;
capture->priv = priv;
capture->type = type;
spin_unlock_irqrestore(&capture->lock, flags);
return 0;
}
/****************************************************************************
* Name: fake_capture_unbind
*
* Description:
* This function is used to un-bind a previously bound upper-half provided
* callback that was to be invoked upon capture edge events.
*
* Input Parameters:
* lower - Pointer to the capture channel lower-half data structure.
*
* Returned Value:
* Returns OK on success.
*
****************************************************************************/
static int fake_capture_unbind(FAR struct cap_lowerhalf_s *lower)
{
FAR struct fake_capture_s *capture = (FAR struct fake_capture_s *)lower;
irqstate_t flags = spin_lock_irqsave(&capture->lock);
capture->cb = NULL;
capture->priv = NULL;
spin_unlock_irqrestore(&capture->lock, flags);
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: fake_capture_initialize
*
* Description:
* This function is called by board-specific logic to initialize
* fake capture.
*
* Input Parameters:
* channel - The capture channel number to initialize.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure. The following
* possible error values may be returned (most are returned by
* register_driver()):
*
****************************************************************************/
int fake_capture_initialize(int channels)
{
char path[32];
int ret = 0;
int i;
for (i = 0; i < channels; i++)
{
FAR struct fake_capture_s *priv;
priv = kmm_zalloc(sizeof(*priv));
DEBUGASSERT(priv);
spin_lock_init(&priv->lock);
priv->lower.ops = &g_fake_cap_ops;
snprintf(path, sizeof(path), "/dev/fake_capture%d", i);
ret = cap_register(path, &priv->lower);
if (ret < 0)
{
cperr("Failed to register capture:%d\n", ret);
kmm_free(priv);
break;
}
}
return ret;
}
+23
View File
@@ -31,6 +31,8 @@
#include <nuttx/fs/ioctl.h>
#include <nuttx/signal.h>
#include <nuttx/signal.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -274,6 +276,27 @@ int cap_register(FAR const char *devpath,
int cap_register_multiple(FAR const char *devpath,
FAR struct cap_lowerhalf_s **lower, int n);
/****************************************************************************
* Name: fake_capture_initialize
*
* Description:
* This function is called by board-specific logic to initialize
* fake capture.
*
* Input Parameters:
* channel - The capture channel number to initialize.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure. The following
* possible error values may be returned (most are returned by
* register_driver()):
*
****************************************************************************/
#ifdef CONFIG_FAKE_CAPTURE
int fake_capture_initialize(int channels);
#endif
#undef EXTERN
#ifdef __cplusplus
}