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