Skip to content

Commit

Permalink
adding constant and custom
Browse files Browse the repository at this point in the history
upload before play
  • Loading branch information
b100dian authored and Thaodan committed Oct 9, 2021
1 parent 78602a5 commit 60a99e7
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/ngf/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ main (int argc, char *argv[])
AppData app;

memset (&app, 0, sizeof (app));
app.default_loglevel = N_LOG_LEVEL_ERROR;
app.default_loglevel = N_LOG_LEVEL_DEBUG;
app.use_default_loglevel = TRUE;
n_log_initialize (app.default_loglevel);

Expand Down
21 changes: 8 additions & 13 deletions src/plugins/ffmemless/ffmemless.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,9 @@ int ffmemless_erase_effect(int effect_id, int device_file)
}
}

int ffmemless_evdev_file_open(const char *file_name)
int ffmemless_evdev_file_open(const char *file_name, unsigned long features[4])
{
int result, fp;
unsigned long features[4];

fp = open(file_name, O_RDWR | O_CLOEXEC);

Expand All @@ -84,24 +83,15 @@ int ffmemless_evdev_file_open(const char *file_name)
close(fp);
return -1;
}
result = test_bit(FF_RUMBLE, features);
result = result && test_bit(FF_PERIODIC, features);
if (result) {
return fp;
} else {
close(fp);
errno = ENOTSUP;
return -1;
}
return fp;
}

