From f65491ba44615072ffb6d9bebf063eba3c5bd088 Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Wed, 22 Nov 2023 16:11:57 +0800 Subject: [PATCH] input/ff: support force feedback driver framework Add new driver frameworks: force feedback for vibrator, like linux, unify vendor vibrator driver using. Incorporating a force feedback-based driving framework to replace the conventional driver/motor setup for controlling vibrators not only enhances the precision and responsiveness of the haptic feedback but also aligns better with the advancements in Linux-based driver systems. refs docs link: https://www.kernel.org/doc/html/v4.19/input/ff.html Signed-off-by: dongjiuzhu1 --- drivers/input/CMakeLists.txt | 4 + drivers/input/Kconfig | 7 + drivers/input/Make.defs | 4 + drivers/input/ff_upper.c | 548 +++++++++++++++++++++++++++++++++++ include/nuttx/fs/ioctl.h | 8 + include/nuttx/input/ff.h | 490 +++++++++++++++++++++++++++++++ 6 files changed, 1061 insertions(+) create mode 100644 drivers/input/ff_upper.c create mode 100644 include/nuttx/input/ff.h diff --git a/drivers/input/CMakeLists.txt b/drivers/input/CMakeLists.txt index dc079de976..ade587aa55 100644 --- a/drivers/input/CMakeLists.txt +++ b/drivers/input/CMakeLists.txt @@ -27,6 +27,10 @@ if(CONFIG_INPUT) list(APPEND SRCS touchscreen_upper.c) endif() + if(CONFIG_INPUT_FF) + list(APPEND SRCS ff_upper.c) + endif() + if(CONFIG_INPUT_MOUSE) list(APPEND SRCS mouse_upper.c) endif() diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index b3a15ca5c5..81667f13e7 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,6 +25,13 @@ config INPUT_GOLDFISH_NBUFFER depends on INPUT_GOLDFISH_EVENTS default 8 +config INPUT_FF + bool "Support Force Feedback device" + default n + ---help--- + Enable support for force feedback devices. + The doc link: https://www.kernel.org/doc/html/v4.19/input/ff.html. + config INPUT_MOUSE bool default n diff --git a/drivers/input/Make.defs b/drivers/input/Make.defs index e1b568f604..7be5294633 100644 --- a/drivers/input/Make.defs +++ b/drivers/input/Make.defs @@ -28,6 +28,10 @@ ifeq ($(CONFIG_INPUT_TOUCHSCREEN),y) CSRCS += touchscreen_upper.c endif +ifeq ($(CONFIG_INPUT_FF),y) + CSRCS += ff_upper.c +endif + ifeq ($(CONFIG_INPUT_MOUSE),y) CSRCS += mouse_upper.c endif diff --git a/drivers/input/ff_upper.c b/drivers/input/ff_upper.c new file mode 100644 index 0000000000..c9b8b9dcdc --- /dev/null +++ b/drivers/input/ff_upper.c @@ -0,0 +1,548 @@ +/**************************************************************************** + * drivers/input/ff_upper.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 + +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ff_effect_s +{ + struct ff_effect data; + FAR struct file *owner; +}; + +/* This structure is for force feedback device upper half driver */ + +struct ff_upperhalf_s +{ + /* A pointer of lower half instance */ + + FAR struct ff_lowerhalf_s *lower; + + /* Manages exclusive access to this structure */ + + mutex_t lock; + + /* Maximum number of effects supported by device. */ + + int max_effects; + + /* The pointer to an array of effects context currently loaded into device. + * when file handle owning an effect gets closed the effect is + * automatically erased. + */ + + FAR struct ff_effect_s *effects; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int ff_open(FAR struct file *filep); +static int ff_close(FAR struct file *filep); +static ssize_t ff_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int ff_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_ff_fops = +{ + ff_open, /* open */ + ff_close, /* close */ + NULL, /* read */ + ff_write, /* write */ + NULL, /* seek */ + ff_ioctl, /* ioctl */ + NULL, /* mmap */ + NULL, /* truncate */ + NULL /* poll */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ff_check_effect_access + * + * Description: + * Check that the effect_id is a valid effect and whether the user + * is the owner. + * + ****************************************************************************/ + +static int ff_check_effect_access(FAR struct ff_upperhalf_s *upper, + int effect_id, FAR struct file * filep) +{ + if (effect_id < 0 || effect_id >= upper->max_effects || + upper->effects[effect_id].owner == NULL) + { + return -EINVAL; + } + + if (filep != NULL && upper->effects[effect_id].owner != filep) + { + return -EACCES; + } + + return 0; +} + +/**************************************************************************** + * Name: ff_check_effects_compatible + * + * Description: + * Checks whether 2 effects can be combined together. + * + ****************************************************************************/ + +static inline bool ff_check_effects_compatible(FAR struct ff_effect *e1, + FAR struct ff_effect *e2) +{ + return e1->type == e2->type && + (e1->type != FF_PERIODIC || + e1->u.periodic.waveform == e2->u.periodic.waveform); +} + +/**************************************************************************** + * Name: ff_compat_effect + * + * Description: + * Convert an effect into compatible one. + * + ****************************************************************************/ + +static int ff_compat_effect(FAR struct ff_lowerhalf_s *lower, + FAR struct ff_effect *effect) +{ + switch (effect->type) + { + case FF_RUMBLE: + { + int magnitude; + + if (!test_bit(FF_PERIODIC, lower->ffbit)) + { + return -EINVAL; + } + + /* Calculate magnitude of sine wave as average of rumble's + * 2/3 of strong magnitude and 1/3 of weak magnitude + */ + + magnitude = effect->u.rumble.strong_magnitude / 3 + + effect->u.rumble.weak_magnitude / 6; + + effect->type = FF_PERIODIC; + effect->u.periodic.waveform = FF_SINE; + effect->u.periodic.period = 50; + effect->u.periodic.magnitude = magnitude; + effect->u.periodic.offset = 0; + effect->u.periodic.phase = 0; + effect->u.periodic.envelope.attack_length = 0; + effect->u.periodic.envelope.attack_level = 0; + effect->u.periodic.envelope.fade_length = 0; + effect->u.periodic.envelope.fade_level = 0; + + return 0; + } + + default: + return 0; + } +} + +/**************************************************************************** + * Name: ff_erase_effect + * + * Description: + * Erases the effect if the requester is also the effect owner. The mutex + * should already be locked before calling this function. + * + ****************************************************************************/ + +static int ff_erase_effect(FAR struct ff_upperhalf_s *upper, int effect_id, + FAR struct file *filep) +{ + FAR struct ff_lowerhalf_s *lower = upper->lower; + irqstate_t flags; + int ret; + + ret = ff_check_effect_access(upper, effect_id, filep); + if (ret < 0) + { + return ret; + } + + flags = enter_critical_section(); + lower->playback(lower, effect_id, 0); + leave_critical_section(flags); + + upper->effects[effect_id].owner = NULL; + if (lower->erase != NULL) + { + ret = lower->erase(lower, effect_id); + if (ret < 0) + { + upper->effects[effect_id].owner = filep; + } + } + + return ret; +} + +/**************************************************************************** + * Name: ff_upload + * + * Description: + * Upload effect into force-feedback device. + * + ****************************************************************************/ + +static int ff_upload(FAR struct ff_upperhalf_s *upper, + FAR struct ff_effect *effect, FAR struct file *filep) +{ + FAR struct ff_lowerhalf_s *lower = upper->lower; + FAR struct ff_effect *old; + int ret = 0; + int id; + + if (effect->type > FF_EFFECT_MAX) + { + return -EINVAL; + } + + if (effect->type == FF_PERIODIC && + (effect->u.periodic.waveform < FF_WAVEFORM_MIN || + effect->u.periodic.waveform > FF_WAVEFORM_MAX || + !test_bit(effect->u.periodic.waveform, lower->ffbit))) + { + return -EINVAL; + } + + if (!test_bit(effect->type, lower->ffbit)) + { + ret = ff_compat_effect(lower, effect); + if (ret < 0) + { + return ret; + } + } + + if (effect->id == -1) + { + for (id = 0; id < upper->max_effects; id++) + { + if (upper->effects[id].owner == NULL) + { + break; + } + } + + if (id >= upper->max_effects) + { + return -ENOSPC; + } + + effect->id = id; + old = NULL; + } + else + { + id = effect->id; + ret = ff_check_effect_access(upper, id, filep); + if (ret < 0) + { + return ret; + } + + old = &upper->effects[id].data; + if (!ff_check_effects_compatible(effect, old)) + { + return -EINVAL; + } + } + + ret = lower->upload(lower, effect, old); + if (ret < 0) + { + return ret; + } + + upper->effects[id].data = *effect; + upper->effects[id].owner = filep; + + return ret; +} + +/**************************************************************************** + * Name: ff_open + ****************************************************************************/ + +static int ff_open(FAR struct file *filep) +{ + return OK; +} + +/**************************************************************************** + * Name: ff_close + ****************************************************************************/ + +static int ff_close(FAR struct file *filep) +{ + return OK; +} + +/**************************************************************************** + * Name: ff_write + ****************************************************************************/ + +static ssize_t ff_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ff_upperhalf_s *upper = inode->i_private; + FAR struct ff_event_s *event = (FAR struct ff_event_s *)buffer; + + DEBUGASSERT(buflen == sizeof(*event)); + + nxmutex_lock(&upper->lock); + ff_event(upper->lower, event->code, event->value); + nxmutex_unlock(&upper->lock); + + return buflen; +} + +/**************************************************************************** + * Name: ff_ioctl + ****************************************************************************/ + +static int ff_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ff_upperhalf_s *upper = inode->i_private; + int ret = OK; + + nxmutex_lock(&upper->lock); + switch (cmd) + { + case EVIOCGBIT: + { + memcpy((FAR unsigned long *)(uintptr_t)arg, upper->lower->ffbit, + sizeof(upper->lower->ffbit)); + } + break; + + case EVIOCSFF: + { + ret = ff_upload(upper, (FAR struct ff_effect *)(uintptr_t)arg, + filep); + } + break; + + case EVIOCRMFF: + { + ret = ff_erase_effect(upper, (int)arg, filep); + } + break; + + case EVIOCGEFFECTS: + { + *(FAR int *)(uintptr_t)arg = upper->max_effects; + } + break; + + default: + ret = -ENOTTY; + break; + } + + nxmutex_unlock(&upper->lock); + return ret; +} + +/**************************************************************************** + * Public Function + ****************************************************************************/ + +/**************************************************************************** + * Name: ff_event + * + * Description: + * The lower half driver pushes force feedback events through this + * interface, provided by force feedback upper half. + * + * Arguments: + * lower - lower half driver handle. + * code - event code. + * value - event value. + * + ****************************************************************************/ + +int ff_event(FAR struct ff_lowerhalf_s *lower, uint32_t code, int value) +{ + irqstate_t flags; + int ret = OK; + + switch (code) + { + case FF_GAIN: + { + if (!test_bit(FF_GAIN, lower->ffbit) || value > 0xffffu) + { + break; + } + + flags = enter_critical_section(); + lower->set_gain(lower, value); + leave_critical_section(flags); + } + break; + + case FF_AUTOCENTER: + { + if (!test_bit(FF_AUTOCENTER, lower->ffbit) || value > 0xffffu) + { + break; + } + + flags = enter_critical_section(); + lower->set_autocenter(lower, value); + leave_critical_section(flags); + } + break; + + default: + { + if (ff_check_effect_access(lower->priv, code, NULL) == 0) + { + flags = enter_critical_section(); + ret = lower->playback(lower, code, value); + leave_critical_section(flags); + } + } + break; + } + + return ret; +} + +/**************************************************************************** + * Name: ff_register + * + * Description: + * This function registers a force feedback device, the upper half binds + * with hardware device through the lower half instance. + * + * Arguments: + * lower - A pointer of lower half instance. + * path - The path of force feedback device. such as "/dev/input_ff0". + * max_effects - Maximum number of effects supported by device. + * + * Return: + * OK if the driver was successfully registered; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int ff_register(FAR struct ff_lowerhalf_s *lower, FAR const char *path, + int max_effects) +{ + FAR struct ff_upperhalf_s *upper; + int ret; + + if (lower == NULL || max_effects == 0 || max_effects > FF_MAX_EFFECTS) + { + return -EINVAL; + } + + upper = kmm_zalloc(sizeof(struct ff_upperhalf_s) + + sizeof(struct ff_effect_s) * max_effects); + if (upper == NULL) + { + return -ENOMEM; + } + + upper->lower = lower; + lower->priv = upper; + upper->max_effects = max_effects; + upper->effects = (FAR struct ff_effect_s *)(upper + 1); + nxmutex_init(&upper->lock); + + ret = register_driver(path, &g_ff_fops, 0666, upper); + if (ret < 0) + { + nxmutex_destroy(&upper->lock); + kmm_free(upper); + } + + return ret; +} + +/**************************************************************************** + * Name: ff_unregister + * + * Description: + * This function is used to force feedback driver to unregister and + * release the occupied resources. + * + * Arguments: + * lower - A pointer to an insatnce of force feedback lower half driver. + * path - The path of force feedback device. such as "/dev/input0" + * + ****************************************************************************/ + +void ff_unregister(FAR struct ff_lowerhalf_s *lower, FAR const char *path) +{ + FAR struct ff_upperhalf_s *upper; + + if (unregister_driver(path) < 0) + { + return; + } + + if (lower->destroy != NULL) + { + lower->destroy(lower); + } + + upper = lower->priv; + nxmutex_destroy(&upper->lock); + kmm_free(upper); +} diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 825de7dbcf..4b91c2d4f5 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -100,6 +100,7 @@ #define _SYSLOGBASE (0x3c00) /* Syslog device ioctl commands */ #define _STEPIOBASE (0x3d00) /* Stepper device ioctl commands */ #define _FPGACFGBASE (0x3e00) /* FPGA configuration ioctl commands */ +#define _FFIOCBASE (0x3f00) /* Force feedback ioctl commands */ #define _PCIBASE (0x4100) /* Pci ioctl commands */ #define _WLIOCBASE (0x8b00) /* Wireless modules ioctl network commands */ @@ -724,6 +725,13 @@ #define _PCIIOCVALID(c) (_IOC_TYPE(c)==_PCIBASE) #define _PCIIOC(nr) _IOC(_PCIBASE,nr) +/* Force Feedback driver command definitions ********************************/ + +/* see nuttx/include/input/ff.h */ + +#define _FFIOCVALID(c) (_IOC_TYPE(c)==_FFIOCBASE) +#define _FFIOC(nr) _IOC(_FFIOCBASE,nr) + /**************************************************************************** * Public Type Definitions ****************************************************************************/ diff --git a/include/nuttx/input/ff.h b/include/nuttx/input/ff.h new file mode 100644 index 0000000000..95e0ba0807 --- /dev/null +++ b/include/nuttx/input/ff.h @@ -0,0 +1,490 @@ +/**************************************************************************** + * include/nuttx/input/ff.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_INPUT_FF_H +#define __INCLUDE_NUTTX_INPUT_FF_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Force feedback effect types. */ + +#define FF_RUMBLE 0x00 +#define FF_PERIODIC 0x01 +#define FF_CONSTANT 0x02 +#define FF_SPRING 0x03 +#define FF_FRICTION 0x04 +#define FF_DAMPER 0x05 +#define FF_INERTIA 0x06 +#define FF_RAMP 0x07 + +#define FF_EFFECT_MIN FF_RUMBLE +#define FF_EFFECT_MAX FF_RAMP + +/* Force feedback periodic effect types. */ + +#define FF_SQUARE 0x08 +#define FF_TRIANGLE 0x09 +#define FF_SINE 0x0a +#define FF_SAW_UP 0x0b +#define FF_SAW_DOWN 0x0c +#define FF_CUSTOM 0x0d + +#define FF_WAVEFORM_MIN FF_SQUARE +#define FF_WAVEFORM_MAX FF_CUSTOM + +/* Set ff device properties. */ + +#define FF_GAIN 0x0e +#define FF_AUTOCENTER 0x0f + +/* Total number of effects should never exceed FF_MAX_EFFECTS. */ + +#define FF_MAX_EFFECTS FF_GAIN + +#define FF_MAX 0x1f +#define FF_CNT (FF_MAX + 1) + +/* Values describing the status of a force-feedback effect. */ + +#define FF_STATUS_STOPPED 0x00 +#define FF_STATUS_PLAYING 0x01 +#define FF_STATUS_MAX 0x01 + +/* IOCTL commands unique to the force feedback device */ + +/* This cmd use to querying device capabilities. + * Arg: pointer to address of + * "unsigned long features[BITS_TO_LONGS(FF_CNT)]". + */ + +#define EVIOCGBIT _FFIOC(0) + +/* This cmd use to send a force effect to a force feedback device. + * Arg: pointer to address of struct ff_effect. + */ + +#define EVIOCSFF _FFIOC(1) + +/* This cmd use to erase a force effect. + * Arg: int value, the effect id. + */ + +#define EVIOCRMFF _FFIOC(2) + +/* This cmd use to report number of effects playable at the same time. + * Arg: pointer to address of int value, return the number of effects. + */ + +#define EVIOCGEFFECTS _FFIOC(3) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Structures used in ioctls to upload effects to a device + * They are pieces of a bigger structure (called ff_effect). + */ + +/* All duration values are expressed in ms. Values above 32767 ms (0x7fff) + * should not be used and have unspecified results. + */ + +/* struct ff_replay - defines scheduling of the force-feedback effect */ + +struct ff_replay +{ + /* Duration of the effect */ + + uint16_t length; + + /* Delay before effect should start playing */ + + uint16_t delay; +}; + +/* struct ff_trigger - defines what triggers the force-feedback effect */ + +struct ff_trigger +{ + /* Number of the button triggering the effect */ + + uint16_t button; + + /* Controls how soon the effect can be re-triggered */ + + uint16_t interval; +}; + +/* struct ff_envelope - generic force-feedback effect envelope + * + * The attack_level and fade_level are absolute values; when applying + * envelope force-feedback core will convert to positive/negative + * value based on polarity of the default level of the effect. + * Valid range for the attack and fade levels is 0x0000 - 0x7fff + */ + +struct ff_envelope +{ + /* Duration of the attack (ms). */ + + uint16_t attack_length; + + /* Level at the beginning of the attack. */ + + uint16_t attack_level; + + /* Duration of fade (ms). */ + + uint16_t fade_length; + + /* Level at the end of fade. */ + + uint16_t fade_level; +}; + +/* struct ff_constant_effect - defines parameters of a constant + * force-feedback effect. + */ + +struct ff_constant_effect +{ + /* Strength of the effect; may be negative. */ + + int16_t level; + + /* Envelope data. */ + + struct ff_envelope envelope; +}; + +/* struct ff_ramp_effect - defines parameters of a ramp force-feedback + * effect. + */ + +struct ff_ramp_effect +{ + /* Beginning strength of the effect; may be negative. */ + + int16_t start_level; + + /* Final strength of the effect; may be negative. */ + + int16_t end_level; + + /* Envelope data. */ + + struct ff_envelope envelope; +}; + +/* struct ff_condition_effect - defines a spring or friction force-feedback + * effect + */ + +struct ff_condition_effect +{ + /* Maximum level when joystick moved all way to the right. */ + + uint16_t right_saturation; + + /* Same for the left side. */ + + uint16_t left_saturation; + + /* Controls how fast the force grows when the joystick moves + * to the right. + */ + + int16_t right_coeff; + + /* Same for the left side */ + + int16_t left_coeff; + + /* Size of the dead zone, where no force is produced */ + + uint16_t deadband; + + /* Position of the dead zone */ + + int16_t center; +}; + +/* struct ff_periodic_effect - defines parameters of a periodic + * force-feedback effect. + * + * Known waveforms - FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, + * FF_SAW_DOWN, FF_CUSTOM. The exact syntax FF_CUSTOM is undefined + * for the time being as no driver supports it yet. + * + * Note: the data pointed by custom_data is copied by the driver. + * You can therefore dispose of the memory after the upload/update. + */ + +struct ff_periodic_effect +{ + /* Kind of the effect (wave). */ + + uint16_t waveform; + + /* Period of the wave (ms). */ + + uint16_t period; + + /* Peak value. */ + + int16_t magnitude; + + /* Mean value of the wave (roughly). */ + + int16_t offset; + + /* 'horizontal' shift. */ + + uint16_t phase; + + /* Envelope data. */ + + struct ff_envelope envelope; + + /* Number of samples (FF_CUSTOM only). */ + + uint32_t custom_len; + + /* Buffer of samples (FF_CUSTOM only). */ + + FAR int16_t *custom_data; +}; + +/* struct ff_rumble_effect - defines parameters of a periodic + * force-feedback effect + * + * Some rumble pads have two motors of different weight. Strong_magnitude + * represents the magnitude of the vibration generated by the heavy one. + */ + +struct ff_rumble_effect +{ + /* Magnitude of the heavy motor. */ + + uint16_t strong_magnitude; + + /* Magnitude of the light one */ + + uint16_t weak_magnitude; +}; + +/* struct ff_effect - defines force feedback effect + * This structure is sent through ioctl from the application to the driver. + * To create a new effect application should set its @id to -1; the kernel + * will return assigned @id which can later be used to update or delete + * this effect. + */ + +struct ff_effect +{ + /* Type of the effect (FF_CONSTANT, FF_PERIODIC, FF_RAMP, FF_SPRING, + * FF_FRICTION, FF_DAMPER, FF_RUMBLE, FF_INERTIA, or FF_CUSTOM). + */ + + uint16_t type; + + /* An unique id assigned to an effect. */ + + int16_t id; + + /* Direction of the effect is encoded as follows: + * 0 deg -> 0x0000 (down) + * 90 deg -> 0x4000 (left) + * 180 deg -> 0x8000 (up) + * 270 deg -> 0xC000 (right) + */ + + uint16_t direction; + + /* Trigger conditions (struct ff_trigger). */ + + struct ff_trigger trigger; + + /* Scheduling of the effect (struct ff_replay). */ + + struct ff_replay replay; + + /* Effect-specific structure (one of ff_constant_effect, + * ff_ramp_effect, ff_periodic_effect, ff_rumble_effect) further + * defining effect parameters. + */ + + union + { + struct ff_constant_effect constant; + struct ff_ramp_effect ramp; + struct ff_periodic_effect periodic; + struct ff_condition_effect condition[2]; + struct ff_rumble_effect rumble; + } u; +}; + +/* The structure is used to description the event by userspace specified. */ + +struct ff_event_s +{ + uint32_t code; /* Event code, eg: FF_GAIN, FF_AUTOCENTER, effect id */ + int value; /* Event value corresponding to Event code */ +}; + +/* struct ff_lowerhalf_s - force-feedback device lower half driver + * + * Every force-feedback device must implement upload() and playback() + * methods; erase() is optional. set_gain() and set_autocenter() need + * only be implemented if driver sets up FF_GAIN and FF_AUTOCENTER + * bits. + * + * Note that playback(), set_gain() and set_autocenter() are called + * with lock held and interrupts off and thus may not sleep. + */ + +struct ff_lowerhalf_s +{ + /* Called to upload an new effect into device. */ + + CODE int (*upload)(FAR struct ff_lowerhalf_s *lower, + FAR struct ff_effect *effect, + FAR struct ff_effect *old); + + /* Called to erase an effect from device. */ + + CODE int (*erase)(FAR struct ff_lowerhalf_s *lower, int effect_id); + + /* Called to request device to start playing specified effect. */ + + CODE int (*playback)(FAR struct ff_lowerhalf_s *lower, int effect_id, + int value); + + /* Called to set specified gain. */ + + CODE void (*set_gain)(FAR struct ff_lowerhalf_s *lower, uint16_t gain); + + /* Called to auto-center device. */ + + CODE void (*set_autocenter)(FAR struct ff_lowerhalf_s *lower, + uint16_t magnitude); + + /* Called by ff upper half when device is being destroyed. */ + + CODE void (*destroy)(FAR struct ff_lowerhalf_s *lower); + + /* The bitmap of force feedback capabilities truly supported by device */ + + unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; + + /* The private opaque pointer to be passed to upper-layer during callback */ + + FAR void *priv; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: ff_event + ****************************************************************************/ + +/**************************************************************************** + * Name: ff_event + * + * Description: + * The lower half driver pushes force feedback events through this + * interface, provided by force feedback upper half. + * + * Arguments: + * lower - lower half driver handle. + * code - event code. + * value - event value. + * + * Return: + * OK if the driver was successfully process event; A negated errno value + * is returned on any failure. + * + ****************************************************************************/ + +int ff_event(FAR struct ff_lowerhalf_s *lower, uint32_t code, int value); + +/**************************************************************************** + * Name: ff_register + * + * Description: + * This function registers a force feedback device, the upper half binds + * with hardware device through the lower half instance. + * + * Arguments: + * lower - A pointer of lower half instance. + * path - The path of force feedback device. such as "/dev/input0". + * max_effects - Maximum number of effects supported by device. + * + * Return: + * OK if the driver was successfully registered; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int ff_register(FAR struct ff_lowerhalf_s *lower, FAR const char *path, + int max_effects); + +/**************************************************************************** + * Name: ff_unregister + * + * Description: + * This function is used to force feedback driver to unregister and + * release the occupied resources. + * + * Arguments: + * lower - A pointer to an insatnce of force feedback lower half driver. + * path - The path of force feedback device. such as "/dev/input0" + * + ****************************************************************************/ + +void ff_unregister(FAR struct ff_lowerhalf_s *lower, FAR const char *path); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_INPUT_FF_H */