testing/fftest: add Force feedback driver test
Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
This commit is contained in:
parent
2f5dc9c24d
commit
433ca0ff89
33
testing/fftest/CMakeLists.txt
Normal file
33
testing/fftest/CMakeLists.txt
Normal 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
24
testing/fftest/Kconfig
Normal 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
23
testing/fftest/Make.defs
Normal 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
30
testing/fftest/Makefile
Normal 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
447
testing/fftest/fftest.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user