int ffmemless_evdev_file_search(void)
int ffmemless_evdev_file_search(unsigned long features[4])
{
int result;
short i = 0;
int fp = 1;
char device_file_name[24];
unsigned long features[4];

/* fail safe stop at 256 devices */
while (fp && i < 256) {
Expand All @@ -119,6 +109,7 @@ int ffmemless_evdev_file_search(void)
continue;
}
result = test_bit(FF_RUMBLE, features);
result = result || test_bit(FF_CONSTANT, features);
result = result && test_bit(FF_PERIODIC, features);
if (result)
return fp;
Expand All @@ -133,3 +124,7 @@ int ffmemless_evdev_file_close(int file)
{
return close(file);
}

int ffmemless_has_feature(__u16 type, unsigned long features[4]) {
return test_bit(type, features);
}
13 changes: 8 additions & 5 deletions src/plugins/ffmemless/ffmemless.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,22 @@ int ffmemless_erase_effect(int effect_id, int device_file);
* of force feedback effects and returns the file pointer if it does.
*
* @param device_file_name A pointer to file name. E.g. "/dev/input/event2"
* @param features An array of 4 longs to be tested with FF_test_bit for features.
* @returns File descriptor on success, -1 on error.
*/
int ffmemless_evdev_file_open(const char *device_file_name);
int ffmemless_evdev_file_open(const char *device_file_name, unsigned long features[4]);

/**
* ffmemless_evdev_file_search - Search first device node with FF support.
*
* This function searches through /dev/input/event? files for first one that
* supports FF_RUMBLE and FF_PRERIODIC type of force feedback effects. Once
* a device node is found, the function returns a open file descriptor
* to the device node.
* supports either FF_RUMBLE or FF_CONSTANT but also FF_PRERIODIC type of force
* feedback effects. Once a device node is found, the function returns a open file
* descriptor to the device node.
*
* @returns File descriptor on success, -1 if no suitable device node found.
*/
int ffmemless_evdev_file_search(void);
int ffmemless_evdev_file_search(unsigned long features[4]);

/**
* ffmemless_evdev_file_close - Close a event device node.
Expand All @@ -60,4 +61,6 @@ int ffmemless_evdev_file_search(void);
*/
int ffmemless_evdev_file_close(int device_file);

int ffmemless_has_feature(__u16 type, unsigned long features[4]);

#endif
128 changes: 110 additions & 18 deletions src/plugins/ffmemless/plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,15 @@
#define FFM_HAPTIC_DURATION_KEY "haptic.duration"
#define FFM_MAX_PARAM_LEN 80

#define NGF_DEFAULT_TYPE FF_RUMBLE
#define NGF_DEFAULT_DURATION 240
#define NGF_DEFAULT_DELAY 0
// rumble
#define NGF_DEFAULT_RMAGNITUDE 27000
#define NGF_DEFAULT_PMAGNITUDE 14000
// constant
#define NGF_DEFAULT_LEVEL 0x5FFF

#define CACHE_EFFECTS

N_PLUGIN_NAME(FFM_PLUGIN_NAME)
N_PLUGIN_DESCRIPTION("Vibra plugin using ff-memless kernel backend")
Expand All @@ -58,13 +62,17 @@ struct ffm_effect_data {
int repeat;
guint playback_time;
int poll_id;
#ifdef CACHE_EFFECTS
struct ff_effect cached_effect;
#endif
};

static struct ffm_data {
int dev_file;
const NProplist *ngfd_props;
NProplist *sys_props;
GHashTable *effects;
unsigned long features[4];
} ffm;

static int ffm_setup_device(const NProplist *props, int *dev_fd)
Expand All @@ -74,17 +82,17 @@ static int ffm_setup_device(const NProplist *props, int *dev_fd)
if (device_file == NULL) {
N_DEBUG (LOG_CAT "No %s provided, using automatic detection",
FFM_DEVFILE_KEY);
*dev_fd = ffmemless_evdev_file_search();
*dev_fd = ffmemless_evdev_file_search(ffm.features);
} else {
N_DEBUG (LOG_CAT "%s found with value \"%s\"",
FFM_DEVFILE_KEY, device_file);
*dev_fd = ffmemless_evdev_file_open(device_file);
*dev_fd = ffmemless_evdev_file_open(device_file, ffm.features);
/* do a fall back to automatic search, in case open fails */
if (*dev_fd == -1) {
N_DEBUG (LOG_CAT "%s is not a valid event device",
device_file);
N_DEBUG (LOG_CAT "Falling back to automatic detection");
*dev_fd = ffmemless_evdev_file_search();
*dev_fd = ffmemless_evdev_file_search(ffm.features);
}
}
if (*dev_fd == -1) {
Expand Down Expand Up @@ -241,7 +249,7 @@ static int ffm_setup_default_effect(GHashTable *effects, int dev_fd)
data = (struct ffm_effect_data *)g_hash_table_lookup(effects,
N_HAPTIC_EFFECT_DEFAULT);
if (!data) {
data = g_new(struct ffm_effect_data, 1);
data = g_new0(struct ffm_effect_data, 1);
data->id = -1;
data->id = 1;
ff.id = -1;
Expand All @@ -251,15 +259,34 @@ static int ffm_setup_default_effect(GHashTable *effects, int dev_fd)
ff.id = data->id;
}

ff.type = FF_RUMBLE;
ff.replay.length = NGF_DEFAULT_DURATION;
ff.u.rumble.strong_magnitude = NGF_DEFAULT_RMAGNITUDE;
ff.u.rumble.weak_magnitude = NGF_DEFAULT_RMAGNITUDE;
N_DEBUG (LOG_CAT "Features array is %lx %lx %lx %lx", ffm.features[0],
ffm.features[1], ffm.features[2], ffm.features[3]);
// We test first for FF_CONSTANT because FF_RUMBLE may be falsely reported by
// input_ff_create if FF_PERIODIC is supported. However, there are some
// drivers which use FF_PERIODIC only with FF_CUSTOM waveform..
// https://github.com/torvalds/linux/blob/master/drivers/input/ff-core.c#L350
if (ffmemless_has_feature(FF_CONSTANT, ffm.features)) {
N_DEBUG (LOG_CAT "Using constant default effect, rumble is %d",
(ffmemless_has_feature(FF_RUMBLE, ffm.features)));
ff.type = FF_CONSTANT;
ff.replay.length = NGF_DEFAULT_DURATION;
ff.u.constant.level = NGF_DEFAULT_LEVEL;
} else {
N_DEBUG (LOG_CAT "Using rumble default effect");
ff.type = FF_RUMBLE;
ff.replay.length = NGF_DEFAULT_DURATION;
ff.u.rumble.strong_magnitude = NGF_DEFAULT_RMAGNITUDE;
ff.u.rumble.weak_magnitude = NGF_DEFAULT_RMAGNITUDE;
}
#ifdef CACHE_EFFECTS
memcpy(&data->cached_effect, &ff, sizeof(ff));
#endif
if (ffmemless_upload_effect(&ff, dev_fd)) {
N_DEBUG (LOG_CAT "%s effect load failed", N_HAPTIC_EFFECT_DEFAULT);
return -1;
}
data->id = ff.id;

N_DEBUG (LOG_CAT "Added effect %s, id %d", N_HAPTIC_EFFECT_DEFAULT, ff.id);
return 0;
}
Expand All @@ -274,6 +301,8 @@ static int ffm_setup_effects(const NProplist *props, GHashTable *effects)
char *key;
struct ff_effect ff;
struct ffm_effect_data *data;
// We're using just an int to map to the array below.
__s16 custom_data[sizeof(int)/sizeof(__s16)] = { 0 };
GHashTableIter iter;

if(!effects || !props) {
Expand Down Expand Up @@ -308,6 +337,8 @@ static int ffm_setup_effects(const NProplist *props, GHashTable *effects)
ff.type = FF_RUMBLE;
} else if (!strcmp(value, "periodic")) {
ff.type = FF_PERIODIC;
} else if (!strcmp(value, "constant")) {
ff.type = FF_CONSTANT;
} else {
N_WARNING (LOG_CAT "unknown effect type %s", value);
continue;
Expand Down Expand Up @@ -363,6 +394,30 @@ static int ffm_setup_effects(const NProplist *props, GHashTable *effects)
/* Usually no separate weak motor, use same value */
ff.u.rumble.weak_magnitude =
ff.u.rumble.strong_magnitude;
} else if (ff.type == FF_CONSTANT) {

N_DEBUG (LOG_CAT "constant effect");
ff.type = FF_CONSTANT;

int level = ffm_get_int_value(props,
key, "_LEVEL", INT32_MIN, INT32_MAX);
ff.u.constant.level = level == INT32_MIN ? 0 : (__s16)level;

ff.u.constant.envelope.attack_length = ffm_get_int_value(
props, key, "_ATTACK",
0, UINT16_MAX);

ff.u.constant.envelope.attack_level = ffm_get_int_value(
props, key, "_ALEVEL",
0, UINT16_MAX);

ff.u.constant.envelope.fade_length = ffm_get_int_value(
props, key, "_FADE",
0, UINT16_MAX);

ff.u.constant.envelope.fade_level = ffm_get_int_value(
props, key, "_FLEVEL",
0, UINT16_MAX);

} else if (ff.type == FF_PERIODIC) {
N_DEBUG (LOG_CAT "periodic effect");
Expand All @@ -374,9 +429,19 @@ static int ffm_setup_effects(const NProplist *props, GHashTable *effects)
ff.u.periodic.waveform = FF_SQUARE;
else if (!g_strcmp0(value, "triangle"))
ff.u.periodic.waveform = FF_TRIANGLE;
else if (!g_strcmp0(value, "custom"))
ff.u.periodic.waveform = FF_CUSTOM;
else
ff.u.periodic.waveform = FF_SINE;

if (ff.u.periodic.waveform == FF_CUSTOM) {
int* custom = (void*)custom_data;
*custom = ffm_get_int_value(props,
key, "_CUSTOM", 0, UINT16_MAX);
ff.u.periodic.custom_data = custom_data;
ff.u.periodic.custom_len = sizeof(int)/sizeof(__s16);
}

ff.u.periodic.period = ffm_get_int_value(props,
key, "_PERIOD", 0, UINT16_MAX);

Expand Down Expand Up @@ -416,6 +481,9 @@ static int ffm_setup_effects(const NProplist *props, GHashTable *effects)
continue;
}

#ifdef CACHE_EFFECTS
memcpy(&data->cached_effect, &ff, sizeof(ff));
#endif
/* Finally load the effect */
if (ffmemless_upload_effect(&ff, ffm.dev_file)) {
N_DEBUG (LOG_CAT "%s effect loading failed",
Expand All @@ -434,6 +502,7 @@ static int ffm_setup_effects(const NProplist *props, GHashTable *effects)
"delay = %dms\n"
"strong rumble magn = 0x%x\n"
"weak rumble magn = 0x%x\n"
"const level = 0x%d\n"
"per_waveform = 0x%x\n"
"period = %dms\n"
"periodic magnitude = 0x%x\n"
Expand All @@ -443,11 +512,13 @@ static int ffm_setup_effects(const NProplist *props, GHashTable *effects)
"att_lev = 0x%x\n"
"fade = %ums\n"
"fade_lev = 0x%x\n",
"custom_len = %d\n",
ff.type,
ff.replay.length,
ff.replay.delay,
ff.u.rumble.strong_magnitude,
ff.u.rumble.weak_magnitude,
ff.u.constant.level,
ff.u.periodic.waveform,
ff.u.periodic.period,
ff.u.periodic.magnitude,
Expand All @@ -456,11 +527,11 @@ static int ffm_setup_effects(const NProplist *props, GHashTable *effects)
ff.u.periodic.envelope.attack_length,
ff.u.periodic.envelope.attack_level,
ff.u.periodic.envelope.fade_length,
ff.u.periodic.envelope.fade_level);
ff.u.periodic.envelope.fade_level,
ff.u.periodic.custom_len);
}

