testing/fftest: add Force feedback driver test

Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
This commit is contained in:
dongjiuzhu1 2024-09-08 22:21:29 +08:00 committed by Xiang Xiao
parent 2f5dc9c24d
commit 433ca0ff89
5 changed files with 557 additions and 0 deletions

View File

@ -0,0 +1,33 @@
# ##############################################################################
# apps/testing/fftest/CMakeLists.txt
#
# 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.
#
# ##############################################################################
if(CONFIG_TESTING_FF)
nuttx_add_application(
NAME
${CONFIG_TESTING_FF_PROGNAME}
PRIORITY
${CONFIG_TESTING_FF_PRIORITY}
STACKSIZE
${CONFIG_TESTING_FF_STACKSIZE}
MODULE
${CONFIG_TESTING_FF}
SRCS
fftest.c)
endif()

24
testing/fftest/Kconfig Normal file
View File

@ -0,0 +1,24 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
config TESTING_FF
bool "Force feedback test"
default n
if TESTING_FF
config TESTING_FF_PROGNAME
string "Program name"
default "fftest"
config TESTING_FF_PRIORITY
int "FF driver test task priority"
default 100
config TESTING_FF_STACKSIZE
int "FF driver test stack size"
default DEFAULT_TASK_STACKSIZE
endif

23
testing/fftest/Make.defs Normal file
View File

@ -0,0 +1,23 @@
############################################################################
# apps/testing/fftest/Make.defs
#
# 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.
#
############################################################################
ifneq ($(CONFIG_TESTING_FF),)
CONFIGURED_APPS += $(APPDIR)/testing/fftest
endif

30
testing/fftest/Makefile Normal file
View File

@ -0,0 +1,30 @@
############################################################################
# apps/testing/fftest/Makefile
#
# 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.
#
############################################################################
include $(APPDIR)/Make.defs
PROGNAME = $(CONFIG_TESTING_FF_PROGNAME)
PRIORITY = $(CONFIG_TESTING_FF_PRIORITY)
STACKSIZE = $(CONFIG_TESTING_FF_STACKSIZE)
MODULE = $(CONFIG_TESTING_FF)
MAINSRC = fftest.c
include $(APPDIR)/Application.mk

447
testing/fftest/fftest.c Normal file
View File

