1 /*
2 * Copyright (C) 2020 Purism SPC
3 * SPDX-License-Identifier: GPL-3.0+
4 * Author: Guido Günther <agx@sigxcpu.org>
5 *
6 * See https://www.kernel.org/doc/html/latest/input/ff.html
7 * and fftest.c from the joystick package.
8 */
9
10 #define G_LOG_DOMAIN "fbd-dev-vibra"
11
12 #include "fbd-dev-vibra.h"
13
14 #include <gio/gio.h>
15
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <linux/input.h>
19 #include <sys/ioctl.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22
23 /**
24 * SECTION:fbd-dev-vibra
25 * @short_description: Haptic motor device interface
26 * @Title: FbdDevVibra
27 *
28 * The #FbdDevVibra is used to interface with haptic motor via the force
29 * feedback interface. It currently only supports one id at a time.
30 */
31
32 enum {
33 PROP_0,
34 PROP_DEVICE,
35 PROP_LAST_PROP,
36 };
37 static GParamSpec *props[PROP_LAST_PROP];
38
39 typedef enum {
40 FBD_DEV_VIBRA_FEATURE_RUMBLE,
41 FBD_DEV_VIBRA_FEATURE_PERIODIC,
42 FBD_DEV_VIBRA_FEATURE_GAIN,
43 } FbdDevVibraFeatureFlags;
44
45 typedef struct _FbdDevVibra {
46 GObject parent;
47
48 GUdevDevice *device;
49 gint fd;
50 gint id; /* currently used id */
51
52 FbdDevVibraFeatureFlags features;
53 } FbdDevVibra;
54
55 static void initable_iface_init (GInitableIface *iface);
56
57 G_DEFINE_TYPE_WITH_CODE (FbdDevVibra, fbd_dev_vibra, G_TYPE_OBJECT,
58 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init));
59
60 static void
fbd_dev_vibra_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)61 fbd_dev_vibra_set_property (GObject *object,
62 guint property_id,
63 const GValue *value,
64 GParamSpec *pspec)
65 {
66 FbdDevVibra *self = FBD_DEV_VIBRA (object);
67
68 switch (property_id) {
69 case PROP_DEVICE:
70 g_clear_object (&self->device);
71 self->device = g_value_dup_object (value);
72 break;
73 default:
74 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
75 break;
76 }
77 }
78
79
80 static void
fbd_dev_vibra_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)81 fbd_dev_vibra_get_property (GObject *object,
82 guint property_id,
83 GValue *value,
84 GParamSpec *pspec)
85 {
86 switch (property_id) {
87 default:
88 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
89 break;
90 }
91 }
92
93 #define BITS_PER_LONG (8 * sizeof (long))
94
95 #define HAS_FEATURE(fbit, a) \
96 (a[(fbit/BITS_PER_LONG)] >> ((fbit % BITS_PER_LONG)) & 1)
97
98 static gboolean
initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)99 initable_init (GInitable *initable,
100 GCancellable *cancellable,
101 GError **error)
102 {
103 FbdDevVibra *self = FBD_DEV_VIBRA (initable);
104 const char *filename = g_udev_device_get_device_file (self->device);
105 gulong features[1 + FF_MAX/BITS_PER_LONG];
106 struct input_event gain = { 0 };
107
108 self->fd = open (filename, O_RDWR | O_NONBLOCK, O_RDWR);
109 if (self->fd < 0) {
110 g_set_error (error,
111 G_FILE_ERROR, G_FILE_ERROR_FAILED,
112 "Unable to open '%s': %s",
113 filename, g_strerror (errno));
114 return FALSE;
115 }
116
117 if (ioctl (self->fd, EVIOCGBIT(EV_FF, sizeof (features)), features) == -1) {
118 g_set_error (error,
119 G_FILE_ERROR, G_FILE_ERROR_FAILED,
120 "Unable to probe features of '%s': %s",
121 filename, g_strerror (errno));
122 return FALSE;
123 }
124
125 if (HAS_FEATURE(FF_RUMBLE, features))
126 self->features |= FBD_DEV_VIBRA_FEATURE_RUMBLE;
127 else {
128 g_set_error (error,
129 G_FILE_ERROR, G_FILE_ERROR_FAILED,
130 "No rumble capable vibra device “%s”: %s",
131 filename, g_strerror (errno));
132 return FALSE;
133 }
134
135 if (HAS_FEATURE(FF_PERIODIC, features))
136 self->features |= FBD_DEV_VIBRA_FEATURE_PERIODIC;
137 else {
138 g_set_error (error,
139 G_FILE_ERROR, G_FILE_ERROR_FAILED,
140 "No rumble capable vibra device “%s”: %s",
141 filename, g_strerror (errno));
142 return FALSE;
143 }
144
145 /* Set gain to 75% if supported */
146 if (HAS_FEATURE(FF_GAIN, features)) {
147 self->features |= FBD_DEV_VIBRA_FEATURE_GAIN;
148 memset(&gain, 0, sizeof(gain));
149 gain.type = EV_FF;
150 gain.code = FF_GAIN;
151 gain.value = 0xC000; /* [0, 0xFFFF]) */
152
153 g_debug("Setting master gain to 75%%");
154 if (write(self->fd, &gain, sizeof(gain)) != sizeof(gain)) {
155 g_set_error (error,
156 G_FILE_ERROR, G_FILE_ERROR_FAILED,
157 "Unable to set gain of '%s': %s",
158 filename, g_strerror (errno));
159 }
160 } else {
161 g_debug ("Gain unsupported");
162 }
163
164 g_debug ("Vibra device at '%s' usable", filename);
165 return TRUE;
166 }
167
168 static void
initable_iface_init(GInitableIface * iface)169 initable_iface_init (GInitableIface *iface)
170 {
171 iface->init = initable_init;
172 }
173
174 static void
fbd_dev_vibra_dispose(GObject * object)175 fbd_dev_vibra_dispose (GObject *object)
176 {
177 FbdDevVibra *self = FBD_DEV_VIBRA (object);
178
179 g_clear_object (&self->device);
180
181 G_OBJECT_CLASS (fbd_dev_vibra_parent_class)->dispose (object);
182 }
183
184 static void
fbd_dev_vibra_finalize(GObject * object)185 fbd_dev_vibra_finalize (GObject *object)
186 {
187 FbdDevVibra *self = FBD_DEV_VIBRA (object);
188
189 if (self->fd >= 0) {
190 close (self->fd);
191 self->fd = -1;
192 }
193
194 G_OBJECT_CLASS (fbd_dev_vibra_parent_class)->finalize (object);
195 }
196
197 static void
fbd_dev_vibra_class_init(FbdDevVibraClass * klass)198 fbd_dev_vibra_class_init (FbdDevVibraClass *klass)
199 {
200 GObjectClass *object_class = G_OBJECT_CLASS (klass);
201
202 object_class->set_property = fbd_dev_vibra_set_property;
203 object_class->get_property = fbd_dev_vibra_get_property;
204
205 object_class->dispose = fbd_dev_vibra_dispose;
206 object_class->finalize = fbd_dev_vibra_finalize;
207
208 props[PROP_DEVICE] =
209 g_param_spec_object (
210 "device",
211 "Device",
212 "The udev device",
213 G_UDEV_TYPE_DEVICE,
214 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
215
216 g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
217 }
218
219 static void
fbd_dev_vibra_init(FbdDevVibra * self)220 fbd_dev_vibra_init (FbdDevVibra *self)
221 {
222 }
223
224 FbdDevVibra *
fbd_dev_vibra_new(GUdevDevice * device,GError ** error)225 fbd_dev_vibra_new (GUdevDevice *device, GError **error)
226 {
227 return FBD_DEV_VIBRA (g_initable_new (FBD_TYPE_DEV_VIBRA,
228 NULL,
229 error,
230 "device", device,
231 NULL));
232 }
233
234 gboolean
fbd_dev_vibra_rumble(FbdDevVibra * self,guint duration,gboolean upload)235 fbd_dev_vibra_rumble (FbdDevVibra *self, guint duration, gboolean upload)
236 {
237 struct input_event event = { 0 };
238 struct ff_effect effect = { 0 };
239
240 g_return_val_if_fail (FBD_IS_DEV_VIBRA (self), FALSE);
241
242 memset(&effect, 0, sizeof(effect));
243 effect.type = FF_RUMBLE;
244 effect.id = -1;
245 effect.u.rumble.strong_magnitude = 0x8000;
246 effect.u.rumble.weak_magnitude = 0;
247 effect.replay.length = duration;
248 effect.replay.delay = 0;
249
250 if (upload) {
251 g_debug("Uploading rumbling vibra effect (%d)", self->fd);
252 if (ioctl(self->fd, EVIOCSFF, &effect) == -1) {
253 g_warning ("Failed to upload rumbling vibra effect: %s", g_strerror (errno));
254 return FALSE;
255 }
256 self->id = effect.id;
257 }
258
259 g_debug("Playing rumbling vibra effect id %d", effect.id);
260 event.type = EV_FF;
261 event.value = 1;
262 event.code = self->id;
263
264 if (write (self->fd, (const void*) &event, sizeof (event)) < 0) {
265 g_warning ("Failed to play rumbling vibra effect.");
266 return FALSE;
267 }
268
269 return TRUE;
270 }
271
272 /* TODO: fall back to multiple rumbles when sine not supported */
273 gboolean
fbd_dev_vibra_periodic(FbdDevVibra * self,guint duration,guint magnitude,guint fade_in_level,guint fade_in_time)274 fbd_dev_vibra_periodic (FbdDevVibra *self, guint duration, guint magnitude,
275 guint fade_in_level, guint fade_in_time)
276 {
277 struct input_event event;
278 struct ff_effect effect = { 0 };
279
280 g_return_val_if_fail (FBD_IS_DEV_VIBRA (self), FALSE);
281
282 if (!magnitude)
283 magnitude = 0x7FFF;
284
285 if (!fade_in_level)
286 fade_in_level = magnitude;
287
288 if (!fade_in_time)
289 fade_in_time = duration;
290
291 effect.type = FF_PERIODIC;
292 effect.id = -1;
293 effect.u.periodic.waveform = FF_SINE;
294 effect.u.periodic.period = 10;
295 effect.u.periodic.magnitude = magnitude;
296 effect.u.periodic.offset = 0;
297 effect.u.periodic.phase = 0;
298 effect.direction = 0x4000;
299 effect.u.periodic.envelope.attack_length = fade_in_time;
300 effect.u.periodic.envelope.attack_level = fade_in_level;;
301 effect.u.periodic.envelope.fade_length = 0;
302 effect.u.periodic.envelope.fade_level = 0;
303 effect.trigger.button = 0;
304 effect.trigger.interval = 0;
305 effect.replay.length = duration;
306 effect.replay.delay = 200;
307
308 g_debug("Uploading periodic effect (%d)", self->fd);
309 if (ioctl(self->fd, EVIOCSFF, &effect) == -1) {
310 g_warning ("Failed to upload periodic vibra effect: %s", g_strerror (errno));
311 return FALSE;
312 }
313
314 g_debug("Playing periodic vibra effect id %d", effect.id);
315 event.type = EV_FF;
316 self->id = event.code = effect.id;
317 event.value = 1;
318
319 if (write (self->fd, (const void*) &event, sizeof (event)) < 0) {
320 g_warning ("Failed to play rumbling effect.");
321 return FALSE;
322 }
323
324 return TRUE;
325 }
326
327
328 gboolean
fbd_dev_vibra_remove_effect(FbdDevVibra * self)329 fbd_dev_vibra_remove_effect (FbdDevVibra *self)
330 {
331 g_return_val_if_fail (FBD_IS_DEV_VIBRA (self), FALSE);
332
333 g_debug("Erasing vibra effect (%d)", self->fd);
334 if (ioctl(self->fd, EVIOCRMFF, self->id) == -1) {
335 g_warning ("Failed to erase vibra effect with id %d: %s", self->id, strerror(errno));
336 return FALSE;
337 }
338 return TRUE;
339 }
340
341
342 gboolean
fbd_dev_vibra_stop(FbdDevVibra * self)343 fbd_dev_vibra_stop(FbdDevVibra *self)
344 {
345 struct input_event stop = { 0 };
346
347 g_return_val_if_fail (FBD_IS_DEV_VIBRA (self), FALSE);
348
349 stop.type = EV_FF;
350 stop.code = self->id;
351 stop.value = 0;
352
353 if (write(self->fd, (const void*) &stop, sizeof(stop)) < 0) {
354 g_warning ("Failed to stop vibra effect with id %d: %s", self->id, strerror(errno));
355 return FALSE;
356 }
357
358 return fbd_dev_vibra_remove_effect (self);
359 }
360
361 GUdevDevice *
fbd_dev_vibra_get_device(FbdDevVibra * self)362 fbd_dev_vibra_get_device(FbdDevVibra *self)
363 {
364 g_return_val_if_fail (FBD_IS_DEV_VIBRA (self), FALSE);
365
366 return self->device;
367 }
368