433ca0ff89
Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
448 lines
11 KiB
C
448 lines
11 KiB
C
/****************************************************************************
|
|
* 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;
|
|
}
|