@ -0,0 +1,447 @@
/****************************************************************************
* apps/testing/fftest/fftest.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 <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <nuttx/bits.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/input/ff.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define N_EFFECTS 6
/****************************************************************************
* Private Types
****************************************************************************/
FAR static const char *g_effect_names[] =
{
"Sine vibration",
"Constant Force",
"Spring Condition",
"Damping Condition",
"Strong Rumble",
"Weak Rumble"
};
/****************************************************************************
* Private Functions
****************************************************************************/
int main(int argc, FAR char **argv)
{
unsigned char fffeatures[1 + FF_MAX / 8 / sizeof(unsigned char)];
FAR const char *device_file_name = "/dev/input_ff0";
struct ff_effect effects[N_EFFECTS];
struct ff_event_s play;
struct ff_event_s stop;
struct ff_event_s gain;
int neffects; /* Number of effects the device can play at the same time */
int fd;
int i;
printf("Force feedback test program.\n");
for (i = 1; i < argc; i++)
{
if (strncmp(argv[i], "--help", 8) == 0)
{
printf("Usage: %s /dev/input_ffX\n", argv[0]);
printf("Tests the force feedback driver\n");
return -EINVAL;
}
else
{
device_file_name = argv[i];
}
}
fd = open(device_file_name, O_WRONLY);
if (fd < 0)
{
perror("Open device file");
return -errno;
}
printf("Device %s opened\n", device_file_name);
/* Query device */
printf("features:\n");
/* Force feedback effects */
memset(fffeatures, 0, sizeof(fffeatures));
if (ioctl(fd, EVIOCGBIT, fffeatures) < 0)
{
perror("Ioctl force feedback features query");
goto out;
}
printf(" * Force feedback effects types: ");
if (test_bit(FF_CONSTANT, fffeatures))
{
printf("Constant, ");
}
if (test_bit(FF_PERIODIC, fffeatures))
{
printf("Periodic, ");
}
if (test_bit(FF_RAMP, fffeatures))
{
printf("Ramp, ");
}
if (test_bit(FF_SPRING, fffeatures))
{
printf("Spring, ");
}
if (test_bit(FF_FRICTION, fffeatures))
{
printf("Friction, ");
}
if (test_bit(FF_DAMPER, fffeatures))
{
printf("Damper, ");
}
if (test_bit(FF_RUMBLE, fffeatures))
{
printf("Rumble, ");
}
if (test_bit(FF_INERTIA, fffeatures))
{
printf("Inertia, ");
}
if (test_bit(FF_GAIN, fffeatures))
{
printf("Gain, ");
}
if (test_bit(FF_AUTOCENTER, fffeatures))
{
printf("Autocenter, ");
}
printf("\n Force feedback periodic effects: ");
if (test_bit(FF_SQUARE, fffeatures))
{
printf("Square, ");
}
if (test_bit(FF_TRIANGLE, fffeatures))
{
printf("Triangle, ");
}
if (test_bit(FF_SINE, fffeatures))
{
printf("Sine, ");
}
if (test_bit(FF_SAW_UP, fffeatures))
{
printf("Saw up, ");
}
if (test_bit(FF_SAW_DOWN, fffeatures))
{
printf("Saw down, ");
}
if (test_bit(FF_CUSTOM, fffeatures))
{
printf("Custom, ");
}
printf("\n [");
for (i = 0; i < sizeof(fffeatures) / sizeof(unsigned char); i++)
{
printf("%02X ", fffeatures[i]);
}
printf("]\n");
printf(" * Number of simultaneous effects: ");
if (ioctl(fd, EVIOCGEFFECTS, &neffects) < 0)
{
perror("Ioctl number of effects");
goto out;
}
printf("%d\n\n", neffects);
/* Set master gain to 75% if supported */
if (test_bit(FF_GAIN, fffeatures))
{
memset(&gain, 0, sizeof(gain));
gain.code = FF_GAIN;
gain.value = 0xc000; /* [0, 0xFFFF] */
printf("Setting master gain to 75%% ... ");
fflush(stdout);
if (write(fd, &gain, sizeof(gain)) != sizeof(gain))
{
perror("Error: write fd failed");
goto out;
}
else
{
printf("OK\n");
}
}
/* Download a periodic sinusoidal effect */
memset(&effects[0], 0, sizeof(effects[0]));
effects[0].type = FF_PERIODIC;
effects[0].id = -1;
effects[0].u.periodic.waveform = FF_SINE;
effects[0].u.periodic.period = 100; /* 0.1 second */
effects[0].u.periodic.magnitude = 0x7fff;
effects[0].u.periodic.offset = 0;
effects[0].u.periodic.phase = 0;
effects[0].direction = 0x4000; /* Along X axis */
effects[0].u.periodic.envelope.attack_length = 1000;
effects[0].u.periodic.envelope.attack_level = 0x7fff;
effects[0].u.periodic.envelope.fade_length = 1000;
effects[0].u.periodic.envelope.fade_level = 0x7fff;
effects[0].trigger.button = 0;
effects[0].trigger.interval = 0;
effects[0].replay.length = 20000; /* 20 seconds */
effects[0].replay.delay = 1000;
printf("Uploading effect #0 (Periodic sinusoidal) ... ");
fflush(stdout);
if (ioctl(fd, EVIOCSFF, &effects[0]) < 0)
{
perror("Error: ioctl failed");
goto out;
}
else
{
printf("OK (id %d)\n", effects[0].id);
}
/* Download a constant effect */
effects[1].type = FF_CONSTANT;
effects[1].id = -1;
effects[1].u.constant.level = 0x2000; /* Strength : 25 % */
effects[1].direction = 0x6000;
effects[1].u.constant.envelope.attack_length = 1000;
effects[1].u.constant.envelope.attack_level = 0x1000;
effects[1].u.constant.envelope.fade_length = 1000;
effects[1].u.constant.envelope.fade_level = 0x1000;
effects[1].trigger.button = 0;
effects[1].trigger.interval = 0;
effects[1].replay.length = 20000; /* 20 seconds */
effects[1].replay.delay = 0;
printf("Uploading effect #1 (Constant) ... ");
fflush(stdout);
if (ioctl(fd, EVIOCSFF, &effects[1]) < 0)
{
perror("Error: ioctl failed");
goto out;
}
else
{
printf("OK (id %d)\n", effects[1].id);
}
/* Download a condition spring effect */
effects[2].type = FF_SPRING;
effects[2].id = -1;
effects[2].u.condition[0].right_saturation = 0x7fff;
effects[2].u.condition[0].left_saturation = 0x7fff;
effects[2].u.condition[0].right_coeff = 0x2000;
effects[2].u.condition[0].left_coeff = 0x2000;
effects[2].u.condition[0].deadband = 0x0;
effects[2].u.condition[0].center = 0x0;
effects[2].u.condition[1] = effects[2].u.condition[0];
effects[2].trigger.button = 0;
effects[2].trigger.interval = 0;
effects[2].replay.length = 20000; /* 20 seconds */
effects[2].replay.delay = 0;
printf("Uploading effect #2 (Spring) ... ");
fflush(stdout);
if (ioctl(fd, EVIOCSFF, &effects[2]) < 0)
{
perror("Error: ioctl failed");
goto out;
}
else
{
printf("OK (id %d)\n", effects[2].id);
}
/* Download a condition damper effect */
effects[3].type = FF_DAMPER;
effects[3].id = -1;
effects[3].u.condition[0].right_saturation = 0x7fff;
effects[3].u.condition[0].left_saturation = 0x7fff;
effects[3].u.condition[0].right_coeff = 0x2000;
effects[3].u.condition[0].left_coeff = 0x2000;
effects[3].u.condition[0].deadband = 0x0;
effects[3].u.condition[0].center = 0x0;
effects[3].u.condition[1] = effects[3].u.condition[0];
effects[3].trigger.button = 0;
effects[3].trigger.interval = 0;
effects[3].replay.length = 20000; /* 20 seconds */
effects[3].replay.delay = 0;
printf("Uploading effect #3 (Damper) ... ");
fflush(stdout);
if (ioctl(fd, EVIOCSFF, &effects[3]) < 0)
{
perror("Error: ioctl failed");
goto out;
}
else
{
printf("OK (id %d)\n", effects[3].id);
}
/* A strong rumbling effect */
effects[4].type = FF_RUMBLE;
effects[4].id = -1;
effects[4].u.rumble.strong_magnitude = 0x8000;
effects[4].u.rumble.weak_magnitude = 0;
effects[4].replay.length = 5000;
effects[4].replay.delay = 1000;
printf("Uploading effect #4 (Strong rumble, with heavy motor) ... ");
fflush(stdout);
if (ioctl(fd, EVIOCSFF, &effects[4]) < 0)
{
perror("Error: ioctl failed");
goto out;
}
else
{
printf("OK (id %d)\n", effects[4].id);
}
/* A weak rumbling effect */
effects[5].type = FF_RUMBLE;
effects[5].id = -1;
effects[5].u.rumble.strong_magnitude = 0;
effects[5].u.rumble.weak_magnitude = 0xc000;
effects[5].replay.length = 5000;
effects[5].replay.delay = 0;
printf("Uploading effect #5 (Weak rumble, with light motor) ... ");
fflush(stdout);
if (ioctl(fd, EVIOCSFF, &effects[5]) < 0)
{
perror("Error: ioctl failed");
goto out;
}
else
{
printf("OK (id %d)\n", effects[5].id);
}
/* Ask user what effects to play */
do
{
printf("Enter effect number, -1 to exit\n");
i = -1;
if (scanf("%d", &i) == EOF)
{
printf("Read error\n");
}
else if (i >= 0 && i < N_EFFECTS)
{
memset(&play, 0, sizeof(play));
play.code = effects[i].id;
play.value = 1;
if (write(fd, &play, sizeof(play)) < 0)
{
perror("Play effect failed");
goto out;
}
printf("Now Playing: %s\n", g_effect_names[i]);
}
else if (i == -2)
{
/* Crash test */
i = *((FAR int *)0);
printf("Crash test: %d\n", i);
}
else if (i != -1)
{
printf("No such effect\n");
}
}
while (i >= 0);
/* Stop the effects */
printf("Stopping effects\n");
for (i = 0; i < N_EFFECTS; i++)
{
memset(&stop, 0, sizeof(stop));
stop.code = effects[i].id;
stop.value = 0;
if (write(fd, &stop, sizeof(stop)) < 0)
{
perror("Error write fd failed");
goto out;
}
}
out:
close(fd);
return errno > 0 ? -errno : 0;
}