bool unsync : 1; /* not in-sync with kernel */
bool resync : 1; /* re-syncing with kernel */
+ bool running : 1;
};
struct unmanaged_evdev {
struct managed_evdev {
idev_evdev evdev;
dev_t devnum;
-
- sd_bus_slot *slot_pause_device;
- sd_bus_slot *slot_resume_device;
sd_bus_slot *slot_take_device;
bool requested : 1; /* TakeDevice() was sent */
sprintf(out, "evdev/%u:%u", major(devnum), minor(devnum));
}
-static int idev_evdev_raise(idev_evdev *evdev, struct input_event *event) {
+static int idev_evdev_feed_resync(idev_evdev *evdev) {
+ idev_data data = {
+ .type = IDEV_DATA_RESYNC,
+ .resync = evdev->resync,
+ };
+
+ return idev_element_feed(&evdev->element, &data);
+}
+
+static int idev_evdev_feed_evdev(idev_evdev *evdev, struct input_event *event) {
idev_data data = {
.type = IDEV_DATA_EVDEV,
.resync = evdev->resync,
* case we cannot keep up with the kernel.
* TODO: Make sure libevdev always reports SYN_DROPPED to us, regardless
* whether any event was synced afterwards.
- * TODO: Forward SYN_DROPPED to attached devices.
*/
flags = LIBEVDEV_READ_FLAG_NORMAL;
} else if (r == LIBEVDEV_READ_STATUS_SYNC) {
if (evdev->resync) {
/* sync-event */
- r = idev_evdev_raise(evdev, &ev);
+ r = idev_evdev_feed_evdev(evdev, &ev);
if (r != 0) {
error = r;
break;
/* start of sync */
evdev->resync = true;
flags = LIBEVDEV_READ_FLAG_SYNC;
+ r = idev_evdev_feed_resync(evdev);
+ if (r != 0) {
+ error = r;
+ break;
+ }
}
} else {
/* normal event */
- r = idev_evdev_raise(evdev, &ev);
+ r = idev_evdev_feed_evdev(evdev, &ev);
if (r != 0) {
error = r;
break;
}
if (error < 0)
- log_debug("idev-evdev: %s/%s: error on data event: %s",
- e->session->name, e->name, strerror(-error));
+ log_debug_errno(error, "idev-evdev: %s/%s: error on data event: %m",
+ e->session->name, e->name);
return error;
error:
assert(evdev->fd_src);
assert(evdev->idle_src);
+ if (evdev->running)
+ return;
+ if (evdev->fd < 0 || evdev->element.n_open < 1 || !evdev->element.enabled)
+ return;
+
+ evdev->running = true;
sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_ON);
sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_ONESHOT);
}
assert(evdev->fd_src);
assert(evdev->idle_src);
+ if (!evdev->running)
+ return;
+
+ evdev->running = false;
+ idev_evdev_feed_resync(evdev);
sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
}
if (fd < 0 || evdev->fd == fd) {
fd = -1;
- if (evdev->fd >= 0 && e->n_open > 0 && e->enabled)
- idev_evdev_enable(evdev);
-
+ idev_evdev_enable(evdev);
return 0;
}
return -EACCES;
evdev->element.readable = true;
- evdev->element.writable = true;
- if (flags == O_RDONLY)
- evdev->element.writable = false;
- else if (flags == O_WRONLY)
- evdev->element.readable = false;
+ evdev->element.writable = !(flags & O_RDONLY);
/*
* TODO: We *MUST* re-sync the device so we get a delta of the changed
return r;
}
- if (e->n_open < 1 || !e->enabled) {
- sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
- sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
- }
+ sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
+ sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
evdev->unsync = true;
evdev->fd = fd;
-
fd = -1;
+
+ idev_evdev_enable(evdev);
return 0;
}
log_debug("idev-evdev: %s/%s: pause", e->session->name, e->name);
+ idev_evdev_disable(evdev);
if (release) {
evdev->idle_src = sd_event_source_unref(evdev->idle_src);
evdev->fd_src = sd_event_source_unref(evdev->fd_src);
evdev->fd = safe_close(evdev->fd);
- } else {
- idev_evdev_disable(evdev);
}
}
fd = open(eu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
if (fd < 0) {
if (errno != EACCES && errno != EPERM) {
- log_debug("idev-evdev: %s/%s: cannot open node %s: %m",
- e->session->name, e->name, eu->devnode);
+ log_debug_errno(errno, "idev-evdev: %s/%s: cannot open node %s: %m",
+ e->session->name, e->name, eu->devnode);
return;
}
fd = open(eu->devnode, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
if (fd < 0) {
- log_debug("idev-evdev: %s/%s: cannot open node %s: %m",
- e->session->name, e->name, eu->devnode);
+ log_debug_errno(errno, "idev-evdev: %s/%s: cannot open node %s: %m",
+ e->session->name, e->name, eu->devnode);
return;
}
r = idev_evdev_resume(&eu->evdev, fd);
if (r < 0)
- log_debug("idev-evdev: %s/%s: cannot resume: %s",
- e->session->name, e->name, strerror(-r));
+ log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m",
+ e->session->name, e->name);
}
static void unmanaged_evdev_pause(idev_element *e) {
fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
if (fd < 0) {
- log_debug("idev-evdev: %s/%s: cannot duplicate evdev fd: %m", s->name, e->name);
+ log_debug_errno(errno, "idev-evdev: %s/%s: cannot duplicate evdev fd: %m", s->name, e->name);
return 0;
}
r = idev_evdev_resume(&em->evdev, fd);
if (r < 0)
- log_debug("idev-evdev: %s/%s: cannot resume: %s",
- s->name, e->name, strerror(-r));
+ log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m",
+ s->name, e->name);
return 0;
}
-static void managed_evdev_resume(idev_element *e) {
+static void managed_evdev_enable(idev_element *e) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
managed_evdev *em = managed_evdev_from_element(e);
idev_session *s = e->session;
return;
error:
- log_debug("idev-evdev: %s/%s: cannot send TakeDevice request: %s",
- s->name, e->name, strerror(-r));
+ log_debug_errno(r, "idev-evdev: %s/%s: cannot send TakeDevice request: %m",
+ s->name, e->name);
}
-static void managed_evdev_pause(idev_element *e) {
+static void managed_evdev_disable(idev_element *e) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
managed_evdev *em = managed_evdev_from_element(e);
idev_session *s = e->session;
}
if (r < 0 && r != -ENOTCONN)
- log_debug("idev-evdev: %s/%s: cannot send ReleaseDevice: %s",
- s->name, e->name, strerror(-r));
+ log_debug_errno(r, "idev-evdev: %s/%s: cannot send ReleaseDevice: %m",
+ s->name, e->name);
}
-static int managed_evdev_pause_device_fn(sd_bus *bus,
- sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- managed_evdev *em = userdata;
- idev_element *e = &em->evdev.element;
+static void managed_evdev_resume(idev_element *e, int fd) {
+ managed_evdev *em = managed_evdev_from_element(e);
+ idev_session *s = e->session;
+ int r;
+
+ /*
+ * We get ResumeDevice signals whenever logind resumed a previously
+ * paused device. The arguments contain the major/minor number of the
+ * related device and a new file-descriptor for the freshly opened
+ * device-node. We take the file-descriptor and immediately resume the
+ * device.
+ */
+
+ fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (fd < 0) {
+ log_debug_errno(errno, "idev-evdev: %s/%s: cannot duplicate evdev fd: %m",
+ s->name, e->name);
+ return;
+ }
+
+ r = idev_evdev_resume(&em->evdev, fd);
+ if (r < 0)
+ log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m",
+ s->name, e->name);
+
+ return;
+}
+
+static void managed_evdev_pause(idev_element *e, const char *mode) {
+ managed_evdev *em = managed_evdev_from_element(e);
idev_session *s = e->session;
idev_context *c = s->context;
- uint32_t major, minor;
- const char *mode;
int r;
/*
* We get PauseDevice() signals from logind whenever a device we
* requested was, or is about to be, paused. Arguments are major/minor
* number of the device and the mode of the operation.
- * In case the event is not about our device, we ignore it. Otherwise,
- * we treat it as asynchronous access-revocation (as if we got HUP on
+ * We treat it as asynchronous access-revocation (as if we got HUP on
* the device fd). Note that we might have already treated the HUP
* event via EPOLLHUP, whichever comes first.
*
* acknowledge the request.
*/
- r = sd_bus_message_read(signal, "uus", &major, &minor, &mode);
- if (r < 0) {
- log_debug("idev-evdev: %s/%s: erroneous PauseDevice signal",
- s->name, e->name);
- return 0;
- }
-
- /* not our device? */
- if (makedev(major, minor) != em->devnum)
- return 0;
-
idev_evdev_pause(&em->evdev, true);
if (streq(mode, "pause")) {
"org.freedesktop.login1.Session",
"PauseDeviceComplete");
if (r >= 0) {
- r = sd_bus_message_append(m, "uu", major, minor);
+ r = sd_bus_message_append(m, "uu", major(em->devnum), minor(em->devnum));
if (r >= 0)
r = sd_bus_send(c->sysbus, m, NULL);
}
if (r < 0)
- log_debug("idev-evdev: %s/%s: cannot send PauseDeviceComplete: %s",
- s->name, e->name, strerror(-r));
+ log_debug_errno(r, "idev-evdev: %s/%s: cannot send PauseDeviceComplete: %m",
+ s->name, e->name);
}
-
- return 0;
-}
-
-static int managed_evdev_resume_device_fn(sd_bus *bus,
- sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- managed_evdev *em = userdata;
- idev_element *e = &em->evdev.element;
- idev_session *s = e->session;
- uint32_t major, minor;
- int r, fd;
-
- /*
- * We get ResumeDevice signals whenever logind resumed a previously
- * paused device. The arguments contain the major/minor number of the
- * related device and a new file-descriptor for the freshly opened
- * device-node.
- * If the signal is not about our device, we simply ignore it.
- * Otherwise, we take the file-descriptor and immediately resume the
- * device.
- */
-
- r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd);
- if (r < 0) {
- log_debug("idev-evdev: %s/%s: erroneous ResumeDevice signal",
- s->name, e->name);
- return 0;
- }
-
- /* not our device? */
- if (makedev(major, minor) != em->devnum)
- return 0;
-
- fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (fd < 0) {
- log_debug("idev-evdev: %s/%s: cannot duplicate evdev fd: %m",
- s->name, e->name);
- return 0;
- }
-
- r = idev_evdev_resume(&em->evdev, fd);
- if (r < 0)
- log_debug("idev-evdev: %s/%s: cannot resume: %s",
- s->name, e->name, strerror(-r));
-
- return 0;
-}
-
-static int managed_evdev_setup_bus(managed_evdev *em) {
- idev_element *e = &em->evdev.element;
- idev_session *s = e->session;
- idev_context *c = s->context;
- _cleanup_free_ char *match = NULL;
- int r;
-
- match = strjoin("type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Session',"
- "member='PauseDevice',"
- "path='", s->path, "'",
- NULL);
- if (!match)
- return -ENOMEM;
-
- r = sd_bus_add_match(c->sysbus,
- &em->slot_pause_device,
- match,
- managed_evdev_pause_device_fn,
- em);
- if (r < 0)
- return r;
-
- free(match);
- match = strjoin("type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Session',"
- "member='ResumeDevice',"
- "path='", s->path, "'",
- NULL);
- if (!match)
- return -ENOMEM;
-
- r = sd_bus_add_match(c->sysbus,
- &em->slot_resume_device,
- match,
- managed_evdev_resume_device_fn,
- em);
- if (r < 0)
- return r;
-
- return 0;
}
static int managed_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
em->evdev = IDEV_EVDEV_INIT(&managed_evdev_vtable, s);
em->devnum = devnum;
- r = managed_evdev_setup_bus(em);
- if (r < 0)
- return r;
-
r = idev_element_add(e, name);
if (r < 0)
return r;
static void managed_evdev_free(idev_element *e) {
managed_evdev *em = managed_evdev_from_element(e);
- em->slot_resume_device = sd_bus_slot_unref(em->slot_resume_device);
- em->slot_pause_device = sd_bus_slot_unref(em->slot_pause_device);
idev_evdev_destroy(&em->evdev);
free(em);
}
static const idev_element_vtable managed_evdev_vtable = {
.free = managed_evdev_free,
- .enable = managed_evdev_resume,
- .disable = managed_evdev_pause,
- .open = managed_evdev_resume,
- .close = managed_evdev_pause,
+ .enable = managed_evdev_enable,
+ .disable = managed_evdev_disable,
+ .open = managed_evdev_enable,
+ .close = managed_evdev_disable,
+ .resume = managed_evdev_resume,
+ .pause = managed_evdev_pause,
};
/*