return 0;

ffm_eff_error1:
g_hash_table_destroy(ffm.effects);
return -1;
Expand Down Expand Up @@ -493,10 +564,35 @@ static int ffm_play(struct ffm_effect_data *data, int play)
N_DEBUG (LOG_CAT "Stopping playback");
}

#ifdef CACHE_EFFECTS
int result = TRUE;

if (play) {
data->cached_effect.id = -1;
if (ffmemless_upload_effect(&data->cached_effect, ffm.dev_file)) {
N_DEBUG (LOG_CAT "%d effect re-load failed", data->id);
result = FALSE;
} else {
data->id = data->cached_effect.id;
}
}

if (ffmemless_play(data->id, ffm.dev_file, play))
return FALSE;
result = FALSE;

if (!play) {
if (ffmemless_erase_effect(data->cached_effect.id, ffm.dev_file))
result = FALSE;
}

return result;

#else

if (ffmemless_play(data->id, ffm.dev_file, play))
return FALSE;
return TRUE;
#endif
}

static int ffm_sink_initialize(NSinkInterface *iface)
Expand Down Expand Up @@ -570,11 +666,7 @@ static int ffm_sink_prepare(NSinkInterface *iface, NRequest *request)

/* creating copy of the data as we need to alter it for this event */
copy = g_new(struct ffm_effect_data, 1);
copy->id = data->id;
copy->repeat = data->repeat;
copy->iface = iface;
copy->request = request;
copy->playback_time = data->playback_time;
memcpy(copy, data, sizeof(struct ffm_effect_data));

repeat = n_proplist_get_bool (props, FFM_SOUND_REPEAT_KEY);
playback_time = n_proplist_get_uint (props, FFM_HAPTIC_DURATION_KEY);
Expand Down Expand Up @@ -666,7 +758,7 @@ N_PLUGIN_LOAD(plugin)
};

/* Checking if there is a device, no point in loading plugin if not..*/
device_fd = ffmemless_evdev_file_search();
device_fd = ffmemless_evdev_file_search(ffm.features);
if (device_fd < 0) {
N_DEBUG (LOG_CAT "No force feedback device, stopping plugin");
return FALSE;
Expand Down

0 comments on commit 60a99e7

Please sign in to comment.